/* Copyright 2006-2012 Xavier Guerrin This file is part of QElectroTech. QElectroTech is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. QElectroTech is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with QElectroTech. If not, see . */ #include "templatecommands.h" #include "templatevisualcell.h" #include "templateview.h" #include "titleblockcell.h" #include "dimension.h" #define TITLEBLOCK_DEFAULT_ROW_HEIGHT TitleBlockDimension(25) #define TITLEBLOCK_DEFAULT_COL_WIDTH TitleBlockDimension(50) /** Constructor @param cell Modified cell @param parent Parent QUndoCommand */ ModifyTitleBlockCellCommand::ModifyTitleBlockCellCommand(TitleBlockCell *cell, QUndoCommand *parent) : QUndoCommand(parent), view_(0), modified_cell_(cell) { } /** Destructor */ ModifyTitleBlockCellCommand::~ModifyTitleBlockCellCommand() { } /** @see QUndoCommand::id() @return the ID of this command. */ int ModifyTitleBlockCellCommand::id() const { return(MODIFY_TITLE_BLOCK_CELL_COMMAND_ID); } /** @see QUndoCommand::mergeWith() @param command Command to merge with. @return true on success, false otherwise */ bool ModifyTitleBlockCellCommand::mergeWith(const QUndoCommand *command) { const ModifyTitleBlockCellCommand *other = static_cast(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); } } } return(false); } /** Undo the change. */ void ModifyTitleBlockCellCommand::undo() { if (!modified_cell_) return; foreach (QString attribute, old_values_.keys()) { modified_cell_ -> setAttribute(attribute, old_values_[attribute]); } if (view_) view_ -> refresh(); } /** Redo the change. */ void ModifyTitleBlockCellCommand::redo() { if (!modified_cell_) return; foreach (QString attribute, new_values_.keys()) { modified_cell_ -> setAttribute(attribute, new_values_[attribute]); } if (view_) view_ -> refresh(); } /** @return the cell modified by this command */ TitleBlockCell *ModifyTitleBlockCellCommand::cell() const { return(modified_cell_); } /** Set the cell modified by this command object @param modified_cell the cell modified by this command */ void ModifyTitleBlockCellCommand::setCell(TitleBlockCell *modified_cell) { modified_cell_ = modified_cell; } /** @return the view to be updated after the cell modification */ TitleBlockTemplateView *ModifyTitleBlockCellCommand::view() const { return(view_); } /** Set the view to be updated after the cell modification @param view the view to be updated after the cell modification */ void ModifyTitleBlockCellCommand::setView(TitleBlockTemplateView *view) { view_ = view; } /** Erase the known old/new values. */ void ModifyTitleBlockCellCommand::clear() { old_values_.clear(); new_values_.clear(); } /** Register a new modification on a title block template cell; you may indicate either the new value or the old one: this method will systematically fetch the other one. @param attribute Name of the modified attribute @param value Old or new value of the modified attribute, depending on is_old_value @param is_old_value (optional, defaults to false) Indicates whether the provided value is the old or the new one. */ void ModifyTitleBlockCellCommand::addModification(const QString &attribute, const QVariant &value, bool is_old_value) { if (is_old_value) { // the provided value is the old one; therefore, the one we fetch is the new one old_values_[attribute] = value; if (modified_cell_) { new_values_[attribute] = modified_cell_ -> attribute(attribute); } } else { // the provided value is the new one; therefore, we fetch the old one if (modified_cell_) { old_values_[attribute] = modified_cell_ -> attribute(attribute); } new_values_[attribute] = value; } } /** Constructor @param tbtemplate Modified title block template @param parent Parent QUndoCommand */ TitleBlockTemplateCommand::TitleBlockTemplateCommand(TitleBlockTemplate *tbtemplate, QUndoCommand *parent) : QUndoCommand(parent), tbtemplate_(tbtemplate), view_(0) { } /** Destructor */ TitleBlockTemplateCommand::~TitleBlockTemplateCommand() { } /** @return the modified title block template. */ TitleBlockTemplate *TitleBlockTemplateCommand::titleBlockTemplate() const { return(tbtemplate_); } /** Set the modified title block template. @param tbtemplate New modified title block template. */ void TitleBlockTemplateCommand::setTitleBlockTemplate(TitleBlockTemplate *tbtemplate) { tbtemplate_ = tbtemplate; } /** @return the view to be updated after the template modification */ TitleBlockTemplateView *TitleBlockTemplateCommand::view() const { return(view_); } /** Set the view to be updated after the template modification @param view the view to be updated after the template modification */ void TitleBlockTemplateCommand::setView(TitleBlockTemplateView *view) { view_ = view; } /** This static method is a convenience to create a ModifyTemplateGridCommand that adds a row to \a tbtemplate at \a index. @param tbtemplate Modified title block template @param index Index where the row should be inserted. @return a ModifyTemplateGridCommand object, or 0 if something went wrong. */ ModifyTemplateGridCommand *ModifyTemplateGridCommand::addRow(TitleBlockTemplate *tbtemplate, int index) { if (!tbtemplate) return(0); // create the command itself ModifyTemplateGridCommand *add_row_command = new ModifyTemplateGridCommand(tbtemplate); add_row_command -> setInsertion(true); add_row_command -> setType(true); add_row_command -> setCells(tbtemplate -> createRow()); add_row_command -> setDimension(TITLEBLOCK_DEFAULT_ROW_HEIGHT); add_row_command -> setIndex(index); return(add_row_command); } /** This static method is a convenience to create a ModifyTemplateGridCommand that adds a column to \a tbtemplate at \a index. @param tbtemplate Modified title block template. @param index Index where the column should be inserted. @return a ModifyTemplateGridCommand object, or 0 if something went wrong. */ ModifyTemplateGridCommand *ModifyTemplateGridCommand::addColumn(TitleBlockTemplate *tbtemplate, int index) { if (!tbtemplate) return(0); // create the command itself ModifyTemplateGridCommand *add_column_command = new ModifyTemplateGridCommand(tbtemplate); add_column_command -> setInsertion(true); add_column_command -> setType(false); add_column_command -> setCells(tbtemplate -> createColumn()); add_column_command -> setDimension(TITLEBLOCK_DEFAULT_COL_WIDTH); add_column_command -> setIndex(index); return(add_column_command); } /** This static method is a convenience to create a ModifyTemplateGridCommand that removes the row at \a index from \a tbtemplate. @param tbtemplate Modified title block template. @param index Index of the removed row. @return a ModifyTemplateGridCommand object, or 0 if something went wrong. */ ModifyTemplateGridCommand *ModifyTemplateGridCommand::deleteRow(TitleBlockTemplate *tbtemplate, int index) { if (!tbtemplate) return(0); // create the command itself ModifyTemplateGridCommand *del_row_command = new ModifyTemplateGridCommand(tbtemplate); del_row_command -> setInsertion(false); del_row_command -> setType(true); del_row_command -> setIndex(index); return(del_row_command); } /** This static method is a convenience to create a ModifyTemplateGridCommand that removes the column at \a index from \a tbtemplate. @param tbtemplate Modified title block template. @param index Index of the removed column. @return a ModifyTemplateGridCommand object, or 0 if something went wrong. */ ModifyTemplateGridCommand *ModifyTemplateGridCommand::deleteColumn(TitleBlockTemplate *tbtemplate, int index) { if (!tbtemplate) return(0); // create the command itself ModifyTemplateGridCommand *del_column_command = new ModifyTemplateGridCommand(tbtemplate); del_column_command -> setInsertion(false); del_column_command -> setType(false); del_column_command -> setIndex(index); return(del_column_command); } /** Construct a default ModifyTemplateGridCommand, i.e. a command adding a 25px row at the bottom of the template. @param tbtemplate Modified title block template @param parent Parent QUndoCommand */ ModifyTemplateGridCommand::ModifyTemplateGridCommand(TitleBlockTemplate *tbtemplate, QUndoCommand *parent) : TitleBlockTemplateCommand(tbtemplate, parent), index_(-1), type_(true), dimension_(TITLEBLOCK_DEFAULT_ROW_HEIGHT), insertion_(true) { updateText(); } /** Destructor */ ModifyTemplateGridCommand::~ModifyTemplateGridCommand() { } /** @return the index of the inserted/deleted row/column */ int ModifyTemplateGridCommand::index() const { return(index_); } /** Set the index of the inserted/deleted row/column. @param index Index of the inserted/deleted row/column. */ void ModifyTemplateGridCommand::setIndex(int index) { index_ = index; } /** @return a list of pointers to cells composing the inserted/deleted row/column. */ QList ModifyTemplateGridCommand::cells() const { return(cells_); } /** Set the cells composing the inserted/deleted row/column. @param cells List of pointers to cells composing the inserted/deleted row/column. */ void ModifyTemplateGridCommand::setCells(const QList &cells) { cells_ = cells; } /** @return the dimension of the inserted/deleted row/column. */ TitleBlockDimension ModifyTemplateGridCommand::dimension() const { return dimension_; } /** Set the dimension of the inserted/deleted row/column @param dimension Dimension of the inserted/deleted row/column */ void ModifyTemplateGridCommand::setDimension(const TitleBlockDimension &dimension) { dimension_ = dimension; } /** @return true if this object is about inserting/deleting a row, false for a column. */ int ModifyTemplateGridCommand::type() const { return(type_); } /** Indicates whether this object inserts/deletes a row or a column. @param type true if this object is about inserting/deleting a row, false for a column. */ void ModifyTemplateGridCommand::setType(bool type) { type_ = type; updateText(); } /** @return true if the row/column is inserted, false if it is deleted */ bool ModifyTemplateGridCommand::isInsertion() const { return(insertion_); } /** @param insertion true if the row/column is inserted, false if it is deleted */ void ModifyTemplateGridCommand::setInsertion(bool insertion) { insertion_ = insertion; updateText(); } /** Undo the change. */ void ModifyTemplateGridCommand::undo() { apply(true); } /** Redo the change. */ void ModifyTemplateGridCommand::redo() { apply(false); } /** Update the text describing what the command does. */ void ModifyTemplateGridCommand::updateText() { if (type_) { if (insertion_) { setText(QObject::tr("Insertion d'une ligne", "label used in the title block template editor undo list")); } else { setText(QObject::tr("Suppression d'une ligne", "label used in the title block template editor undo list")); } } else { if (insertion_) { setText(QObject::tr("Insertion d'une colonne", "label used in the title block template editor undo list")); } else { setText(QObject::tr("Suppression d'une colonne", "label used in the title block template editor undo list")); } } } /* This method takes care of the actual job when undoing / redoing a row/column insertion/removal. @param true to undo the change, false to apply it. */ void ModifyTemplateGridCommand::apply(bool undo) { if (!tbtemplate_ || index_ == -1) return; if (insertion_ ^ undo) { if (type_) { tbtemplate_ -> insertRow(dimension_.value, cells_, index_); } else { tbtemplate_ -> insertColumn(dimension_, cells_, index_); } } else { if (type_) { dimension_.value = tbtemplate_ -> rowDimension(index_); cells_ = tbtemplate_ -> takeRow(index_); } else { dimension_ = tbtemplate_ -> columnDimension(index_); cells_ = tbtemplate_ -> takeColumn(index_); } } // update the view, if any if (view_) { view_ -> updateLayout(); } } /** Construct a default ModifyTemplateDimension. @param tbtemplate Modified title block template @param parent Parent QUndoCommand */ ModifyTemplateDimension::ModifyTemplateDimension(TitleBlockTemplate *tbtemplate, QUndoCommand *parent) : TitleBlockTemplateCommand(tbtemplate, parent), index_(-1), type_(true), before_(TitleBlockDimension(-1)), after_(TitleBlockDimension(-1)) { } /** Destructor */ ModifyTemplateDimension::~ModifyTemplateDimension() { } /** @return the index of the resized row/column */ int ModifyTemplateDimension::index() const { return(index_); } /** Set the index of the resized row/column. @param index Index of the resized row/column. */ void ModifyTemplateDimension::setIndex(int index) { index_ = index; } /** @return true if this object is about resizing a row, false for a column. */ int ModifyTemplateDimension::type() const { return type_; } /** Indicates whether this object resizes a row or a column. @param type true if this object is about resizing a row, false for a column. */ void ModifyTemplateDimension::setType(bool type) { type_ = type; updateText(); } /** @return the dimension of the row/column before it is resized */ TitleBlockDimension ModifyTemplateDimension::dimensionBefore() const { return(before_); } /** @param dimension the dimension of the row/column before it is resized */ void ModifyTemplateDimension::setDimensionBefore(const TitleBlockDimension &dimension) { before_ = dimension; } /** @return the dimension of the row/column after it is resized */ TitleBlockDimension ModifyTemplateDimension::dimensionAfter() const { return(after_); } /** @param dimension the dimension of the row/column after it is resized */ void ModifyTemplateDimension::setDimensionAfter(const TitleBlockDimension &dimension) { after_ = dimension; } /** Restore the previous size of the row/column. */ void ModifyTemplateDimension::undo() { apply(before_); } /** Resize the row/column. */ void ModifyTemplateDimension::redo() { apply(after_); } /** Update the text describing what the command does. */ void ModifyTemplateDimension::updateText() { if (type_) { setText(QObject::tr("Modification d'une ligne", "label used in the title block template editor undo list")); } else { setText(QObject::tr("Modification d'une colonne", "label used in the title block template editor undo list")); } } /** Applies a given size to the row/column @param dimension Size to apply */ void ModifyTemplateDimension::apply(const TitleBlockDimension &dimension) { if (!tbtemplate_) return; if (type_) { tbtemplate_ -> setRowDimension(index_, dimension); } else { tbtemplate_ -> setColumnDimension(index_, dimension); } if (view_) { if (type_) { view_ -> rowsDimensionsChanged(); } else { view_ -> columnsDimensionsChanged(); } } } /** Construct a command object that acts on \a tbtemplate in order to merge \a merged_cells. Note: you should check the resulting object is valid using isValid(). @param merged_cells Cells to be merged together into a single one. @param tbtemplate Modified title block template. @param parent Parent QUndoCommand. */ MergeCellsCommand::MergeCellsCommand(const TitleBlockTemplateCellsSet &merged_cells, TitleBlockTemplate *tbtemplate, QUndoCommand *parent) : TitleBlockTemplateCommand(tbtemplate, parent), spanning_cell_(0), row_span_after_(-1), col_span_after_(-1) { if (!canMerge(merged_cells, tbtemplate)) return; // the spanning cell is the top left cell TitleBlockTemplateVisualCell *top_left_cell = merged_cells.topLeftCell(); if (!top_left_cell) return; spanning_cell_ = top_left_cell -> cell(); if (!spanning_cell_) return; // store the spanner_cell attribute of each cell implied in the merge foreach(TitleBlockCell *cell, merged_cells.cells()) { spanner_cells_before_merge_.insert(cell, cell -> spanner_cell); } // store the former values of the row_span and col_span attributes of the spanning cell row_span_before_ = spanning_cell_ -> row_span; col_span_before_ = spanning_cell_ -> col_span; // calculate their new values after the merge operation TitleBlockCell *bottom_right_cell = getBottomRightCell(merged_cells); if (!bottom_right_cell) return; row_span_after_ = bottom_right_cell -> num_row - spanning_cell_ -> num_row; col_span_after_ = bottom_right_cell -> num_col - spanning_cell_ -> num_col; setText( QString( QObject::tr( "Fusion de %1 cellules", "label used in the title block template editor undo list; %1 is the number of merged cells" ) ).arg(merged_cells.count()) ); } /** Destructor */ MergeCellsCommand::~MergeCellsCommand() { } /** @param merged_cells Cell sto be merged. @param tbtemplate Modified title block template. @return true if the merge is feasible, false otherwise */ bool MergeCellsCommand::canMerge(const TitleBlockTemplateCellsSet &merged_cells, TitleBlockTemplate *tbtemplate) { Q_UNUSED(tbtemplate) // basic checks if (!merged_cells.isRectangle()) return(false); if (merged_cells.count() < 2) return(false); // the spanning cell is the top left cell TitleBlockTemplateVisualCell *top_left_cell = merged_cells.topLeftCell(); if (!top_left_cell || !top_left_cell -> cell()) return(false); if (!getBottomRightCell(merged_cells)) return(false); return(true); } /** @return true if this command object is valid and usable, false otherwise. */ bool MergeCellsCommand::isValid() const { // we consider having a non-zero spanning cell and positive spans makes a MergeCellsCommand valid return(spanning_cell_ && row_span_after_ != -1 && col_span_after_ != -1); } /** Undo the merge operation. */ void MergeCellsCommand::undo() { if (!isValid()) return; // restore the original spanning_cell attribute of all impacted cells foreach (TitleBlockCell *cell, spanner_cells_before_merge_.keys()) { cell -> spanner_cell = spanner_cells_before_merge_[cell]; } // restore the row_span and col_span attributes of the spanning cell spanning_cell_ -> row_span = row_span_before_; spanning_cell_ -> col_span = col_span_before_; if (view_) view_ -> updateLayout(); } /** Apply the merge operation */ void MergeCellsCommand::redo() { if (!isValid()) return; // set the spanning_cell attributes of spanned cells to the spanning cell foreach (TitleBlockCell *cell, spanner_cells_before_merge_.keys()) { if (cell == spanning_cell_) continue; cell -> spanner_cell = spanning_cell_; } // set the new values of the row_span and col_span attributes spanning_cell_ -> row_span = row_span_after_; spanning_cell_ -> col_span = col_span_after_; if (view_) view_ -> updateLayout(); } /** @param cells_set Set of title block template visual cells. @return the bottom right logical cell within a set of visual cells. */ TitleBlockCell *MergeCellsCommand::getBottomRightCell(const TitleBlockTemplateCellsSet &cells_set) { // first, we get the visual cell at the bottom right TitleBlockTemplateVisualCell *bottom_right_cell = cells_set.bottomRightCell(); if (!bottom_right_cell) return(0); // next, we get its logical cells: the painted one and the spanned ones (if any) QSet logical_cells = bottom_right_cell -> cells(); if (logical_cells.isEmpty()) return(0); if (logical_cells.count() == 1) return(logical_cells.toList().first()); // we then look for the bottom right logical cell int max_num_row = -1, max_num_col = -1; TitleBlockCell *candidate = 0; foreach(TitleBlockCell *cell, logical_cells) { if (cell -> num_row > max_num_row) max_num_row = cell -> num_row; if (cell -> num_col > max_num_col) max_num_col = cell -> num_col; if (cell -> num_row == max_num_row && cell -> num_col == max_num_col) { candidate = cell; } } return(candidate); } /** Construct a command object that acts on \a tbtemplate in order to split \a splitted_cells. Note: you should check the resulting object is valid using isValid(). @param splitted_cells Cell to be splitted. @param tbtemplate Modified title block template. @param parent Parent QUndoCommand. */ SplitCellsCommand::SplitCellsCommand(const TitleBlockTemplateCellsSet &splitted_cells, TitleBlockTemplate *tbtemplate, QUndoCommand *parent) : TitleBlockTemplateCommand(tbtemplate, parent), spanning_cell_(0), row_span_before_(-1), col_span_before_(-1) { if (!canSplit(splitted_cells, tbtemplate)) return; // retrieve values necessary for the undo operation spanning_cell_ = splitted_cells.first() -> cell(); spanned_cells_ = tbtemplate_ -> spannedCells(spanning_cell_); row_span_before_ = spanning_cell_ -> row_span; col_span_before_ = spanning_cell_ -> col_span; setText( QString( QObject::tr( "Séparation d'une cellule en %1", "label used in the title block template editor undo list; %1 is the number of cells after the split" ) ).arg(spanned_cells_.count() + 1) ); } /** Destructor */ SplitCellsCommand::~SplitCellsCommand() { } /** @param splitted_cells Cell to be splitted. @param tbtemplate Modified title block template. @return true if the split is feasible, false otherwise */ bool SplitCellsCommand::canSplit(const TitleBlockTemplateCellsSet &splitted_cells, TitleBlockTemplate *tbtemplate) { Q_UNUSED(tbtemplate) // basic check: the command applies to a single visual cell only if (splitted_cells.count() != 1) return(false); // fetch the spanning cell TitleBlockCell *spanning_cell = splitted_cells.first() -> cell(); if (!spanning_cell) return(false); // ensure the cell spans over other cells and therefore can be splitted if (!spanning_cell -> spans()) return(false); return(true); } /** @return true if this command object is valid and usable, false otherwise. */ bool SplitCellsCommand::isValid() const { // we consider having a non-zero spanning cell and at least one spanned cell makes a SplitCellsCommand valid return(spanning_cell_ && spanned_cells_.count()); } /** Undo the split operation */ void SplitCellsCommand::undo() { if (!isValid()) return; // the spanned cells are spanned again foreach(TitleBlockCell *cell, spanned_cells_) { cell -> spanner_cell = spanning_cell_; } // the spanning cell span again spanning_cell_ -> row_span = row_span_before_; spanning_cell_ -> col_span = col_span_before_; if (view_) view_ -> updateLayout(); } /** Apply the split operation */ void SplitCellsCommand::redo() { if (!isValid()) return; // the spanned cells are not spanned anymore foreach(TitleBlockCell *cell, spanned_cells_) { cell -> spanner_cell = 0; } // the spanning cell does not span anymore spanning_cell_ -> row_span = 0; spanning_cell_ -> col_span = 0; if (view_) view_ -> updateLayout(); } /** Constructor @param tbt Changed title block template @param old_info Former information @param new_info New information @param parent Parent QUndoCommand */ ChangeTemplateInformationsCommand::ChangeTemplateInformationsCommand(TitleBlockTemplate *tbt, const QString &old_info, const QString &new_info, QUndoCommand *parent) : QUndoCommand(QObject::tr("modification des informations compl\351mentaires", "undo caption"), parent), tbtemplate_(tbt), old_information_(old_info), new_information_(new_info) { } /** Destructor */ ChangeTemplateInformationsCommand::~ChangeTemplateInformationsCommand() { } /** Undo the information change */ void ChangeTemplateInformationsCommand::undo() { if (!tbtemplate_) return; tbtemplate_ -> setInformation(old_information_); } /** Redo the information change */ void ChangeTemplateInformationsCommand::redo() { tbtemplate_ -> setInformation(new_information_); }