From 8b1e7904c4e926bcbae8916d7a21dbe90153eb62 Mon Sep 17 00:00:00 2001 From: xavier Date: Sun, 25 Mar 2012 16:34:53 +0000 Subject: [PATCH] The title block template editor now supports pasting multiple cells. git-svn-id: svn+ssh://svn.tuxfamily.org/svnroot/qet/qet/branches/0.3@1597 bfdf4180-ca20-0410-9c96-a3a8aa849046 --- sources/titleblock/templatecommands.cpp | 78 +++++++++++++++++++++++-- sources/titleblock/templatecommands.h | 3 + sources/titleblock/templateview.cpp | 58 ++++++++++++++++-- sources/titleblock/templateview.h | 1 + sources/titleblocktemplate.cpp | 44 ++++++++++++++ sources/titleblocktemplate.h | 15 +++-- 6 files changed, 181 insertions(+), 18 deletions(-) diff --git a/sources/titleblock/templatecommands.cpp b/sources/titleblock/templatecommands.cpp index 1f0cd0ece..d7d49b078 100644 --- a/sources/titleblock/templatecommands.cpp +++ b/sources/titleblock/templatecommands.cpp @@ -59,7 +59,6 @@ bool ModifyTitleBlockCellCommand::mergeWith(const QUndoCommand *command) { if (other) { if (other -> modified_cell_ == modified_cell_) { if (other -> new_values_.keys() == new_values_.keys()) { - qDebug() << Q_FUNC_INFO << "merging"; new_values_ = other -> new_values_; return(true); } @@ -208,6 +207,14 @@ void TitleBlockTemplateCommand::refreshView() { view_ -> refresh(); } +/** + Refresh the view, including layout reloading, if any. +*/ +void TitleBlockTemplateCommand::refreshLayout() { + if (!view_) return; + view_ -> updateLayout(); +} + /** This static method is a convenience to create a ModifyTemplateGridCommand that adds a row to \a tbtemplate at \a index. @@ -933,21 +940,80 @@ void PasteTemplateCellsCommand::updateText() { Undo a paste action. */ void PasteTemplateCellsCommand::undo() { + bool span_management = erased_cells_.count() > 1; foreach (TitleBlockCell *cell, erased_cells_.keys()) { cell -> loadContentFromCell(erased_cells_.value(cell)); } - refreshView(); + if (span_management) { + // restore all span parameters as they were before the paste operation. + tbtemplate_ -> setAllSpans(spans_before_); + tbtemplate_ -> applyCellSpans(); + refreshLayout(); + } else { + refreshView(); + } } /** Redo a paste action. */ void PasteTemplateCellsCommand::redo() { - // paste the first cell ony - foreach (TitleBlockCell *cell, erased_cells_.keys()) { - cell -> loadContentFromCell(pasted_cells_.value(cell)); + // we only play with spans when pasting more than one cell. + bool span_management = erased_cells_.count() > 1; + + if (span_management) { + // When pasting several cells, we may modify the span parameters of existing, + // non-erased cells. The easiest way to ensure everything can be restored at its + // initial state consists in saving the span parameters of every cell. + if (spans_before_.isEmpty()) { + spans_before_ = tbtemplate_ -> getAllSpans(); + } + } + + // copy data from each pasted cell into each erased cell + foreach (TitleBlockCell *cell, erased_cells_.keys()) { + if (span_management) { + // the erased cell may be spanned by another cell + if (TitleBlockCell *spanning_cell = cell -> spanner_cell) { + // for the moment, we simply cancel the whole spanning + tbtemplate_ -> forgetSpanning(spanning_cell); + } + } + + // copy non-spans data + TitleBlockCell pasted_cell = pasted_cells_.value(cell); + cell -> loadContentFromCell(pasted_cell); + + if (span_management) { + // copy spans data + if ((pasted_cell.row_span != cell -> row_span) || (pasted_cell.col_span != cell -> col_span)) { + tbtemplate_ -> forgetSpanning(cell); + + // set the new/pasted span parameters + cell -> row_span = qBound(0, pasted_cell.row_span, tbtemplate_ -> rowsCount() - 1 - cell -> num_row); + cell -> col_span = qBound(0, pasted_cell.col_span, tbtemplate_ -> columnsCount() - 1 - cell -> num_col); + + if (cell -> row_span || cell -> col_span) { + // browse newly spanned cells... + foreach (TitleBlockCell *spanned_cell, tbtemplate_ -> spannedCells(cell)) { + // ... to ensure they are not already spanned by other cells + if (spanned_cell -> spanner_cell && spanned_cell -> spanner_cell != cell) { + // if so, simply cancel the whole spanning + tbtemplate_ -> forgetSpanning(spanned_cell -> spanner_cell); + } + } + + // set the spanner_cell attribute of newly spanned cells + tbtemplate_ -> applyCellSpan(cell); + } + } + } + } + if (span_management) { + refreshLayout(); + } else { + refreshView(); } - refreshView(); } /** diff --git a/sources/titleblock/templatecommands.h b/sources/titleblock/templatecommands.h index 25585b2d2..f9c1e2371 100644 --- a/sources/titleblock/templatecommands.h +++ b/sources/titleblock/templatecommands.h @@ -78,6 +78,7 @@ class TitleBlockTemplateCommand : public QUndoCommand { TitleBlockTemplateView *view() const; void setView(TitleBlockTemplateView *); void refreshView(); + void refreshLayout(); // attributes protected: @@ -300,6 +301,8 @@ class PasteTemplateCellsCommand : public TitleBlockTemplateCommand { // attributes public: + /// Spans before operation + QHash > spans_before_; /// Pasted cells QHash pasted_cells_; /// Existing cells impacted by the paste operation diff --git a/sources/titleblock/templateview.cpp b/sources/titleblock/templateview.cpp index e06998b02..a19589190 100644 --- a/sources/titleblock/templateview.cpp +++ b/sources/titleblock/templateview.cpp @@ -160,6 +160,10 @@ QList TitleBlockTemplateView::copy() { xml_export.appendChild(tbtpartial); foreach (TitleBlockCell *cell, copied_cells) { tbtemplate_ -> exportCellToXml(cell, tbtpartial); + tbtpartial.setAttribute("row", cell -> num_row); + tbtpartial.setAttribute("col", cell -> num_col); + tbtpartial.setAttribute("row_span", cell -> row_span); + tbtpartial.setAttribute("col_span", cell -> col_span); } QClipboard *clipboard = QApplication::clipboard(); @@ -202,6 +206,24 @@ QList TitleBlockTemplateView::pastedCells() { if (e.tagName() == "empty" || e.tagName() == "field" || e.tagName() == "logo") { TitleBlockCell cell; cell.loadContentFromXml(e); + int row_num = -1, col_num = -1, row_span = -1, col_span = -1; + if (!QET::attributeIsAnInteger(e, "row", &row_num) || row_num < 0) { + continue; + } + if (!QET::attributeIsAnInteger(e, "col", &col_num) || col_num < 0) { + continue; + } + cell.num_row = row_num; + cell.num_col = col_num; + + // parse the rowspan and colspan attributes + if (QET::attributeIsAnInteger(e, "rowspan", &row_span) && row_span > 0) { + cell.row_span = row_span; + } + + if (QET::attributeIsAnInteger(e, "colspan", &col_span) && col_span > 0) { + cell.col_span = col_span; + } pasted_cells << cell; } } @@ -212,22 +234,27 @@ QList TitleBlockTemplateView::pastedCells() { Import the cells described in the clipboard. */ void TitleBlockTemplateView::paste() { + if (!tbtemplate_) return; QList pasted_cells = pastedCells(); - - // paste the first cell only if (!pasted_cells.count()) return; - // onto the first selected one + // the top left cell among the selected ones will be used to position the pasted cells TitleBlockTemplateVisualCell *selected_cell = selectedCellsSet().topLeftCell(); if (!selected_cell) return; - TitleBlockCell *erased_cell = selected_cell -> cell(); if (!erased_cell) return; + // change num_row and num_col attributes of pasted cells so they get positionned relatively to selected_cell + normalizeCells(pasted_cells, erased_cell -> num_row, erased_cell -> num_col); + PasteTemplateCellsCommand *paste_command = new PasteTemplateCellsCommand(tbtemplate_); - paste_command -> addCell(erased_cell, *erased_cell, pasted_cells.first()); + foreach (TitleBlockCell cell, pasted_cells) { + TitleBlockCell *erased_cell = tbtemplate_ -> cell(cell.num_row, cell.num_col); + if (!erased_cell) continue; + paste_command -> addCell(erased_cell, *erased_cell, cell); + } + requestGridModification(paste_command); - /// TODO paste more cells, using some kind of heuristic to place them? } /** @@ -768,6 +795,25 @@ bool TitleBlockTemplateView::event(QEvent *event) { return(QGraphicsView::event(event)); } +/** + Given a cells list, change their position so the top left one is at row \a x and column \a y. + @param cells Cells list +*/ +void TitleBlockTemplateView::normalizeCells(QList &cells, int x, int y) const { + if (!cells.count()) return; + + int min_row = cells.at(0).num_row; + int min_col = cells.at(0).num_col; + for (int i = 1 ; i < cells.count() ; ++ i) { + if (cells.at(i).num_row < min_row) min_row = cells.at(i).num_row; + if (cells.at(i).num_col < min_col) min_col = cells.at(i).num_col; + } + for (int i = 0 ; i < cells.count() ; ++ i) { + cells[i].num_row = cells[i].num_row - min_row + x; + cells[i].num_col = cells[i].num_col - min_col + y; + } +} + /** Load the \a tbt title block template. If a different template was previously loaded, it is deleted. diff --git a/sources/titleblock/templateview.h b/sources/titleblock/templateview.h index 130402cbb..f6c638ac5 100644 --- a/sources/titleblock/templateview.h +++ b/sources/titleblock/templateview.h @@ -97,6 +97,7 @@ class TitleBlockTemplateView : public QGraphicsView { virtual qreal zoomFactor() const; virtual void fillWithEmptyCells(); virtual bool event(QEvent *); + virtual void normalizeCells(QList &, int x = 0, int y = 0) const; signals: void selectedCellsChanged(QList); diff --git a/sources/titleblocktemplate.cpp b/sources/titleblocktemplate.cpp index 3dfbcafb1..b4e3fef49 100644 --- a/sources/titleblocktemplate.cpp +++ b/sources/titleblocktemplate.cpp @@ -1021,6 +1021,35 @@ QSet TitleBlockTemplate::spannedCells(const TitleBlockCell *gi return(set); } +/** + Export the span parameters of all cell in the current grid. +*/ +QHash > TitleBlockTemplate::getAllSpans() const { + QHash > spans; + for (int j = 0 ; j < rows_heights_.count() ; ++ j) { + for (int i = 0 ; i < columns_width_.count() ; ++ i) { + spans.insert( + cells_[i][j], + QPair( + cells_[i][j] -> row_span, + cells_[i][j] -> col_span + ) + ); + } + } + return(spans); +} + +/** + Restore a set of span parameters. +*/ +void TitleBlockTemplate::setAllSpans(const QHash > &spans) { + foreach (TitleBlockCell *cell, spans.keys()) { + cell -> row_span = spans[cell].first; + cell -> col_span = spans[cell].second; + } +} + /** @param logo_name Logo name to be added / replaced @param logo_data Logo data @@ -1333,6 +1362,21 @@ void TitleBlockTemplate::forgetSpanning() { } } +/** + Set the spanner_cell attribute of every cell spanned by \a spanning_cell to 0. + @param modify_cell (Optional, defaults to true) Whether to set row_span and col_span of \a spanning_cell to 0. +*/ +void TitleBlockTemplate::forgetSpanning(TitleBlockCell *spanning_cell, bool modify_cell) { + if (!spanning_cell) return; + foreach (TitleBlockCell *spanned_cell, spannedCells(spanning_cell)) { + spanned_cell -> spanner_cell = 0; + } + if (modify_cell) { + spanning_cell -> row_span = 0; + spanning_cell -> col_span = 0; + } +} + /** Forget any previously applied span, then apply again all spans defined by existing cells. diff --git a/sources/titleblocktemplate.h b/sources/titleblocktemplate.h index b477c4751..548eb8c54 100644 --- a/sources/titleblocktemplate.h +++ b/sources/titleblocktemplate.h @@ -83,6 +83,8 @@ class TitleBlockTemplate : public QObject { TitleBlockCell *cell(int, int) const; QSet spannedCells(const TitleBlockCell *) const; + QHash > getAllSpans() const; + void setAllSpans(const QHash > &); bool addLogo(const QString &, QByteArray *, const QString & = "svg", const QString & = "xml"); bool addLogoFromFile(const QString &, const QString & = QString()); bool removeLogo(const QString &); @@ -96,6 +98,13 @@ class TitleBlockTemplate : public QObject { void render(QPainter &, const DiagramContext &, int) const; void renderCell(QPainter &, const TitleBlockCell &, const DiagramContext &, const QRect &) const; QString toString() const; + void applyCellSpans(); + void forgetSpanning(); + void forgetSpanning(TitleBlockCell *, bool = true); + bool checkCellSpan(TitleBlockCell *); + void applyCellSpan(TitleBlockCell *); + void applyRowColNums(); + void rowColsChanged(); protected: void loadInformation(const QDomElement &); @@ -121,12 +130,6 @@ class TitleBlockTemplate : public QObject { int lengthRange(int, int, const QList &) const; QString finalTextForCell(const TitleBlockCell &, const DiagramContext &) const; void renderTextCell(QPainter &, const QString &, const TitleBlockCell &, const QRectF &) const; - void applyCellSpans(); - void forgetSpanning(); - bool checkCellSpan(TitleBlockCell *); - void applyCellSpan(TitleBlockCell *); - void applyRowColNums(); - void rowColsChanged(); // attributes private: