diff --git a/Commands.cpp b/Commands.cpp new file mode 100644 index 0000000..6958b67 --- /dev/null +++ b/Commands.cpp @@ -0,0 +1,98 @@ + +#include "Commands.h" + +AddShapesCommand::AddShapesCommand(MainWindow *mainWindow, uid mappingId, QUndoCommand *parent): + QUndoCommand(parent) +{ + m_mainWindow = mainWindow; + m_mappingId = mappingId; +} + +void AddShapesCommand::undo() +{ + m_mappingPtr = m_mainWindow->getMappingManager().getMappingById(m_mappingId); + m_mainWindow->deleteMapping(m_mappingId); +} + +void AddShapesCommand::redo() +{ + if(m_mappingPtr != NULL) + { + uint currentId = m_mainWindow->getMappingManager().addMapping(m_mappingPtr); + m_mainWindow->addMappingItem(currentId); + } +} + + +MoveVertexCommand::MoveVertexCommand(MapperGLCanvas *mapperGLCanvas, int activeVertex, const QPointF &point, QUndoCommand *parent) : + QUndoCommand(parent) +{ + m_mapperGLCanvas = mapperGLCanvas; + m_shape = m_mapperGLCanvas->getCurrentShape(); + m_activeVertex = activeVertex; + vertexPosition = point; +} + +void MoveVertexCommand::undo() +{ + m_shape->setVertex(m_activeVertex, vertexPosition); + m_mapperGLCanvas->update(); + emit m_mapperGLCanvas->shapeChanged(m_mapperGLCanvas->getCurrentShape()); +} + +void MoveVertexCommand::redo() +{ + m_shape->setVertex(m_activeVertex, vertexPosition); + m_mapperGLCanvas->update(); + emit m_mapperGLCanvas->shapeChanged(m_mapperGLCanvas->getCurrentShape()); +} + + +MoveShapesCommand::MoveShapesCommand(MapperGLCanvas *mapperGLCanvas, QMouseEvent *event, const QPointF &point, QUndoCommand *parent) : + QUndoCommand(parent) +{ + m_mapperGLCanvas = mapperGLCanvas; + m_shape = m_mapperGLCanvas->getCurrentShape(); + m_event = event; + newPosition = point; +} + +void MoveShapesCommand::undo() +{ + m_shape->translate(oldPosition.x(), oldPosition.y()); + m_mapperGLCanvas->update(); + emit m_mapperGLCanvas->shapeChanged(m_mapperGLCanvas->getCurrentShape()); +} + +void MoveShapesCommand::redo() +{ + m_shape->translate(m_event->x() - newPosition.x(), m_event->y() - newPosition.y()); + m_mapperGLCanvas->update(); + emit m_mapperGLCanvas->shapeChanged(m_mapperGLCanvas->getCurrentShape()); + + oldPosition.setX(newPosition.x() - m_event->x()); + oldPosition.setY(newPosition.y() - m_event->y()); +} + + +DeleteMappingCommand::DeleteMappingCommand(MainWindow *mainWindow, uid mappingId, QUndoCommand *parent) : + QUndoCommand(parent) +{ + m_mainWindow = mainWindow; + m_mappingId = mappingId; +} + +void DeleteMappingCommand::undo() +{ + if(m_mappingPtr != NULL) + { + uint currentId = m_mainWindow->getMappingManager().addMapping(m_mappingPtr); + m_mainWindow->addMappingItem(currentId); + } +} + +void DeleteMappingCommand::redo() +{ + m_mappingPtr = m_mainWindow->getMappingManager().getMappingById(m_mappingId); + m_mainWindow->deleteMapping(m_mappingId); +} diff --git a/Commands.h b/Commands.h new file mode 100644 index 0000000..7c8d155 --- /dev/null +++ b/Commands.h @@ -0,0 +1,85 @@ +/* + * Commands.h + * + * (c) 2014 Sofian Audry -- info(@)sofianaudry(.)com + * (c) 2014 Alexandre Quessy -- alexandre(@)quessy(.)net + * (c) 2014 Dame Diongue -- baydamd(@)gmail(.)com + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +#ifndef COMMANDS_H_ +#define COMMANDS_H_ + +#include +#include "MainWindow.h" +#include "MapperGLCanvas.h" + +class AddShapesCommand : public QUndoCommand +{ +public: + AddShapesCommand(MainWindow *mainWindow, uid mappingId, QUndoCommand *parent = 0); + void undo(); + void redo(); + +private: + MainWindow *m_mainWindow; + Mapping::ptr m_mappingPtr; + uid m_mappingId; + +}; + +class MoveVertexCommand : public QUndoCommand +{ +public: + MoveVertexCommand(MapperGLCanvas *mapperGLCanvas, int activeVertex, const QPointF &point, QUndoCommand *parent = 0); + void undo(); + void redo(); + +private: + MapperGLCanvas *m_mapperGLCanvas; + Shape *m_shape; + int m_activeVertex; + QPointF vertexPosition; + +}; + +class MoveShapesCommand : public QUndoCommand +{ +public: + MoveShapesCommand(MapperGLCanvas *mapperGLCanvas, QMouseEvent *event, const QPointF &point, QUndoCommand *parent = 0); + void undo(); + void redo(); + +private: + MapperGLCanvas *m_mapperGLCanvas; + Shape *m_shape; + QMouseEvent *m_event; + QPointF newPosition, oldPosition; +}; + +class DeleteMappingCommand : public QUndoCommand +{ +public: + DeleteMappingCommand(MainWindow *mainWindow, uid mappingId, QUndoCommand *parent = 0); + void undo(); + void redo(); + +private: + MainWindow *m_mainWindow; + Mapping::ptr m_mappingPtr; + uid m_mappingId; +}; + +#endif /* COMMANDS_H_ */ diff --git a/MainWindow.cpp b/MainWindow.cpp index 3dbbf69..8ff8e25 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -19,6 +19,7 @@ */ #include "MainWindow.h" +#include "Commands.h" #include "ProjectWriter.h" #include "ProjectReader.h" #include @@ -26,7 +27,6 @@ MainWindow::MainWindow() { - // Create model. if (Media::hasVideoSupport()) std::cout << "Video support: yes" << std::endl; @@ -53,8 +53,6 @@ MainWindow::MainWindow() createContextMenu(); createToolBars(); createStatusBar(); - updateRecentFileActions(); - updateRecentVideoActions(); // Load settings. readSettings(); @@ -288,38 +286,43 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) if (keyEvent->modifiers() == Qt::CTRL) { switch (keyEvent->key()) { +<<<<<<< HEAD case Qt::Key_F: if (outputWindow->windowState() != Qt::WindowFullScreen) outputWindow->setFullScreen(true); +======= + case Qt::Key_F: + outputWindow->setFullScreen(true); +>>>>>>> feature-undo-commands break; - case Qt::Key_N: + case Qt::Key_N: newFile(); break; - case Qt::Key_O: + case Qt::Key_O: open(); break; - case Qt::Key_S: + case Qt::Key_S: save(); break; - case Qt::Key_Q: + case Qt::Key_Q: close(); break; - case Qt::Key_Delete: + case Qt::Key_Delete: deleteItem(); break; - case Qt::Key_M: + case Qt::Key_M: addMesh(); break; - case Qt::Key_T: + case Qt::Key_T: addTriangle(); break; - case Qt::Key_E: + case Qt::Key_E: addEllipse(); break; - case Qt::Key_D: + case Qt::Key_D: outputWindow->setVisible(true); break; - case Qt::Key_P: + case Qt::Key_P: if (_isPlaying) { pause(); @@ -329,11 +332,18 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) play(); } break; - case Qt::Key_R: + case Qt::Key_R: rewind(); break; + case Qt::Key_Z: + undoStack->undo(); + break; } } + else if (keyEvent->matches(QKeySequence::Redo)) + { + undoStack->redo(); + } else if (keyEvent->key() == Qt::Key_Escape) { outputWindow->setFullScreen(false); @@ -368,6 +378,7 @@ void MainWindow::newFile() { clearWindow(); setCurrentFile(""); + undoStack->clear(); } // Restart video playback. XXX Hack @@ -524,6 +535,9 @@ void MainWindow::addMesh() Mapping::ptr mapping(mappingPtr); uint mappingId = mappingManager->addMapping(mapping); addMappingItem(mappingId); + + // Implement undoStack Commands + undoStack->push(new AddShapesCommand(this, mappingId)); } void MainWindow::addTriangle() @@ -557,6 +571,9 @@ void MainWindow::addTriangle() Mapping::ptr mapping(mappingPtr); uint mappingId = mappingManager->addMapping(mapping); addMappingItem(mappingId); + + // Implement undoStack Commands + undoStack->push(new AddShapesCommand(this, mappingId)); } void MainWindow::addEllipse() @@ -590,6 +607,9 @@ void MainWindow::addEllipse() Mapping::ptr mapping(mappingPtr); uint mappingId = mappingManager->addMapping(mapping); addMappingItem(mappingId); + + // Implement undoStack Commands + undoStack->push(new AddShapesCommand(this, mappingId)); } void MainWindow::play() @@ -676,7 +696,7 @@ void MainWindow::deleteItem() if (isMappingTabSelected) //currentSelectedItem->listWidget() == mappingList) { // Delete mapping. - deleteMapping( getItemId(*mappingList->currentItem()) ); + undoStack->push(new DeleteMappingCommand(this, getItemId(*mappingList->currentItem()))); //currentSelectedItem = NULL; } else if (isPaintTabSelected) //currentSelectedItem->listWidget() == paintList) @@ -1146,6 +1166,9 @@ void MainWindow::createLayout() void MainWindow::createActions() { + // UndoStack + undoStack = new QUndoStack(this); + // New. newAction = new QAction(tr("&New"), this); newAction->setIcon(QIcon(":/new")); @@ -1194,11 +1217,15 @@ void MainWindow::createActions() connect(recentVideoActions[i], SIGNAL(triggered()), this, SLOT(openRecentVideo())); } - // Clear action + // Clear recent video list action clearRecentFileActions = new QAction(this); clearRecentFileActions->setVisible(true); connect(clearRecentFileActions, SIGNAL(triggered()), this, SLOT(clearRecentFileList())); + // Empty list of recent video action + emptyRecentVideos = new QAction(tr("No Videos"), this); + emptyRecentVideos->setEnabled(false); + // Import video. importVideoAction = new QAction(tr("&Import media source file..."), this); @@ -1247,6 +1274,18 @@ void MainWindow::createActions() // connect(pasteAction, SIGNAL(triggered()), spreadsheet, SLOT(paste())); // + // Undo action + undoAction = undoStack->createUndoAction(this, tr("&Undo")); + undoAction->setShortcut(QKeySequence::Undo); + undoAction->setIconVisibleInMenu(false); + undoAction->setShortcutContext(Qt::ApplicationShortcut); + + //Redo action + redoAction = undoStack->createRedoAction(this, tr("&Redo")); + redoAction->setShortcut(QKeySequence::Redo); + redoAction->setIconVisibleInMenu(false); + redoAction->setShortcutContext(Qt::ApplicationShortcut); + // About. aboutAction = new QAction(tr("&About"), this); aboutAction->setStatusTip(tr("Show the application's About box")); @@ -1438,6 +1477,7 @@ void MainWindow::createMenus() // Recent import video recentVideoMenu = fileMenu->addMenu(tr("Recents video")); + recentVideoMenu->addAction(emptyRecentVideos); for (int i = 0; i < MaxRecentVideo; ++i) recentVideoMenu->addAction(recentVideoActions[i]); @@ -1448,6 +1488,8 @@ void MainWindow::createMenus() // Edit. editMenu = menuBar->addMenu(tr("&Edit")); + editMenu->addAction(undoAction); + editMenu->addAction(redoAction); // editMenu->addAction(cutAction); // editMenu->addAction(copyAction); // editMenu->addAction(pasteAction); @@ -1497,7 +1539,7 @@ void MainWindow::createContextMenu() // spreadsheet->addAction(cutAction); // spreadsheet->addAction(copyAction); // spreadsheet->addAction(pasteAction); -// spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu); + // spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu); } void MainWindow::createToolBars() @@ -1599,6 +1641,7 @@ void MainWindow::readSettings() } config_osc_receive_port = settings.value("osc_receive_port", 12345).toInt(); + // Update Recent files and video updateRecentFileActions(); updateRecentVideoActions(); } @@ -1769,9 +1812,9 @@ void MainWindow::updateRecentFileActions() void MainWindow::updateRecentVideoActions() { recentVideos = settings.value("recentVideos").toStringList(); - int numRecentVidoes = qMin(recentVideos.size(), int(MaxRecentVideo)); + int numRecentVideos = qMin(recentVideos.size(), int(MaxRecentVideo)); - for (int i = 0; i < numRecentVidoes; ++i) + for (int i = 0; i < numRecentVideos; ++i) { QString text = tr("&%1 %2") .arg(i + 1) @@ -1781,14 +1824,12 @@ void MainWindow::updateRecentVideoActions() recentVideoActions[i]->setVisible(true); } - for (int j = numRecentVidoes; j < MaxRecentVideo; ++j) + for (int j = numRecentVideos; j < MaxRecentVideo; ++j) recentVideoActions[j]->setVisible(false); - if (numRecentVidoes <= 0) + if (numRecentVideos > 0) { - QAction *noVideos = new QAction(tr("No Videos"), this); - noVideos->setEnabled(false); - recentVideoMenu->addAction(noVideos); + emptyRecentVideos->setVisible(false); } } diff --git a/MainWindow.h b/MainWindow.h index a99770a..5b70ebf 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -22,6 +22,9 @@ #define MAIN_WINDOW_H_ #include +#if QT_VERSION >= 0x050000 + #include +#endif #include #include #include @@ -262,6 +265,8 @@ private: QAction *saveAction; QAction *saveAsAction; QAction *exitAction; + QAction *undoAction; + QAction *redoAction; // QAction *cutAction; // QAction *copyAction; // QAction *pasteAction; @@ -269,6 +274,7 @@ private: QAction *preferencesAction; QAction *aboutAction; QAction *clearRecentFileActions; + QAction *emptyRecentVideos; QAction *addMeshAction; QAction *addTriangleAction; @@ -352,6 +358,11 @@ private: PreferencesDialog* _preferences_dialog; + // UndoStack + QUndoStack *undoStack; + + + public: // Accessor/mutators for the view. /////////////////////////////////////////////////////////////////// MappingManager& getMappingManager() { return *mappingManager; } @@ -366,6 +377,9 @@ public: void removeCurrentPaint(); void removeCurrentMapping(); + // Use the same undoStack for whole program + QUndoStack* getUndoStack() const { return undoStack; } + void startFullScreen(); bool setOscPort(QString portNumber); bool setOscPort(int portNumber); diff --git a/MapperGLCanvas.cpp b/MapperGLCanvas.cpp index 72346e9..9723200 100644 --- a/MapperGLCanvas.cpp +++ b/MapperGLCanvas.cpp @@ -21,6 +21,7 @@ #include "MapperGLCanvas.h" #include "MainWindow.h" +#include "Commands.h" MapperGLCanvas::MapperGLCanvas(MainWindow* mainWindow, QWidget* parent, const QGLWidget * shareWidget) : QGLWidget(QGLFormat(QGL::SampleBuffers), parent, shareWidget), @@ -188,6 +189,9 @@ void MapperGLCanvas::mouseReleaseEvent(QMouseEvent* event) void MapperGLCanvas::mouseMoveEvent(QMouseEvent* event) { + // Prepare to store commands + undoStack = getMainWindow()->getUndoStack(); + if (_mousePressedOnVertex) { // std::cout << "Move event " << std::endl; @@ -202,10 +206,9 @@ void MapperGLCanvas::mouseMoveEvent(QMouseEvent* event) // Stick to vertices. if (stickyVertices()) glueVertex(shape, &p); - shape->setVertex(_activeVertex, p); - update(); - emit shapeChanged(getCurrentShape()); + // Enable to Undo and Redo when mouse move the position of vertices + undoStack->push(new MoveVertexCommand(this, _activeVertex, p)); } } else if (_shapeGrabbed) @@ -217,9 +220,7 @@ void MapperGLCanvas::mouseMoveEvent(QMouseEvent* event) { if (!_shapeFirstGrab) { - shape->translate(event->x() - prevMousePosition.x(), event->y() - prevMousePosition.y()); - update(); - emit shapeChanged(getCurrentShape()); + undoStack->push(new MoveShapesCommand(this, event, prevMousePosition)); } else _shapeFirstGrab = false; @@ -232,6 +233,9 @@ void MapperGLCanvas::mouseMoveEvent(QMouseEvent* event) void MapperGLCanvas::keyPressEvent(QKeyEvent* event) { + // Prepare to store commands + undoStack = getMainWindow()->getUndoStack(); + // Checks if the key has been handled by this function or needs to be deferred to superclass. bool handledKey = false; @@ -264,13 +268,18 @@ void MapperGLCanvas::keyPressEvent(QKeyEvent* event) p.rx()--; break; default: - handledKey = false; + if (event->matches(QKeySequence::Undo)) + undoStack->undo(); + + else if (event->matches(QKeySequence::Redo)) + undoStack->redo(); + else + handledKey = false; break; } // TODO: this will always be called even if no arrow key has been pressed (small performance issue). - shape->setVertex(_activeVertex, p); - update(); - emit shapeChanged(getCurrentShape()); + // Enable to Undo and Redo when arrow keys move the position of vertices + undoStack->push(new MoveVertexCommand(this, _activeVertex, p)); } // Defer unhandled keys to parent. diff --git a/MapperGLCanvas.h b/MapperGLCanvas.h index ca8ef24..9576638 100644 --- a/MapperGLCanvas.h +++ b/MapperGLCanvas.h @@ -23,6 +23,7 @@ #include #include #include +#include #include @@ -141,6 +142,9 @@ private: // True iff we want vertices to stick to each other. bool _stickyVertices; + // Pointer to MainWindow UndoStack + QUndoStack *undoStack; + signals: void shapeChanged(Shape*); void imageChanged(); diff --git a/main.cpp b/main.cpp index 490a27c..c68aedc 100644 --- a/main.cpp +++ b/main.cpp @@ -4,7 +4,6 @@ #include #include -#include #include #if USING_QT_5 #include diff --git a/mapmap.pro b/mapmap.pro index b85f270..b5a323c 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -2,10 +2,12 @@ CONFIG += qt debug TEMPLATE = app VERSION = 0.2.1 TARGET = mapmap -QT += gui opengl xml widgets +QT += gui opengl xml +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB HEADERS = \ + Commands.h \ DestinationGLCanvas.h \ MM.h \ MainApplication.h \ @@ -30,6 +32,7 @@ HEADERS = \ Util.h SOURCES = \ + Commands.cpp \ DestinationGLCanvas.cpp \ MM.cpp \ MainApplication.cpp \