From 030b6397f0ab0519ed4457133f3883768b4faf33 Mon Sep 17 00:00:00 2001 From: sbh Date: Tue, 3 Feb 2015 20:50:01 +0300 Subject: [PATCH] History for undo and redo commands --- src/Model/Model_Session.cpp | 18 +++++- src/Model/Model_Session.h | 6 +- src/ModelAPI/ModelAPI_Session.h | 7 ++- src/ModuleBase/ModuleBase_Operation.cpp | 3 +- src/PartSet/PartSet_Module.cpp | 2 +- src/XGUI/CMakeLists.txt | 2 + src/XGUI/XGUI_HistoryMenu.cpp | 77 +++++++++++++++++++++++++ src/XGUI/XGUI_HistoryMenu.h | 39 +++++++++++++ src/XGUI/XGUI_ObjectsBrowser.cpp | 2 +- src/XGUI/XGUI_Workshop.cpp | 75 ++++++++++++++++++++---- src/XGUI/XGUI_Workshop.h | 10 +++- 11 files changed, 222 insertions(+), 19 deletions(-) create mode 100644 src/XGUI/XGUI_HistoryMenu.cpp create mode 100644 src/XGUI/XGUI_HistoryMenu.h diff --git a/src/Model/Model_Session.cpp b/src/Model/Model_Session.cpp index 816b87f75..e9edb8856 100644 --- a/src/Model/Model_Session.cpp +++ b/src/Model/Model_Session.cpp @@ -50,7 +50,7 @@ void Model_Session::closeAll() Model_Application::getApplication()->deleteAllDocuments(); } -void Model_Session::startOperation() +void Model_Session::startOperation(const std::string& theId) { ROOT_DOC->startOperation(); static std::shared_ptr aStartedMsg @@ -118,6 +118,22 @@ void Model_Session::redo() setCheckTransactions(true); } +//! Returns stack of performed operations +std::list Model_Session::undoList() +{ + std::list temp; + temp.push_front("Part"); + temp.push_front("Sketch"); + temp.push_front("Extrusion"); + return temp; +} +//! Returns stack of rolled back operations +std::list Model_Session::redoList() +{ + std::list temp; + return temp; +} + FeaturePtr Model_Session::createFeature(string theFeatureID) { if (this != myImpl) { diff --git a/src/Model/Model_Session.h b/src/Model/Model_Session.h index fa94b8aae..0ae8bc46a 100644 --- a/src/Model/Model_Session.h +++ b/src/Model/Model_Session.h @@ -48,7 +48,7 @@ class Model_Session : public ModelAPI_Session, public Events_Listener MODEL_EXPORT virtual void closeAll(); //! Starts a new operation (opens a tansaction) - MODEL_EXPORT virtual void startOperation(); + MODEL_EXPORT virtual void startOperation(const std::string& theId); //! Finishes the previously started operation (closes the transaction) MODEL_EXPORT virtual void finishOperation(); //! Aborts the operation @@ -66,6 +66,10 @@ class Model_Session : public ModelAPI_Session, public Events_Listener MODEL_EXPORT virtual bool canRedo(); //! Redoes last operation MODEL_EXPORT virtual void redo(); + //! Returns stack of performed operations + MODEL_EXPORT virtual std::list undoList(); + //! Returns stack of rolled back operations + MODEL_EXPORT virtual std::list redoList(); /// Returns the root document of the application (that may contains sub-documents) MODEL_EXPORT virtual std::shared_ptr moduleDocument(); diff --git a/src/ModelAPI/ModelAPI_Session.h b/src/ModelAPI/ModelAPI_Session.h index 619509dd2..933dfc586 100644 --- a/src/ModelAPI/ModelAPI_Session.h +++ b/src/ModelAPI/ModelAPI_Session.h @@ -45,7 +45,8 @@ class MODELAPI_EXPORT ModelAPI_Session virtual void closeAll() = 0; //! Starts a new operation (opens a tansaction) - virtual void startOperation() = 0; + //! \param theId of operation for history (optional) + virtual void startOperation(const std::string& theId) = 0; //! Finishes the previously started operation (closes the transaction) virtual void finishOperation() = 0; //! Aborts the operation @@ -63,6 +64,10 @@ class MODELAPI_EXPORT ModelAPI_Session virtual bool canRedo() = 0; //! Redoes last operation virtual void redo() = 0; + //! Returns stack of performed operations (from last to first) + virtual std::list undoList() = 0; + //! Returns stack of rolled back operations (from last rolled back to first) + virtual std::list redoList() = 0; /// Registers the plugin that creates features. /// It is obligatory for each plugin to call this function on loading to be found by diff --git a/src/ModuleBase/ModuleBase_Operation.cpp b/src/ModuleBase/ModuleBase_Operation.cpp index de9a9e857..bfe58446d 100644 --- a/src/ModuleBase/ModuleBase_Operation.cpp +++ b/src/ModuleBase/ModuleBase_Operation.cpp @@ -146,7 +146,8 @@ std::shared_ptr ModuleBase_Operation::document() const void ModuleBase_Operation::start() { - ModelAPI_Session::get()->startOperation(); + QString anId = getDescription()->operationId(); + ModelAPI_Session::get()->startOperation(anId.toStdString()); if (!myIsEditing) createFeature(); diff --git a/src/PartSet/PartSet_Module.cpp b/src/PartSet/PartSet_Module.cpp index 49323976a..7fe2956f1 100644 --- a/src/PartSet/PartSet_Module.cpp +++ b/src/PartSet/PartSet_Module.cpp @@ -534,7 +534,7 @@ void PartSet_Module::deleteObjects() } SessionPtr aMgr = ModelAPI_Session::get(); - aMgr->startOperation(); + aMgr->startOperation("DeletePartSet"); std::set::const_iterator anIt = aRefFeatures.begin(), aLast = aRefFeatures.end(); for (; anIt != aLast; anIt++) { diff --git a/src/XGUI/CMakeLists.txt b/src/XGUI/CMakeLists.txt index 26879a5e3..c7a27c716 100644 --- a/src/XGUI/CMakeLists.txt +++ b/src/XGUI/CMakeLists.txt @@ -23,6 +23,7 @@ SET(PROJECT_HEADERS XGUI_Tools.h XGUI_ViewerProxy.h XGUI_Workshop.h + XGUI_HistoryMenu.h ) SET(PROJECT_AUTOMOC @@ -46,6 +47,7 @@ SET(PROJECT_SOURCES XGUI_Tools.cpp XGUI_ViewerProxy.cpp XGUI_Workshop.cpp + XGUI_HistoryMenu.cpp ) SET(PROJECT_RESOURCES diff --git a/src/XGUI/XGUI_HistoryMenu.cpp b/src/XGUI/XGUI_HistoryMenu.cpp new file mode 100644 index 000000000..3e36f42a0 --- /dev/null +++ b/src/XGUI/XGUI_HistoryMenu.cpp @@ -0,0 +1,77 @@ +/* + * XGUI_HistoryMenu.cpp + * + * Created on: Feb 2, 2015 + * Author: sbh + */ + +#include + +#include + +#include +#include +#include + +//! Extends given feature with previously created context menu. +//! \param theId - Id of the feature to add \a theMenu +//! \param theMenu - Enables or disables menu feature +XGUI_HistoryMenu::XGUI_HistoryMenu(QToolButton* theParent) + : QMenu(theParent), + myHistoryList(new QListWidget(this)) +{ + theParent->setMenu(this); + theParent->setPopupMode(QToolButton::MenuButtonPopup); + + QWidgetAction* aListAction = new QWidgetAction(this); + aListAction->setDefaultWidget(myHistoryList); + this->addAction(aListAction); + + myHistoryList->setMouseTracking(true); // track mouse hover + myHistoryList->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(myHistoryList, SIGNAL(itemEntered(QListWidgetItem *)), + this, SLOT(setStackSelectedTo(QListWidgetItem *))); + connect(myHistoryList, SIGNAL(itemClicked(QListWidgetItem *)), + this, SLOT(onItemPressed(QListWidgetItem *))); +} + +XGUI_HistoryMenu::~XGUI_HistoryMenu() +{ +} + +void XGUI_HistoryMenu::setHistory(const QList& theActions) +{ + myHistoryList->clear(); + foreach(QAction* anAct, theActions) { + QListWidgetItem* anItem = new QListWidgetItem(anAct->icon(), + anAct->text(), + myHistoryList); + anAct->deleteLater(); + } +} + + +void XGUI_HistoryMenu::setStackSelectedTo(QListWidgetItem * theItem) +{ + if (!theItem) + return; + + QListWidgetItem* eachItem = NULL; + bool isSelect = true; + for(int aRow = 0; aRow < myHistoryList->count(); ++aRow) { + eachItem = myHistoryList->item(aRow); + myHistoryList->setItemSelected(eachItem, isSelect); + // Deselect items below hovered + if (eachItem == theItem) { + isSelect = false; + } + } +} + +void XGUI_HistoryMenu::onItemPressed(QListWidgetItem * theItem) +{ + int selectedSize = myHistoryList->row(theItem) + 1; + emit actionsSelected(selectedSize); + hide(); + myHistoryList->clear(); +} diff --git a/src/XGUI/XGUI_HistoryMenu.h b/src/XGUI/XGUI_HistoryMenu.h new file mode 100644 index 000000000..dfcb256fb --- /dev/null +++ b/src/XGUI/XGUI_HistoryMenu.h @@ -0,0 +1,39 @@ +/* + * XGUI_HistoryMenu.h + * + * Created on: Feb 2, 2015 + * Author: sbh + */ + +#ifndef XGUI_HISTORYMENU_H_ +#define XGUI_HISTORYMENU_H_ + +#include +#include + +class QListWidget; +class QToolButton; +class QListWidgetItem; + +class XGUI_EXPORT XGUI_HistoryMenu : public QMenu +{ + Q_OBJECT + public: + explicit XGUI_HistoryMenu(QToolButton* theParent); + virtual ~XGUI_HistoryMenu(); + + signals: + void actionsSelected(int); + + public slots: + void setHistory(const QList&); + + protected slots: + void setStackSelectedTo(QListWidgetItem *); + void onItemPressed(QListWidgetItem *); + + private: + QListWidget* myHistoryList; +}; + +#endif /* XGUI_OPERATIONSSTACKPOPUP_H_ */ diff --git a/src/XGUI/XGUI_ObjectsBrowser.cpp b/src/XGUI/XGUI_ObjectsBrowser.cpp index 69cc9c616..243e98eaf 100644 --- a/src/XGUI/XGUI_ObjectsBrowser.cpp +++ b/src/XGUI/XGUI_ObjectsBrowser.cpp @@ -80,7 +80,7 @@ void XGUI_DataTree::commitData(QWidget* theEditor) QString aRes = aEditor->text(); ObjectPtr aFeature = mySelectedData.first(); SessionPtr aMgr = ModelAPI_Session::get(); - aMgr->startOperation(); + aMgr->startOperation("RenameFeature"); aFeature->data()->setName(qPrintable(aRes)); aMgr->finishOperation(); } diff --git a/src/XGUI/XGUI_Workshop.cpp b/src/XGUI/XGUI_Workshop.cpp index 2ed8c86f5..0dbb76876 100644 --- a/src/XGUI/XGUI_Workshop.cpp +++ b/src/XGUI/XGUI_Workshop.cpp @@ -16,6 +16,7 @@ #include "XGUI_ContextMenuMgr.h" #include "XGUI_ModuleConnector.h" #include +#include #include #include @@ -68,6 +69,9 @@ #include #include #include +#include +#include +#include #ifdef _DEBUG #include @@ -256,14 +260,30 @@ void XGUI_Workshop::initMenu() aCommand->connectTo(this, SLOT(onSave())); //aCommand->disable(); - aCommand = aGroup->addFeature("UNDO_CMD", tr("Undo"), tr("Undo last command"), + QString aUndoId = "UNDO_CMD"; + aCommand = aGroup->addFeature(aUndoId, tr("Undo"), tr("Undo last command"), QIcon(":pictures/undo.png"), QKeySequence::Undo); aCommand->connectTo(this, SLOT(onUndo())); - aCommand = aGroup->addFeature("REDO_CMD", tr("Redo"), tr("Redo last command"), + QToolButton* aToolBtn = qobject_cast(aGroup->widget(aUndoId)); + XGUI_HistoryMenu* aUndoMenu = new XGUI_HistoryMenu(aToolBtn); + connect(this, SIGNAL(updateUndoHistory(const QList&)), + aUndoMenu, SLOT(setHistory(const QList&))); + connect(aUndoMenu, SIGNAL(actionsSelected(int)), + this, SLOT(onUndo(int))); + + QString aRedoId = "REDO_CMD"; + aCommand = aGroup->addFeature(aRedoId, tr("Redo"), tr("Redo last command"), QIcon(":pictures/redo.png"), QKeySequence::Redo); aCommand->connectTo(this, SLOT(onRedo())); + aToolBtn = qobject_cast(aGroup->widget(aRedoId)); + XGUI_HistoryMenu* aRedoMenu = new XGUI_HistoryMenu(aToolBtn); + connect(this, SIGNAL(updateUndoHistory(const QList&)), + aRedoMenu, SLOT(setHistory(const QList&))); + connect(aRedoMenu, SIGNAL(actionsSelected(int)), + this, SLOT(onUndo(int))); + aCommand = aGroup->addFeature("REBUILD_CMD", tr("Rebuild"), tr("Rebuild data objects"), QIcon(":pictures/rebuild.png"), QKeySequence()); aCommand->connectTo(this, SLOT(onRebuild())); @@ -675,7 +695,6 @@ void XGUI_Workshop::addFeature(const std::shared_ptr& the // Remember features icons myIcons[QString::fromStdString(theMessage->id())] = QString::fromStdString(theMessage->icon()); - //Find or create Workbench QString aWchName = QString::fromStdString(theMessage->workbenchId()); QString aNestedFeatures = QString::fromStdString(theMessage->nestedFeatures()); bool isUsePropPanel = theMessage->isUseInput(); @@ -684,7 +703,7 @@ void XGUI_Workshop::addFeature(const std::shared_ptr& the QAction* aAction = salomeConnector()->addFeature(aWchName, aFeatureId, QString::fromStdString(theMessage->text()), QString::fromStdString(theMessage->tooltip()), - QIcon(theMessage->icon().c_str()), + QIcon(QString::fromStdString(theMessage->icon())), QKeySequence(), isUsePropPanel); salomeConnector()->setNestedActions(aFeatureId, aNestedFeatures.split(" ", QString::SkipEmptyParts)); @@ -693,7 +712,7 @@ void XGUI_Workshop::addFeature(const std::shared_ptr& the myActionsMgr->addCommand(aAction); myModule->actionCreated(aAction); } else { - + //Find or create Workbench AppElements_MainMenu* aMenuBar = myMainWindow->menuObject(); AppElements_Workbench* aPage = aMenuBar->findWorkbench(aWchName); if (!aPage) { @@ -894,24 +913,28 @@ bool XGUI_Workshop::onSaveAs() } //****************************************************** -void XGUI_Workshop::onUndo() +void XGUI_Workshop::onUndo(int theTimes) { objectBrowser()->treeView()->setCurrentIndex(QModelIndex()); SessionPtr aMgr = ModelAPI_Session::get(); if (aMgr->isOperation()) operationMgr()->onAbortOperation(); - aMgr->undo(); + for (int i = 0; i < theTimes; ++i) { + aMgr->undo(); + } updateCommandStatus(); } //****************************************************** -void XGUI_Workshop::onRedo() +void XGUI_Workshop::onRedo(int theTimes) { objectBrowser()->treeView()->setCurrentIndex(QModelIndex()); SessionPtr aMgr = ModelAPI_Session::get(); if (aMgr->isOperation()) operationMgr()->onAbortOperation(); - aMgr->redo(); + for (int i = 0; i < theTimes; ++i) { + aMgr->redo(); + } updateCommandStatus(); } @@ -921,7 +944,7 @@ void XGUI_Workshop::onRebuild() SessionPtr aMgr = ModelAPI_Session::get(); bool aWasOperation = aMgr->isOperation(); // keep this value if (!aWasOperation) { - aMgr->startOperation(); + aMgr->startOperation("Rebuild"); } static const Events_ID aRebuildEvent = Events_Loop::loop()->eventByName("Rebuild"); Events_Loop::loop()->send(std::shared_ptr( @@ -1050,6 +1073,9 @@ void XGUI_Workshop::updateCommandStatus() } aUndoCmd->setEnabled(aMgr->canUndo() && !aMgr->isOperation()); aRedoCmd->setEnabled(aMgr->canRedo() && !aMgr->isOperation()); + + updateHistory(); + } else { foreach(QAction* aCmd, aCommands) { QString aId = aCmd->data().toString(); @@ -1065,6 +1091,33 @@ void XGUI_Workshop::updateCommandStatus() emit commandStatusUpdated(); } +void XGUI_Workshop::updateHistory() +{ + std::list aUndoList = ModelAPI_Session::get()->undoList(); + std::list::iterator it = aUndoList.begin(); + QList aUndoRes; + for ( ; it != aUndoList.end(); it++) { + QString anId = QString::fromStdString(*it); + QIcon aIcon; + if (myIcons.contains(anId)) + aIcon = QIcon(myIcons[anId]); + aUndoRes << new QAction(aIcon, anId, NULL); + } + emit updateUndoHistory(aUndoRes); + + std::list aRedoList = ModelAPI_Session::get()->redoList(); + it = aRedoList.begin(); + QList aRedoRes; + for ( ; it != aRedoList.end(); it++) { + QString anId = QString::fromStdString(*it); + QIcon aIcon; + if (myIcons.contains(anId)) + aIcon = QIcon(myIcons[anId]); + aRedoRes << new QAction(aIcon, anId, NULL); + } + emit updateRedoHistory(aUndoRes); +} + //****************************************************** QDockWidget* XGUI_Workshop::createObjectBrowser(QWidget* theParent) { @@ -1302,7 +1355,7 @@ These features will be deleted also. Would you like to continue?")).arg(aNames), } SessionPtr aMgr = ModelAPI_Session::get(); - aMgr->startOperation(); + aMgr->startOperation("DeleteObjects"); std::set::const_iterator anIt = aRefFeatures.begin(), aLast = aRefFeatures.end(); for (; anIt != aLast; anIt++) { diff --git a/src/XGUI/XGUI_Workshop.h b/src/XGUI/XGUI_Workshop.h index 80aa6d2f8..23990f8bc 100644 --- a/src/XGUI/XGUI_Workshop.h +++ b/src/XGUI/XGUI_Workshop.h @@ -215,10 +215,16 @@ signals: //! the application is started void applicationStarted(); + void updateUndoHistory(const QList&); + void updateRedoHistory(const QList&); + public slots: /// Update of commands status void updateCommandStatus(); + /// update history list (undo/redo commands) + void updateHistory(); + /// Create a new dokument void onNew(); @@ -235,10 +241,10 @@ signals: void onExit(); /// Undo last command - void onUndo(); + void onUndo(int times = 1); /// Redo previous command - void onRedo(); + void onRedo(int times = 1); /// Rebuild data tree void onRebuild(); -- 2.30.2