diff --git a/.gitignore b/.gitignore index de902b5..2fd7c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ Release/ *.mov *.ogg *.png +# except resources files +!resources/images/icons/*.png *.tar *.rar *.qm diff --git a/concurrentqueue.h b/ConcurrentQueue.h similarity index 100% rename from concurrentqueue.h rename to ConcurrentQueue.h diff --git a/ConsoleWindow.cpp b/ConsoleWindow.cpp new file mode 100644 index 0000000..69f2f20 --- /dev/null +++ b/ConsoleWindow.cpp @@ -0,0 +1,143 @@ +/* + * ConsoleWindow.cpp + * + * (c) 2016 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 . + */ + +#include "ConsoleWindow.h" +#include + +ConsoleWindow* ConsoleWindow::_singleton = NULL; + +ConsoleWindow::ConsoleWindow(QWidget *parent) : QMainWindow(parent) +{ + // Set Fixed size + resize(CONSOLE_WINDOW_DEFAULT_WIDTH, CONSOLE_WINDOW_DEFAULT_HEIGHT); + setMinimumSize(CONSOLE_WINDOW_DEFAULT_WIDTH, CONSOLE_WINDOW_DEFAULT_HEIGHT); + // Create console + _console = new QPlainTextEdit(this); + // Make read-only but allow copy of text + _console->setReadOnly(true); + // Create and customize font + int id = QFontDatabase::addApplicationFont(":/console-font"); + QString family = QFontDatabase::applicationFontFamilies(id).at(0); + QFont font(QFont(family, 10, QFont::Normal)); + _console->setFont(font); + + // Set color scheme + QPalette scheme = palette(); + scheme.setColor(QPalette::Base, Qt::black); + scheme.setColor(QPalette::Text, Qt::white); + _console->setPalette(scheme); + + // Create view elements + createActions(); + createMenu(); + + // Set window title + setWindowTitle(tr("Message Log Output - Mapmap")); + // Set main widget + setCentralWidget(_console); +} + +ConsoleWindow *ConsoleWindow::getInstance() +{ + if (!_singleton) + _singleton = new ConsoleWindow; + + return _singleton; +} + +void ConsoleWindow::createActions() +{ + // Quit + quitAction = new QAction(tr("&Close"), this); + quitAction->setShortcut(QKeySequence::Close); + quitAction->setStatusTip(tr("Close the console")); + connect(quitAction, SIGNAL(triggered(bool)), this, SLOT(close())); +} + +void ConsoleWindow::createMenu() +{ + // File menu + fileMenu = menuBar()->addMenu(tr("&File")); + fileMenu->addSeparator(); + fileMenu->addAction(quitAction); +} + +void ConsoleWindow::messageLog(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + // Message + QByteArray message = msg.toLocal8Bit(); + // Context + QString contexts(QStringLiteral("%1:%2").arg(context.file).arg(context.line)), + // Date and time + time(QDateTime::currentDateTime().toString(tr("MMM dd yy HH:mm"))), + // Colorized time + timeHtml = "" + time + "", + debug = "Debug:", + info = "Info:", + warning = "Warning:", + critical = "Critical:", + fatal = "Fatal!"; + // Output + QString output; + + switch (type) { + case QtDebugMsg: + output = time + " | " + debug + " " + QString(message.constData()) + " - " + contexts + ""; + break; +#if QT_VERSION >= 0x050500 + case QtInfoMsg: + output = time + " | " + info + " " + QString(message.constData()) + " - " + contexts + ""; + break; +#endif + case QtWarningMsg: + output = time + " | " + warning + " " + QString(message.constData()) + " - " + contexts + ""; + break; + case QtCriticalMsg: + output = time + " | " + critical + " " + QString(message.constData()) + " - " + contexts + ""; + break; + case QtFatalMsg: + output = time + " | " + fatal + " " + QString(message.constData()) + " - " + contexts + ""; + abort(); + } + // Print in console + _console->appendHtml(output); +} + +void ConsoleWindow::closeEvent(QCloseEvent *event) +{ + // Send signal if the window is closed + emit windowClosed(); + event->accept(); +} + +void ConsoleWindow::kill() +{ + if (_singleton) { + delete _singleton; + _singleton = NULL; + } +} + +ConsoleWindow::~ConsoleWindow() +{ + kill(); +} + + + diff --git a/ConsoleWindow.h b/ConsoleWindow.h new file mode 100644 index 0000000..01caf59 --- /dev/null +++ b/ConsoleWindow.h @@ -0,0 +1,77 @@ +/* + * ConsoleWindow.h + * + * (c) 2016 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 CONSOLE_H +#define CONSOLE_H + +#include +#include +#include +#include + +class QAction; +class QMenu; + +class ConsoleWindow : public QMainWindow +{ + Q_OBJECT + +private: + // Private constructor + ConsoleWindow(QWidget *parent = 0); + +public: + // Using a singleton instance + static ConsoleWindow *getInstance(); + // Console log message handler + void messageLog(QtMsgType type, const QMessageLogContext &context, const QString &msg); + // Destructor + ~ConsoleWindow(); + // This instance killer + static void kill(); + +signals: + void windowClosed(); + +protected: + void closeEvent(QCloseEvent *event); + +private: + // This instance + static ConsoleWindow *_singleton; + + // Console logger + QPlainTextEdit *_console; + + // Create view elements + void createActions(); + void createMenu(); + + // Actions + QAction *quitAction; + + // Menus + QMenu *fileMenu; + + // Constants + static const int CONSOLE_WINDOW_DEFAULT_WIDTH = 640; + static const int CONSOLE_WINDOW_DEFAULT_HEIGHT = 480; +}; + +#endif // CONSOLE_H diff --git a/DestinationGLCanvas.cpp b/DestinationGLCanvas.cpp index 8ea036d..a4defce 100644 --- a/DestinationGLCanvas.cpp +++ b/DestinationGLCanvas.cpp @@ -26,20 +26,12 @@ DestinationGLCanvas::DestinationGLCanvas(MainWindow* mainWindow, QWidget* parent { } -MShape::ptr DestinationGLCanvas::getShapeFromMappingId(uid mappingId) const +MShape::ptr DestinationGLCanvas::getShapeFromMapping(const Mapping::ptr& mapping) const { - if (mappingId == NULL_UID) - return MShape::ptr(); - else - return getMainWindow()->getMappingManager().getMappingById(mappingId)->getShape(); + return (mapping.isNull() ? MShape::ptr() : mapping->getShape()); } -QSharedPointer DestinationGLCanvas::getShapeGraphicsItemFromMappingId(uid mappingId) const +QSharedPointer DestinationGLCanvas::getShapeGraphicsItemFromMapping(const Mapping::ptr& mapping) const { - if (mappingId == NULL_UID) - return QSharedPointer(); - else - { - return MainWindow::instance()->getMappingGuiByMappingId(mappingId)->getGraphicsItem(); - } + return (mapping.isNull() ? QSharedPointer() : MainWindow::instance()->getMappingGuiByMappingId(mapping->getId())->getGraphicsItem()); } diff --git a/DestinationGLCanvas.h b/DestinationGLCanvas.h index 08f686c..38f70af 100644 --- a/DestinationGLCanvas.h +++ b/DestinationGLCanvas.h @@ -35,8 +35,8 @@ public: virtual ~DestinationGLCanvas() {} virtual bool isOutput() const { return true; } - virtual MShape::ptr getShapeFromMappingId(uid mappingId) const; - virtual QSharedPointer getShapeGraphicsItemFromMappingId(uid mappingId) const; + virtual MShape::ptr getShapeFromMapping(const Mapping::ptr& mapping) const; + virtual QSharedPointer getShapeGraphicsItemFromMapping(const Mapping::ptr& mapping) const; }; #endif /* DESTINATIONGLCANVAS_H_ */ diff --git a/Doxyfile b/Doxyfile index 6d760d4..df9e333 100644 --- a/Doxyfile +++ b/Doxyfile @@ -558,7 +558,7 @@ EXCLUDE_SYMLINKS = NO # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = *old/* *test/* +EXCLUDE_PATTERNS = *old/* *test/* moc_* qrc_* *.txt # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/MM.cpp b/MM.cpp index e2d36c2..020c8b7 100644 --- a/MM.cpp +++ b/MM.cpp @@ -21,7 +21,7 @@ const QString MM::APPLICATION_NAME = "MapMap"; const QString MM::VERSION = "0.3.2"; -const QString MM::COPYRIGHT_OWNERS = "Sofian Audry, Alexandre Quessy, Mike Latona, Vasilis Liaskovitis, Dame Diongue"; +const QString MM::COPYRIGHT_OWNERS = "Sofian Audry, Alexandre Quessy, Dame Diongue, Mike Latona, Vasilis Liaskovitis"; const QString MM::ORGANIZATION_NAME = "MapMap"; const QString MM::ORGANIZATION_DOMAIN = "mapmap.info"; const QString MM::FILE_EXTENSION = "mmp"; @@ -33,8 +33,11 @@ const QColor MM::BLUE_GRAY("#323541"); const QColor MM::DARK_GRAY("#272a36"); const QColor MM::CONTROL_COLOR(WHITE); +const QColor MM::CONTROL_LOCKED_COLOR("#FF0000"); const QBrush MM::VERTEX_BACKGROUND(QColor(CONTROL_COLOR.red(), CONTROL_COLOR.green(), CONTROL_COLOR.blue(), 63)); const QBrush MM::VERTEX_SELECTED_BACKGROUND(QColor(CONTROL_COLOR.red(), CONTROL_COLOR.green(), CONTROL_COLOR.blue(), 127)); +//const QBrush MM::VERTEX_LOCKED_BACKGROUND(QColor(CONTROL_LOCKED_COLOR.red(), CONTROL_LOCKED_COLOR.green(), CONTROL_LOCKED_COLOR.blue(), 63)); +const QBrush MM::VERTEX_LOCKED_BACKGROUND(CONTROL_LOCKED_COLOR); const qreal MM::SHAPE_STROKE_WIDTH = 1.5; const qreal MM::SHAPE_INNER_STROKE_WIDTH = 0.5; @@ -43,6 +46,7 @@ const QPen MM::SHAPE_INNER_STROKE(QBrush(CONTROL_COLOR), SHAPE_INNER_STROKE_WIDT const qreal MM::VERTEX_STICK_RADIUS = 10; const qreal MM::VERTEX_SELECT_RADIUS = 10; +const qreal MM::VERTEX_LOCKED_RADIUS = 6; const qreal MM::VERTEX_SELECT_STROKE_WIDTH = 1; // Time. diff --git a/MM.h b/MM.h index 754a86a..0816a9d 100644 --- a/MM.h +++ b/MM.h @@ -47,6 +47,8 @@ public: static const int DEFAULT_WINDOW_HEIGHT = 480; static const int TOP_TOOLBAR_ICON_SIZE = 48; static const int BOTTOM_TOOLBAR_ICON_SIZE = 32; + static const int ZOOM_TOOLBAR_ICON_SIZE = 22; + static const int ZOOM_TOOLBAR_BUTTON_SIZE = 32; // Style. static const QColor WHITE; @@ -54,8 +56,10 @@ public: static const QColor DARK_GRAY; static const QColor CONTROL_COLOR; + static const QColor CONTROL_LOCKED_COLOR; static const QBrush VERTEX_BACKGROUND; static const QBrush VERTEX_SELECTED_BACKGROUND; + static const QBrush VERTEX_LOCKED_BACKGROUND; static const qreal SHAPE_STROKE_WIDTH; static const qreal SHAPE_INNER_STROKE_WIDTH; @@ -65,6 +69,7 @@ public: // Control. static const qreal VERTEX_STICK_RADIUS; static const qreal VERTEX_SELECT_RADIUS; + static const qreal VERTEX_LOCKED_RADIUS; static const qreal VERTEX_SELECT_STROKE_WIDTH; // Time. @@ -80,6 +85,7 @@ public: static const int MESH_SUBDIVISION_MAX_DEPTH_EDITING = 4; static const int MESH_SUBDIVISION_MAX_DEPTH = (-1); static const int ELLIPSE_N_TRIANGLES = 100; // n triangles used to draw an ellipse + static const int VERTEX_MOVES_STEP = 25; }; #endif diff --git a/MainWindow.cpp b/MainWindow.cpp index c573696..a9c8d6e 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -28,10 +28,11 @@ MainWindow::MainWindow() { // Create model. - if (Media::hasVideoSupport()) - std::cout << "Video support: yes" << std::endl; - else - std::cout << "Video support: no" << std::endl; +#if QT_VERSION >= 0x050500 + QMessageLogger(__FILE__, __LINE__, 0).info() << "Video support: " << (Media::hasVideoSupport() ? "yes" : "no"); +#else + QMessageLogger(__FILE__, __LINE__, 0).debug() << "Video support: " << (Media::hasVideoSupport() ? "yes" : "no"); +#endif mappingManager = new MappingManager; @@ -51,6 +52,7 @@ MainWindow::MainWindow() _stickyVertices = true; _displayTestSignal = false; _displayUndoStack = false; + _showMenuBar = true; // Show menubar by default // UndoStack undoStack = new QUndoStack(this); @@ -92,7 +94,7 @@ MainWindow::MainWindow() MainWindow::~MainWindow() { delete mappingManager; -// delete _facade; + // delete _facade; #ifdef HAVE_OSC delete osc_timer; #endif // ifdef @@ -131,23 +133,33 @@ void MainWindow::handlePaintItemSelectionChanged() void MainWindow::handleMappingItemSelectionChanged() { - if (mappingList->selectedItems().empty()) - { - removeCurrentMapping(); - } - else - { - QListWidgetItem* item = mappingList->currentItem(); - currentSelectedItem = item; - - // Set current paint and mappings. - uid mappingId = getItemId(*item); - Mapping::ptr mapping = mappingManager->getMappingById(mappingId); - uid paintId = mapping->getPaint()->getId(); - setCurrentMapping(mappingId); - setCurrentPaint(paintId); - } + if (mappingList->selectedItems().empty()) + { + removeCurrentMapping(); + /* Disable some menus and buttons when + * no mapping was selected */ + sourceCanvas->enableZoomToolBar(false); + sourceMenu->setEnabled(false); + destinationCanvas->enableZoomToolBar(false); + destinationMenu->setEnabled(false); + } + else + { + QListWidgetItem* item = mappingList->currentItem(); + currentSelectedItem = item; + // Set current paint and mappings. + uid mappingId = getItemId(*item); + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); + uid paintId = mapping->getPaint()->getId(); + setCurrentMapping(mappingId); + setCurrentPaint(paintId); + // Enable some menus and buttons + sourceCanvas->enableZoomToolBar(true); + sourceMenu->setEnabled(true); + destinationCanvas->enableZoomToolBar(true); + destinationMenu->setEnabled(true); + } // Update canvases. updateCanvases(); @@ -233,21 +245,21 @@ void MainWindow::handlePaintChanged(Paint::ptr paint) { QSharedPointer media = qSharedPointerCast(paint); Q_CHECK_PTR(media); updatePaintItem(paintId, createFileIcon(media->getUri()), strippedName(media->getUri())); -// QString fileName = QFileDialog::getOpenFileName(this, -// tr("Import media source file"), "."); -// // Restart video playback. XXX Hack -// if (!fileName.isEmpty()) -// importMediaFile(fileName, paint, false); + // QString fileName = QFileDialog::getOpenFileName(this, + // tr("Import media source file"), "."); + // // Restart video playback. XXX Hack + // if (!fileName.isEmpty()) + // importMediaFile(fileName, paint, false); } if (paint->getType() == "image") { QSharedPointer image = qSharedPointerCast(paint); Q_CHECK_PTR(image); updatePaintItem(paintId, createImageIcon(image->getUri()), strippedName(image->getUri())); -// QString fileName = QFileDialog::getOpenFileName(this, -// tr("Import media source file"), "."); -// // Restart video playback. XXX Hack -// if (!fileName.isEmpty()) -// importMediaFile(fileName, paint, true); + // QString fileName = QFileDialog::getOpenFileName(this, + // tr("Import media source file"), "."); + // // Restart video playback. XXX Hack + // if (!fileName.isEmpty()) + // importMediaFile(fileName, paint, true); } else if (paint->getType() == "color") { // Pop-up color-choosing dialog to choose color paint. @@ -268,7 +280,14 @@ void MainWindow::closeEvent(QCloseEvent *event) // Popup dialog allowing the user to save before closing. if (okToContinue()) { + // Save settings writeSettings(); + // Close all top level widgets + foreach (QWidget *widget, QApplication::topLevelWidgets()) { + if (widget != this) { // Avoid recursion + widget->close(); + } + } event->accept(); } else @@ -282,7 +301,7 @@ void MainWindow::closeEvent(QCloseEvent *event) bool MainWindow::eventFilter(QObject *obj, QEvent *event) { - bool eventKey = false; + bool eventKey = false; if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); @@ -290,60 +309,48 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) // Menubar shortcut if (keyEvent->modifiers() == Qt::CTRL) { - switch (keyEvent->key()) { - case Qt::Key_F: - if (outputWindow->windowState() != Qt::WindowFullScreen) - { - outputWindow->setFullScreen(true); - outputWindowFullScreenAction->setChecked(true); - outputWindowFullScreenAction->setEnabled(true); - } - break; - case Qt::Key_N: - newFile(); - break; - case Qt::Key_O: - open(); - break; - case Qt::Key_S: - save(); - break; - case Qt::Key_Q: - close(); - break; - case Qt::Key_Delete: - deleteItem(); - break; - case Qt::Key_M: - addMesh(); - break; - case Qt::Key_T: - addTriangle(); - break; - case Qt::Key_E: - addEllipse(); - break; - case Qt::Key_W: - outputWindow->setVisible(true); - break; - case Qt::Key_R: - rewind(); - break; - case Qt::Key_Z: - undoStack->undo(); - break; - } + switch (keyEvent->key()) { + case Qt::Key_N: + newFile(); + break; + case Qt::Key_O: + open(); + break; + case Qt::Key_S: + save(); + break; + case Qt::Key_Q: + close(); + break; + case Qt::Key_Delete: + deleteItem(); + break; + case Qt::Key_M: + addMesh(); + break; + case Qt::Key_T: + addTriangle(); + break; + case Qt::Key_E: + addEllipse(); + break; + case Qt::Key_W: + outputWindow->setVisible(true); + break; + 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); - outputWindowFullScreenAction->setChecked(false); - outputWindowFullScreenAction->setEnabled(false); - } + outputWindow->close(); else if (keyEvent->key() == Qt::Key_Space) { if (_isPlaying) @@ -366,11 +373,35 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *event) } } +void MainWindow::keyPressEvent(QKeyEvent *event) +{ +#ifdef Q_OS_OSX // On Mac OS X + // Do nothing +#endif + +#ifdef Q_OS_LINUX // On Linux + if (event->modifiers() & Qt::AltModifier) { + QString currentDesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower(); + if (currentDesktop != "unity" && !_showMenuBar) { + menuBar()->setHidden(!menuBar()->isHidden()); + menuBar()->setFocus(Qt::MenuBarFocusReason); + } + } +#endif +#ifdef Q_OS_WIN + if (event->modifiers() & Qt::AltModifier) { + if (!_showMenuBar) { + menuBar()->setHidden(!menuBar()->isHidden()); + menuBar()->setFocus(Qt::MenuBarFocusReason); + } + } +#endif +} + void MainWindow::setOutputWindowFullScreen(bool enable) { - outputWindow->setFullScreen(false); + outputWindow->setFullScreen(enable); // setCheckState - outputWindowFullScreenAction->setChecked(enable); displayControlsAction->setChecked(enable); } @@ -400,9 +431,9 @@ void MainWindow::open() if (okToContinue()) { QString fileName = QFileDialog::getOpenFileName(this, - tr("Open project"), - settings.value("defaultProjectDir").toString(), - tr("MapMap files (*.%1)").arg(MM::FILE_EXTENSION)); + tr("Open project"), + settings.value("defaultProjectDir").toString(), + tr("MapMap files (*.%1)").arg(MM::FILE_EXTENSION)); if (! fileName.isEmpty()) loadFile(fileName); } @@ -436,8 +467,8 @@ bool MainWindow::saveAs() // Popul file dialog to choose filename. QString fileName = QFileDialog::getSaveFileName(this, - tr("Save project"), settings.value("defaultProjectDir").toString(), - tr("MapMap files (*.%1)").arg(MM::FILE_EXTENSION)); + tr("Save project"), settings.value("defaultProjectDir").toString(), + tr("MapMap files (*.%1)").arg(MM::FILE_EXTENSION)); // Restart video playback. XXX Hack videoTimer->start(); @@ -448,31 +479,16 @@ bool MainWindow::saveAs() if (! fileName.endsWith(MM::FILE_EXTENSION)) { std::cout << "filename doesn't end with expected extension: " << - fileName.toStdString() << std::endl; - fileName.append("."); - fileName.append(MM::FILE_EXTENSION); + fileName.toStdString() << std::endl; + fileName.append("."); + fileName.append(MM::FILE_EXTENSION); } // Save to filename. return saveFile(fileName); } -void MainWindow::importVideo() -{ - // Stop video playback to avoid lags. XXX Hack - videoTimer->stop(); - - // Pop-up file-choosing dialog to choose media file. - // TODO: restrict the type of files that can be imported - QString fileName = QFileDialog::getOpenFileName(this, tr("Import media source file"), settings.value("defaultVideoDir").toString(), tr("Video files (%1);;All files (*)").arg(MM::VIDEO_FILES_FILTER)); - // Restart video playback. XXX Hack - videoTimer->start(); - - if (!fileName.isEmpty()) - importMediaFile(fileName, false); -} - -void MainWindow::importImage() +void MainWindow::importMedia() { // Stop video playback to avoid lags. XXX Hack videoTimer->stop(); @@ -480,13 +496,22 @@ void MainWindow::importImage() // Pop-up file-choosing dialog to choose media file. // TODO: restrict the type of files that can be imported QString fileName = QFileDialog::getOpenFileName(this, - tr("Import media source file"), settings.value("defaultImageDir").toString(), tr("Image files (%1);;All files (*)").arg(MM::IMAGE_FILES_FILTER)); - + tr("Import media source file"), + settings.value("defaultVideoDir").toString(), + tr("Media files (%1 %2);;All files (*)") + .arg(MM::VIDEO_FILES_FILTER) + .arg(MM::IMAGE_FILES_FILTER)); // Restart video playback. XXX Hack videoTimer->start(); - if (!fileName.isEmpty()) - importMediaFile(fileName, true); + // Check if file is image or not + // according to file extension + if (!fileName.isEmpty()) { + if (MM::IMAGE_FILES_FILTER.contains(QFileInfo(fileName).suffix(), Qt::CaseInsensitive)) + importMediaFile(fileName, true); + else + importMediaFile(fileName, false); + } } void MainWindow::addColor() @@ -499,8 +524,8 @@ void MainWindow::addColor() // it should rather be a member of this class, or so. static QColor color = QColor(0, 255, 0, 255); color = QColorDialog::getColor(color, this, tr("Select Color"), - // QColorDialog::DontUseNativeDialog | - QColorDialog::ShowAlphaChannel); + // QColorDialog::DontUseNativeDialog | + QColorDialog::ShowAlphaChannel); if (color.isValid()) { addColorPaint(color); @@ -656,25 +681,25 @@ void MainWindow::about() // Pop-up about dialog. QMessageBox::about(this, tr("About MapMap"), - tr("

%1

" - "

Copyright © 2013 %2.

" - "

MapMap is a free software for video mapping.

" - "

Projection mapping, also known as video mapping and spatial augmented reality, " - "is a projection technology used to turn objects, often irregularly shaped, into " - "a display surface for video projection. These objects may be complex industrial " - "landscapes, such as buildings. By using specialized software, a two or three " - "dimensional object is spatially mapped on the virtual program which mimics the " - "real environment it is to be projected on. The software can interact with a " - "projector to fit any desired image onto the surface of that object. This " - "technique is used by artists and advertisers alike who can add extra dimensions, " - "optical illusions, and notions of movement onto previously static objects. The " - "video is commonly combined with, or triggered by, audio to create an " - "audio-visual narrative." - "This project was made possible by the support of the International Organization of " - "La Francophonie.

" - "

http://mapmap.info
" - "http://www.francophonie.org

" - ).arg(MM::VERSION, MM::COPYRIGHT_OWNERS)); + tr("

%1

" + "

Copyright © 2013 %2.

" + "

MapMap is a free software for video mapping.

" + "

Projection mapping, also known as video mapping and spatial augmented reality, " + "is a projection technology used to turn objects, often irregularly shaped, into " + "a display surface for video projection. These objects may be complex industrial " + "landscapes, such as buildings. By using specialized software, a two or three " + "dimensional object is spatially mapped on the virtual program which mimics the " + "real environment it is to be projected on. The software can interact with a " + "projector to fit any desired image onto the surface of that object. This " + "technique is used by artists and advertisers alike who can add extra dimensions, " + "optical illusions, and notions of movement onto previously static objects. The " + "video is commonly combined with, or triggered by, audio to create an " + "audio-visual narrative." + "This project was made possible by the support of the International Organization of " + "La Francophonie.

" + "

http://mapmap.info
" + "http://www.francophonie.org

" + ).arg(MM::VERSION, MM::COPYRIGHT_OWNERS)); // Restart video playback. XXX Hack videoTimer->start(); @@ -682,14 +707,37 @@ void MainWindow::about() void MainWindow::updateStatusBar() { - // Nothing to do for now. -// locationLabel->setText(spreadsheet->currentLocation()); -// formulaLabel->setText(spreadsheet->currentFormula()); + QPointF mousePos = destinationCanvas->mapToScene(destinationCanvas->mapFromGlobal(destinationCanvas->cursor().pos())); + if (currentSelectedItem) // Show mouse coordinate only if mappingList is not empty + mousePosLabel->setText("Mouse coordinate: X " + QString::number(mousePos.x()) + " Y " + QString::number(mousePos.y())); + else + mousePosLabel->setText(""); // Otherwise set empty text. + currentMessageLabel->setText(statusBar()->currentMessage()); + sourceZoomLabel->setText("Source: " + QString::number(int(sourceCanvas->getZoomFactor() * 100)).append(QChar('%'))); + destinationZoomLabel->setText("Destination: " + QString::number(int(destinationCanvas->getZoomFactor() * 100)).append(QChar('%'))); + undoLabel->setText(undoStack->text(undoStack->count() - 1)); +} + +void MainWindow::showMenuBar(bool shown) +{ + _showMenuBar = shown; + +#ifdef Q_OS_OSX // On Mac OS X + // Do nothing +#endif +#ifdef Q_OS_LINUX // On Linux + QString currentDesktop = QString(getenv("XDG_CURRENT_DESKTOP")).toLower(); + if (currentDesktop != "unity") + menuBar()->setVisible(shown); +#endif +#ifdef Q_OS_WIN // On Windows + menuBar()->setVisible(shown); +#endif } /** * Called when the user wants to delete an item. - * + * * Deletes either a Paint or a Mapping. */ void MainWindow::deleteItem() @@ -715,14 +763,14 @@ void MainWindow::deleteItem() { qCritical() << "Selected item neither a mapping nor a paint." << endl; } - } + } } -void MainWindow::cloneItem() +void MainWindow::duplicateMappingItem() { if (currentSelectedItem) { - cloneMappingItem(getItemId(*mappingList->currentItem())); + duplicateMapping(getItemId(*mappingList->currentItem())); } else { @@ -747,17 +795,42 @@ void MainWindow::renameMappingItem() // Set current item editable and rename it QListWidgetItem* item = mappingList->currentItem(); item->setFlags(item->flags() | Qt::ItemIsEditable); + // Used by context menu mappingList->editItem(item); + // Switch to mapping tab. contentTab->setCurrentWidget(mappingSplitter); } -void MainWindow::renameMappingItem(uid mappingId, QString name) +void MainWindow::setMappingitemLocked(bool locked) { - if (mappingList->count() > 0) { - mappingList->item(mappingId)->setText(name); + setMappingLocked(getItemId(*mappingList->currentItem()), locked); +} + +void MainWindow::setMappingitemVisible(bool visible) +{ + setMappingVisible(getItemId(*mappingList->currentItem()), !visible); +} + +void MainWindow::setMappingItemSolo(bool solo) +{ + setMappingSolo(getItemId(*mappingList->currentItem()), solo); +} + +void MainWindow::renameMapping(uid mappingId, const QString &name) +{ + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); + if (!mapping.isNull()) { + getItemFromId(*mappingList, mappingId)->setText(name); + mapping->setName(name); } } +void MainWindow::mappingListEditEnd(QWidget *editor) +{ + QString name = reinterpret_cast(editor)->text(); + renameMapping(getItemId(*mappingList->currentItem()), name); +} + void MainWindow::deletePaintItem() { if(currentSelectedItem) @@ -775,29 +848,39 @@ void MainWindow::renamePaintItem() // Set current item editable and rename it QListWidgetItem* item = paintList->currentItem(); item->setFlags(item->flags() | Qt::ItemIsEditable); + // Used by context menu paintList->editItem(item); + // Switch to paint tab contentTab->setCurrentWidget(paintSplitter); } -void MainWindow::renamePaintItem(uid paintId, QString name) +void MainWindow::renamePaint(uid paintId, const QString &name) { - if (paintList->count() > 0) { - paintList->item(paintId)->setText(name); + Paint::ptr paint = mappingManager->getPaintById(paintId); + if (!paint.isNull()) { + getItemFromId(*paintList, paintId)->setText(name); + paint->setName(name); } } +void MainWindow::paintListEditEnd(QWidget *editor) +{ + QString name = reinterpret_cast(editor)->text(); + renamePaint(getItemId(*paintList->currentItem()), name); +} + void MainWindow::openRecentFile() { - QAction *action = qobject_cast(sender()); - if (action) - loadFile(action->data().toString()); + QAction *action = qobject_cast(sender()); + if (action) + loadFile(action->data().toString()); } void MainWindow::openRecentVideo() { - QAction *action = qobject_cast(sender()); - if (action) - importMediaFile(action->data().toString(),false); + QAction *action = qobject_cast(sender()); + if (action) + importMediaFile(action->data().toString(),false); } bool MainWindow::clearProject() @@ -853,7 +936,7 @@ uid MainWindow::createMediaPaint(uid paintId, QString uri, float x, float y, { // Check if file exists before if (! fileExists(uri)) - uri = locateMediaFile(uri, isImage); + uri = locateMediaFile(uri, isImage); Texture* tex = 0; if (isImage) @@ -1104,14 +1187,27 @@ void MainWindow::setMappingVisible(uid mappingId, bool visible) void MainWindow::setMappingSolo(uid mappingId, bool solo) { - Q_UNUSED(mappingId); - Q_UNUSED(solo); + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); + if (!mapping.isNull()) { + // Turn this mapping into solo mode + mapping->setSolo(solo); + // Update canvases + updateCanvases(); + } } void MainWindow::setMappingLocked(uid mappingId, bool locked) { - Q_UNUSED(mappingId); - Q_UNUSED(locked); + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); + + if (!mapping.isNull()) { + // Lock position of mapping + mapping->setLocked(locked); + // Lock shape too. + mapping->getShape()->setLocked(locked); + // Update canvases + updateCanvases(); + } } void MainWindow::deleteMapping(uid mappingId) @@ -1120,10 +1216,10 @@ void MainWindow::deleteMapping(uid mappingId) if (Mapping::getUidAllocator().exists(mappingId)) { removeMappingItem(mappingId); - } + } } -void MainWindow::cloneMappingItem(uid mappingId) +void MainWindow::duplicateMapping(uid mappingId) { // Current Mapping Mapping::ptr mappingPtr = mappingManager->getMappingById(mappingId); @@ -1143,14 +1239,14 @@ void MainWindow::cloneMappingItem(uid mappingId) { if (shapeType == "quad") shapePtr = MShape::ptr(new Quad(shape->getVertex(0), shape->getVertex(1), - shape->getVertex(2), shape->getVertex(3))); + shape->getVertex(2), shape->getVertex(3))); if (shapeType == "triangle") shapePtr = MShape::ptr(new Triangle(shape->getVertex(0), shape->getVertex(1), shape->getVertex(2))); if (shapeType == "ellipse") shapePtr = MShape::ptr(new Ellipse(shape->getVertex(0), shape->getVertex(1), shape->getVertex(2), - shape->getVertex(3))); + shape->getVertex(3))); mapping = new ColorMapping(paint, shapePtr); } @@ -1160,14 +1256,14 @@ void MainWindow::cloneMappingItem(uid mappingId) if (shapeType == "mesh") shapePtr = MShape::ptr(new Mesh(shape->getVertex(0), shape->getVertex(1), - shape->getVertex(3), shape->getVertex(2))); + shape->getVertex(3), shape->getVertex(2))); if (shapeType == "triangle") shapePtr = MShape::ptr(new Triangle(shape->getVertex(0), shape->getVertex(1), shape->getVertex(2))); if (shapeType == "ellipse") shapePtr = MShape::ptr(new Ellipse(shape->getVertex(0), shape->getVertex(1), shape->getVertex(2), - shape->getVertex(3), shape->getVertex(4))); + shape->getVertex(3), shape->getVertex(4))); mapping = new TextureMapping(paint, shapePtr, inputShape); } @@ -1192,8 +1288,8 @@ void MainWindow::deletePaint(uid paintId, bool replace) { if (replace == false) { int r = QMessageBox::warning(this, tr("MapMap"), - tr("Remove this paint and all its associated mappings?"), - QMessageBox::Ok | QMessageBox::Cancel); + tr("Remove this paint and all its associated mappings?"), + QMessageBox::Ok | QMessageBox::Cancel); if (r == QMessageBox::Ok) { removePaintItem(paintId); @@ -1239,9 +1335,6 @@ void MainWindow::createLayout() mappingPropertyPanel->setDisabled(true); mappingPropertyPanel->setMinimumHeight(MAPPING_PROPERTY_PANEL_MINIMUM_HEIGHT); - // Create undo view. - undoView = new QUndoView(getUndoStack(), this); - // Create canvases. sourceCanvas = new SourceGLCanvas(this); sourceCanvas->setFocusPolicy(Qt::ClickFocus); @@ -1254,7 +1347,6 @@ void MainWindow::createLayout() destinationCanvas->setMinimumSize(CANVAS_MINIMUM_WIDTH, CANVAS_MINIMUM_HEIGHT); outputWindow = new OutputGLWindow(this, destinationCanvas); - outputWindow->setVisible(true); outputWindow->installEventFilter(destinationCanvas); outputWindow->installEventFilter(this); @@ -1268,8 +1360,12 @@ void MainWindow::createLayout() // Output changed -> change destinatioin // XXX si je decommente cette ligne alors quand je clique sur ajouter media ca gele... -// connect(outputWindow->getCanvas()->scene(), SIGNAL(changed(const QList&)), -// destinationCanvas, SLOT(updateCanvas())); + // connect(outputWindow->getCanvas()->scene(), SIGNAL(changed(const QList&)), + // destinationCanvas, SLOT(updateCanvas())); + + // Create console logging output + consoleWindow = ConsoleWindow::getInstance(); + consoleWindow->setVisible(false); // Create layout. paintSplitter = new QSplitter(Qt::Vertical); @@ -1321,49 +1417,53 @@ void MainWindow::createActions() newAction = new QAction(tr("&New"), this); newAction->setIcon(QIcon(":/new")); newAction->setShortcut(QKeySequence::New); - newAction->setStatusTip(tr("Create a new project")); + newAction->setToolTip(tr("Create a new project")); newAction->setIconVisibleInMenu(false); + addAction(newAction); connect(newAction, SIGNAL(triggered()), this, SLOT(newFile())); // Open. openAction = new QAction(tr("&Open..."), this); openAction->setIcon(QIcon(":/open")); openAction->setShortcut(QKeySequence::Open); - openAction->setStatusTip(tr("Open an existing project")); + openAction->setToolTip(tr("Open an existing project")); openAction->setIconVisibleInMenu(false); + addAction(openAction); connect(openAction, SIGNAL(triggered()), this, SLOT(open())); // Save. saveAction = new QAction(tr("&Save"), this); saveAction->setIcon(QIcon(":/save")); saveAction->setShortcut(QKeySequence::Save); - saveAction->setStatusTip(tr("Save the project")); + saveAction->setToolTip(tr("Save the project")); saveAction->setIconVisibleInMenu(false); + addAction(saveAction); connect(saveAction, SIGNAL(triggered()), this, SLOT(save())); // Save as. saveAsAction = new QAction(tr("Save &As..."), this); saveAsAction->setIcon(QIcon(":/save-as")); - saveAsAction->setShortcut(tr("Ctrl+Shift+S")); - saveAsAction->setStatusTip(tr("Save the project as...")); + saveAsAction->setShortcut(QKeySequence::SaveAs); + saveAsAction->setToolTip(tr("Save the project as...")); saveAsAction->setIconVisibleInMenu(false); + addAction(saveAsAction); connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs())); // Recents file for (int i = 0; i < MaxRecentFiles; i++) { - recentFileActions[i] = new QAction(this); - recentFileActions[i]->setVisible(false); - connect(recentFileActions[i], SIGNAL(triggered()), - this, SLOT(openRecentFile())); + recentFileActions[i] = new QAction(this); + recentFileActions[i]->setVisible(false); + connect(recentFileActions[i], SIGNAL(triggered()), + this, SLOT(openRecentFile())); } // Recent video for (int i = 0; i < MaxRecentVideo; i++) { - recentVideoActions[i] = new QAction(this); - recentVideoActions[i]->setVisible(false); - connect(recentVideoActions[i], SIGNAL(triggered()), this, SLOT(openRecentVideo())); + recentVideoActions[i] = new QAction(this); + recentVideoActions[i]->setVisible(false); + connect(recentVideoActions[i], SIGNAL(triggered()), this, SLOT(openRecentVideo())); } // Clear recent video list action @@ -1376,35 +1476,30 @@ void MainWindow::createActions() emptyRecentVideos->setEnabled(false); - // Import video. - importVideoAction = new QAction(tr("&Import Video File..."), this); - importVideoAction->setShortcut(tr("Ctrl+I")); - importVideoAction->setIcon(QIcon(":/add-video")); - importVideoAction->setStatusTip(tr("Import a video source file...")); - importVideoAction->setIconVisibleInMenu(false); - connect(importVideoAction, SIGNAL(triggered()), this, SLOT(importVideo())); - - // Import imiage. - importImageAction = new QAction(tr("&Import Image File..."), this); - importImageAction->setShortcut(tr("Ctrl+Shift+I")); - importImageAction->setIcon(QIcon(":/add-image")); - importImageAction->setStatusTip(tr("Import a image source file...")); - importImageAction->setIconVisibleInMenu(false); - connect(importImageAction, SIGNAL(triggered()), this, SLOT(importImage())); + // Import Media. + importMediaAction = new QAction(tr("&Import Media File..."), this); + importMediaAction->setShortcut(tr("Ctrl+I")); + importMediaAction->setIcon(QIcon(":/add-video")); + importMediaAction->setToolTip(tr("Import a video or image file...")); + importMediaAction->setIconVisibleInMenu(false); + addAction(importMediaAction); + connect(importMediaAction, SIGNAL(triggered()), this, SLOT(importMedia())); // Add color. addColorAction = new QAction(tr("Add &Color Paint..."), this); addColorAction->setShortcut(tr("Ctrl+Shift+A")); addColorAction->setIcon(QIcon(":/add-color")); - addColorAction->setStatusTip(tr("Add a color paint...")); + addColorAction->setToolTip(tr("Add a color paint...")); addColorAction->setIconVisibleInMenu(false); + addAction(addColorAction); connect(addColorAction, SIGNAL(triggered()), this, SLOT(addColor())); // Exit/quit. exitAction = new QAction(tr("E&xit"), this); - exitAction->setShortcut(tr("Ctrl+Q")); - exitAction->setStatusTip(tr("Exit the application")); + exitAction->setShortcut(QKeySequence::Quit); + exitAction->setToolTip(tr("Exit the application")); exitAction->setIconVisibleInMenu(false); + addAction(exitAction); connect(exitAction, SIGNAL(triggered()), this, SLOT(close())); // Undo action @@ -1412,68 +1507,104 @@ void MainWindow::createActions() undoAction->setShortcut(QKeySequence::Undo); undoAction->setIconVisibleInMenu(false); undoAction->setShortcutContext(Qt::ApplicationShortcut); + addAction(undoAction); //Redo action redoAction = undoStack->createRedoAction(this, tr("&Redo")); redoAction->setShortcut(QKeySequence::Redo); redoAction->setIconVisibleInMenu(false); redoAction->setShortcutContext(Qt::ApplicationShortcut); + addAction(redoAction); // About. aboutAction = new QAction(tr("&About"), this); - aboutAction->setStatusTip(tr("Show the application's About box")); + aboutAction->setToolTip(tr("Show the application's About box")); aboutAction->setIconVisibleInMenu(false); + addAction(aboutAction); connect(aboutAction, SIGNAL(triggered()), this, SLOT(about())); // Duplicate. - cloneAction = new QAction(tr("Duplicate"), this); - cloneAction->setShortcut(tr("Ctrl+D")); - cloneAction->setStatusTip(tr("Duplicate item")); - cloneAction->setIconVisibleInMenu(false); - connect(cloneAction, SIGNAL(triggered()), this, SLOT(cloneItem())); + cloneMappingAction = new QAction(tr("Duplicate"), this); + cloneMappingAction->setShortcut(Qt::CTRL + Qt::Key_D); + cloneMappingAction->setToolTip(tr("Duplicate item")); + cloneMappingAction->setIconVisibleInMenu(false); + addAction(cloneMappingAction); + connect(cloneMappingAction, SIGNAL(triggered()), this, SLOT(duplicateMappingItem())); // Delete mapping. - deleteMappingAction = new QAction(tr("Delete"), this); - deleteMappingAction->setShortcut(tr("CTRL+DEL")); - deleteMappingAction->setStatusTip(tr("Delete item")); + deleteMappingAction = new QAction(tr("Delete mapping"), this); + deleteMappingAction->setShortcut(QKeySequence::Delete); + deleteMappingAction->setToolTip(tr("Delete item")); deleteMappingAction->setIconVisibleInMenu(false); + addAction(deleteMappingAction); connect(deleteMappingAction, SIGNAL(triggered()), this, SLOT(deleteMappingItem())); // Rename mapping. renameMappingAction = new QAction(tr("Rename"), this); renameMappingAction->setShortcut(Qt::Key_F2); - renameMappingAction->setStatusTip(tr("Rename item")); + renameMappingAction->setToolTip(tr("Rename item")); renameMappingAction->setIconVisibleInMenu(false); + addAction(renameMappingAction); connect(renameMappingAction, SIGNAL(triggered()), this, SLOT(renameMappingItem())); + // Lock mapping. + mappingLockedAction = new QAction(tr("Lock mapping"), this); + mappingLockedAction->setToolTip(tr("Lock mapping item")); + mappingLockedAction->setIconVisibleInMenu(false); + mappingLockedAction->setCheckable(true); + mappingLockedAction->setChecked(false); + addAction(mappingLockedAction); + connect(mappingLockedAction, SIGNAL(triggered(bool)), this, SLOT(setMappingitemLocked(bool))); + + // Mute mapping. + mappingMuteAction = new QAction(tr("Mute mapping"), this); + mappingMuteAction->setToolTip(tr("Mute mapping item")); + mappingMuteAction->setIconVisibleInMenu(false); + mappingMuteAction->setCheckable(true); + mappingMuteAction->setChecked(false); + addAction(mappingMuteAction); + connect(mappingMuteAction, SIGNAL(triggered(bool)), this, SLOT(setMappingitemVisible(bool))); + + // Solo mapping. + mappingSoloAction = new QAction(tr("Solo mapping"), this); + mappingSoloAction->setToolTip(tr("solo mapping item")); + mappingSoloAction->setIconVisibleInMenu(false); + mappingSoloAction->setCheckable(true); + mappingSoloAction->setChecked(false); + addAction(mappingSoloAction); + connect(mappingSoloAction, SIGNAL(triggered(bool)), this, SLOT(setMappingItemSolo(bool))); + // Delete paint. - deletePaintAction = new QAction(tr("Delete"), this); + deletePaintAction = new QAction(tr("Delete paint"), this); //deletePaintAction->setShortcut(tr("CTRL+DEL")); - deletePaintAction->setStatusTip(tr("Delete item")); + deletePaintAction->setToolTip(tr("Delete item")); deletePaintAction->setIconVisibleInMenu(false); connect(deletePaintAction, SIGNAL(triggered()), this, SLOT(deletePaintItem())); // Rename paint. renamePaintAction = new QAction(tr("Rename"), this); //renamePaintAction->setShortcut(Qt::Key_F2); - renamePaintAction->setStatusTip(tr("Rename item")); + renamePaintAction->setToolTip(tr("Rename item")); renamePaintAction->setIconVisibleInMenu(false); + addAction(renamePaintAction); connect(renamePaintAction, SIGNAL(triggered()), this, SLOT(renamePaintItem())); // Preferences... preferencesAction = new QAction(tr("&Preferences..."), this); //preferencesAction->setIcon(QIcon(":/preferences")); - preferencesAction->setShortcut(tr("CTRL+,")); - preferencesAction->setStatusTip(tr("Configure preferences...")); + preferencesAction->setShortcut(Qt::CTRL + Qt::Key_Comma); + preferencesAction->setToolTip(tr("Configure preferences...")); //preferencesAction->setIconVisibleInMenu(false); + addAction(preferencesAction); connect(preferencesAction, SIGNAL(triggered()), this, SLOT(preferences())); // Add quad/mesh. addMeshAction = new QAction(tr("Add Quad/&Mesh"), this); addMeshAction->setShortcut(tr("CTRL+M")); addMeshAction->setIcon(QIcon(":/add-mesh")); - addMeshAction->setStatusTip(tr("Add quad/mesh")); + addMeshAction->setToolTip(tr("Add quad/mesh")); addMeshAction->setIconVisibleInMenu(false); + addAction(addMeshAction); connect(addMeshAction, SIGNAL(triggered()), this, SLOT(addMesh())); addMeshAction->setEnabled(false); @@ -1481,8 +1612,9 @@ void MainWindow::createActions() addTriangleAction = new QAction(tr("Add &Triangle"), this); addTriangleAction->setShortcut(tr("CTRL+T")); addTriangleAction->setIcon(QIcon(":/add-triangle")); - addTriangleAction->setStatusTip(tr("Add triangle")); + addTriangleAction->setToolTip(tr("Add triangle")); addTriangleAction->setIconVisibleInMenu(false); + addAction(addTriangleAction); connect(addTriangleAction, SIGNAL(triggered()), this, SLOT(addTriangle())); addTriangleAction->setEnabled(false); @@ -1490,8 +1622,9 @@ void MainWindow::createActions() addEllipseAction = new QAction(tr("Add &Ellipse"), this); addEllipseAction->setShortcut(tr("CTRL+E")); addEllipseAction->setIcon(QIcon(":/add-ellipse")); - addEllipseAction->setStatusTip(tr("Add ellipse")); + addEllipseAction->setToolTip(tr("Add ellipse")); addEllipseAction->setIconVisibleInMenu(false); + addAction(addEllipseAction); connect(addEllipseAction, SIGNAL(triggered()), this, SLOT(addEllipse())); addEllipseAction->setEnabled(false); @@ -1499,8 +1632,9 @@ void MainWindow::createActions() playAction = new QAction(tr("Play"), this); playAction->setShortcut(Qt::Key_Space); playAction->setIcon(QIcon(":/play")); - playAction->setStatusTip(tr("Play")); + playAction->setToolTip(tr("Play")); playAction->setIconVisibleInMenu(false); + addAction(playAction); connect(playAction, SIGNAL(triggered()), this, SLOT(play())); playAction->setVisible(true); @@ -1508,86 +1642,104 @@ void MainWindow::createActions() pauseAction = new QAction(tr("Pause"), this); pauseAction->setShortcut(Qt::Key_Space); pauseAction->setIcon(QIcon(":/pause")); - pauseAction->setStatusTip(tr("Pause")); + pauseAction->setToolTip(tr("Pause")); pauseAction->setIconVisibleInMenu(false); + addAction(pauseAction); connect(pauseAction, SIGNAL(triggered()), this, SLOT(pause())); pauseAction->setVisible(false); - // Pause. + // Rewind. rewindAction = new QAction(tr("Rewind"), this); rewindAction->setShortcut(tr("CTRL+R")); rewindAction->setIcon(QIcon(":/rewind")); - rewindAction->setStatusTip(tr("Rewind")); + rewindAction->setToolTip(tr("Rewind")); rewindAction->setIconVisibleInMenu(false); + addAction(rewindAction); connect(rewindAction, SIGNAL(triggered()), this, SLOT(rewind())); // Toggle display of output window. - displayOutputWindowAction = new QAction(tr("&Display Output Window"), this); - displayOutputWindowAction->setShortcut(tr("Ctrl+W")); - displayOutputWindowAction->setIcon(QIcon(":/output-window")); - displayOutputWindowAction->setStatusTip(tr("Display output window")); - displayOutputWindowAction->setIconVisibleInMenu(false); - displayOutputWindowAction->setCheckable(true); - displayOutputWindowAction->setChecked(true); - // Manage show/hide of GL output window. - connect(displayOutputWindowAction, SIGNAL(toggled(bool)), outputWindow, SLOT(setVisible(bool))); - // When closing the GL output window, uncheck the action in menu. - connect(outputWindow, SIGNAL(closed()), displayOutputWindowAction, SLOT(toggle())); - - // Toggle display of output window. - outputWindowFullScreenAction = new QAction(tr("&Fullscreen"), this); - outputWindowFullScreenAction->setIcon(QIcon(":/fullscreen")); - outputWindowFullScreenAction->setShortcut(tr("Ctrl+F")); - outputWindowFullScreenAction->setStatusTip(tr("Full screen")); - outputWindowFullScreenAction->setIconVisibleInMenu(false); - outputWindowFullScreenAction->setCheckable(true); - outputWindowFullScreenAction->setChecked(false); - // Manage fullscreen mode for output window. - connect(outputWindowFullScreenAction, SIGNAL(toggled(bool)), outputWindow, SLOT(setFullScreen(bool))); - // When fullscreen is toggled by the output window (eg. when pressing ESC), change the action checkbox. - connect(outputWindow, SIGNAL(fullScreenToggled(bool)), outputWindowFullScreenAction, SLOT(setChecked(bool))); - // Output window should be displayed for full screen option to be available. - connect(displayOutputWindowAction, SIGNAL(toggled(bool)), outputWindowFullScreenAction, SLOT(setEnabled(bool))); + outputFullScreenAction = new QAction(tr("&Full Screen"), this); + outputFullScreenAction->setShortcut(Qt::CTRL + Qt::Key_F); + outputFullScreenAction->setIcon(QIcon(":/fullscreen")); + outputFullScreenAction->setToolTip(tr("Full screen mode")); + outputFullScreenAction->setIconVisibleInMenu(false); + outputFullScreenAction->setCheckable(true); + // Don't be displayed by default + outputFullScreenAction->setChecked(false); + addAction(outputFullScreenAction); + // Manage fullscreen/modal show of GL output window. + connect(outputFullScreenAction, SIGNAL(toggled(bool)), outputWindow, SLOT(setFullScreen(bool))); + // When closing the GL output window or hit ESC key, uncheck the action in menu. + connect(outputWindow, SIGNAL(closed()), outputFullScreenAction, SLOT(toggle())); // Toggle display of canvas controls. displayControlsAction = new QAction(tr("&Display Canvas Controls"), this); - displayControlsAction->setShortcut(tr("Alt+D")); + displayControlsAction->setShortcut(Qt::ALT + Qt::Key_C); displayControlsAction->setIcon(QIcon(":/control-points")); - displayControlsAction->setStatusTip(tr("Display canvas controls")); + displayControlsAction->setToolTip(tr("Display canvas controls")); displayControlsAction->setIconVisibleInMenu(false); displayControlsAction->setCheckable(true); displayControlsAction->setChecked(_displayControls); + addAction(displayControlsAction); // Manage show/hide of canvas controls. connect(displayControlsAction, SIGNAL(toggled(bool)), this, SLOT(enableDisplayControls(bool))); // Toggle sticky vertices stickyVerticesAction = new QAction(tr("&Sticky Vertices"), this); - stickyVerticesAction->setShortcut(tr("Alt+S")); + stickyVerticesAction->setShortcut(Qt::ALT + Qt::Key_S); stickyVerticesAction->setIcon(QIcon(":/control-points")); - stickyVerticesAction->setStatusTip(tr("Enable sticky vertices")); + stickyVerticesAction->setToolTip(tr("Enable sticky vertices")); stickyVerticesAction->setIconVisibleInMenu(false); stickyVerticesAction->setCheckable(true); stickyVerticesAction->setChecked(_stickyVertices); + addAction(stickyVerticesAction); // Manage sticky vertices connect(stickyVerticesAction, SIGNAL(toggled(bool)), this, SLOT(enableStickyVertices(bool))); displayTestSignalAction = new QAction(tr("&Display Test Signal"), this); - displayTestSignalAction->setShortcut(tr("Alt+T")); + displayTestSignalAction->setShortcut(Qt::ALT + Qt::Key_T); displayTestSignalAction->setIcon(QIcon(":/control-points")); - displayTestSignalAction->setStatusTip(tr("Display test signal")); + displayTestSignalAction->setToolTip(tr("Display test signal")); displayTestSignalAction->setIconVisibleInMenu(false); displayTestSignalAction->setCheckable(true); displayTestSignalAction->setChecked(_displayTestSignal); + addAction(displayTestSignalAction); // Manage show/hide of test signal connect(displayTestSignalAction, SIGNAL(toggled(bool)), this, SLOT(enableTestSignal(bool))); // Toggle display of Undo Stack displayUndoStackAction = new QAction(tr("Display &Undo Stack"), this); - displayUndoStackAction->setShortcut(tr("Ctrl+U")); + displayUndoStackAction->setShortcut(Qt::ALT + Qt::Key_U); displayUndoStackAction->setCheckable(true); displayUndoStackAction->setChecked(_displayUndoStack); + addAction(displayUndoStackAction); // Manage show/hide of Undo Stack connect(displayUndoStackAction, SIGNAL(toggled(bool)), this, SLOT(displayUndoStack(bool))); + + // Toggle display of Console output + openConsoleAction = new QAction(tr("Open Conso&le"), this); + openConsoleAction->setShortcut(Qt::ALT + Qt::Key_L); + openConsoleAction->setCheckable(true); + openConsoleAction->setChecked(false); + addAction(openConsoleAction); + connect(openConsoleAction, SIGNAL(toggled(bool)), consoleWindow, SLOT(setVisible(bool))); + // uncheck action when window is closed + connect(consoleWindow, SIGNAL(windowClosed()), openConsoleAction, SLOT(toggle())); + + // Toggle display of zoom tool buttons + displayZoomToolAction = new QAction(tr("Display &Zoom Toolbar"), this); + displayZoomToolAction->setShortcut(Qt::ALT + Qt::Key_Z); + displayZoomToolAction->setCheckable(true); + displayZoomToolAction->setChecked(true); + addAction(displayZoomToolAction); + connect(displayZoomToolAction, SIGNAL(toggled(bool)), sourceCanvas, SLOT(showZoomToolBar(bool))); + connect(displayZoomToolAction, SIGNAL(toggled(bool)), destinationCanvas, SLOT(showZoomToolBar(bool))); + + // Toggle show/hide menuBar + showMenuBarAction = new QAction(tr("&Menu Bar"), this); + showMenuBarAction->setCheckable(true); + showMenuBarAction->setChecked(_showMenuBar); + connect(showMenuBarAction, SIGNAL(toggled(bool)), this, SLOT(showMenuBar(bool))); } void MainWindow::startFullScreen() @@ -1595,9 +1747,7 @@ void MainWindow::startFullScreen() // Remove canvas controls. displayControlsAction->setChecked(false); // Display output window. - displayOutputWindowAction->setChecked(true); - // Send fullscreen. - outputWindowFullScreenAction->setChecked(true); + outputFullScreenAction->setChecked(true); } void MainWindow::createMenus() @@ -1618,22 +1768,21 @@ void MainWindow::createMenus() fileMenu->addAction(saveAction); fileMenu->addAction(saveAsAction); fileMenu->addSeparator(); - fileMenu->addAction(importVideoAction); - fileMenu->addAction(importImageAction); + fileMenu->addAction(importMediaAction); fileMenu->addAction(addColorAction); // Recent file separator separatorAction = fileMenu->addSeparator(); recentFileMenu = fileMenu->addMenu(tr("Open Recents Projects")); for (int i = 0; i < MaxRecentFiles; ++i) - recentFileMenu->addAction(recentFileActions[i]); + recentFileMenu->addAction(recentFileActions[i]); recentFileMenu->addAction(clearRecentFileActions); // Recent import video recentVideoMenu = fileMenu->addMenu(tr("Open Recents Videos")); recentVideoMenu->addAction(emptyRecentVideos); for (int i = 0; i < MaxRecentVideo; ++i) - recentVideoMenu->addAction(recentVideoActions[i]); + recentVideoMenu->addAction(recentVideoActions[i]); // Exit fileMenu->addSeparator(); @@ -1642,31 +1791,60 @@ void MainWindow::createMenus() // Edit. editMenu = menuBar->addMenu(tr("&Edit")); + // Undo & Redo menu editMenu->addAction(undoAction); editMenu->addAction(redoAction); - editMenu->addAction(deleteMappingAction); + editMenu->addSeparator(); + // Source canvas menu + sourceMenu = editMenu->addMenu(tr("Source")); + sourceMenu->setEnabled(false); + sourceMenu->addAction(deletePaintAction); + sourceMenu->addAction(renamePaintAction); + // Destination canvas menu + destinationMenu = editMenu->addMenu(tr("Destination")); + destinationMenu->setEnabled(false); + destinationMenu->addAction(cloneMappingAction); + destinationMenu->addAction(deleteMappingAction); + destinationMenu->addAction(renameMappingAction); + editMenu->addSeparator(); + // Preferences editMenu->addAction(preferencesAction); // View. viewMenu = menuBar->addMenu(tr("&View")); - viewMenu->addAction(displayOutputWindowAction); - viewMenu->addAction(outputWindowFullScreenAction); + // Toolbars menu + toolBarsMenu = viewMenu->addMenu(tr("Toolbars")); +#ifdef Q_OS_LINUX + if (QString(getenv("XDG_CURRENT_DESKTOP")).toLower() != "unity") + toolBarsMenu->addAction(showMenuBarAction); +#endif +#ifdef Q_OS_WIN + toolBarsMenu->addAction(showMenuBarAction); +#endif + viewMenu->addSeparator(); viewMenu->addAction(displayControlsAction); viewMenu->addAction(stickyVerticesAction); viewMenu->addAction(displayTestSignalAction); viewMenu->addSeparator(); viewMenu->addAction(displayUndoStackAction); + viewMenu->addAction(displayZoomToolAction); + viewMenu->addSeparator(); + viewMenu->addAction(outputFullScreenAction); // Run. - runMenu = menuBar->addMenu(tr("&Run")); - runMenu->addAction(playAction); - runMenu->addAction(pauseAction); - runMenu->addAction(rewindAction); + playbackMenu = menuBar->addMenu(tr("&Playback")); + playbackMenu->addAction(playAction); + playbackMenu->addAction(pauseAction); + playbackMenu->addAction(rewindAction); + + // Tools + toolsMenu = menuBar->addMenu(tr("&Tools")); + toolsMenu->addAction(openConsoleAction); // Help. helpMenu = menuBar->addMenu(tr("&Help")); helpMenu->addAction(aboutAction); -// helpMenu->addAction(aboutQtAction); + // helpMenu->addAction(aboutQtAction); } @@ -1676,9 +1854,12 @@ void MainWindow::createMappingContextMenu() mappingContextMenu = new QMenu(this); // Add different Action - mappingContextMenu->addAction(cloneAction); + mappingContextMenu->addAction(cloneMappingAction); mappingContextMenu->addAction(deleteMappingAction); mappingContextMenu->addAction(renameMappingAction); + mappingContextMenu->addAction(mappingLockedAction); + mappingContextMenu->addAction(mappingMuteAction); + mappingContextMenu->addAction(mappingSoloAction); // Set context menu policy mappingList->setContextMenuPolicy(Qt::CustomContextMenu); @@ -1687,8 +1868,8 @@ void MainWindow::createMappingContextMenu() // Context Menu Connexions connect(mappingList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); - connect(destinationCanvas, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); - connect(outputWindow, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); + connect(destinationCanvas, SIGNAL(shapeContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); + connect(outputWindow->getCanvas(), SIGNAL(shapeContextMenuRequested(const QPoint&)), this, SLOT(showMappingContextMenu(const QPoint&))); } void MainWindow::createPaintContextMenu() @@ -1706,20 +1887,16 @@ void MainWindow::createPaintContextMenu() // Connexions connect(paintList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPaintContextMenu(const QPoint&))); - connect(sourceCanvas, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showPaintContextMenu(const QPoint&))); + connect(sourceCanvas, SIGNAL(shapeContextMenuRequested(const QPoint&)), this, SLOT(showPaintContextMenu(const QPoint&))); } void MainWindow::createToolBars() { - mainToolBar = addToolBar(tr("&File")); + mainToolBar = addToolBar(tr("&Toolbar")); mainToolBar->setIconSize(QSize(MM::TOP_TOOLBAR_ICON_SIZE, MM::TOP_TOOLBAR_ICON_SIZE)); mainToolBar->setMovable(false); - mainToolBar->addAction(importVideoAction); - mainToolBar->addAction(importImageAction); + mainToolBar->addAction(importMediaAction); mainToolBar->addAction(addColorAction); - mainToolBar->addAction(newAction); - mainToolBar->addAction(openAction); - mainToolBar->addAction(saveAction); mainToolBar->addSeparator(); @@ -1729,52 +1906,59 @@ void MainWindow::createToolBars() mainToolBar->addSeparator(); - mainToolBar->addAction(displayOutputWindowAction); - mainToolBar->addAction(outputWindowFullScreenAction); - mainToolBar->addAction(displayControlsAction); - mainToolBar->addAction(stickyVerticesAction); + mainToolBar->addAction(outputFullScreenAction); mainToolBar->addAction(displayTestSignalAction); - runToolBar = addToolBar(tr("&Run")); - runToolBar->setIconSize(QSize(MM::TOP_TOOLBAR_ICON_SIZE, MM::TOP_TOOLBAR_ICON_SIZE)); - runToolBar->setMovable(false); // XXX: style hack: dummy expanding widget allows the placement of toolbar at the top right // From: http://www.qtcentre.org/threads/9102-QToolbar-setContentsMargins - QWidget* spacer = new QWidget(runToolBar); + QWidget* spacer = new QWidget(mainToolBar); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - runToolBar->addWidget(spacer); - runToolBar->addAction(playAction); - runToolBar->addAction(pauseAction); - runToolBar->addAction(rewindAction); + mainToolBar->addWidget(spacer); + mainToolBar->addAction(playAction); + mainToolBar->addAction(pauseAction); + mainToolBar->addAction(rewindAction); + + // Disable toolbar context menu + mainToolBar->setContextMenuPolicy(Qt::PreventContextMenu); + + // Toggle show/hide of toolbar + showToolBarAction = mainToolBar->toggleViewAction(); + toolBarsMenu->addAction(showToolBarAction); // Add toolbars. addToolBar(Qt::TopToolBarArea, mainToolBar); - addToolBar(Qt::TopToolBarArea, runToolBar); - -// editToolBar = addToolBar(tr("&Edit")); -// editToolBar->addAction(cloneAction); -// editToolBar->addAction(deleteAction); -// editToolBar->addSeparator(); -// editToolBar->addAction(findAction); -// editToolBar->addAction(goToCellAction); } void MainWindow::createStatusBar() { -// locationLabel = new QLabel(" W999 "); -// locationLabel->setAlignment(Qt::AlignHCenter); -// locationLabel->setMinimumSize(locationLabel->sizeHint()); -// -// formulaLabel = new QLabel; -// formulaLabel->setIndent(3); -// -// statusBar()->addWidget(locationLabel); -// statusBar()->addWidget(formulaLabel, 1); -// -// connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)), this, -// SLOT(updateStatusBar())); -// connect(spreadsheet, SIGNAL(modified()), this, SLOT(spreadsheetModified())); + // Create canvases zoom level statut + destinationZoomLabel = new QLabel(statusBar()); + destinationZoomLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + destinationZoomLabel->setContentsMargins(2, 0, 0, 0); + sourceZoomLabel = new QLabel(statusBar()); + sourceZoomLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + sourceZoomLabel->setContentsMargins(2, 0, 0, 0); + // Undoview statut + undoLabel = new QLabel(statusBar()); + undoLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + undoLabel->setContentsMargins(2, 0, 0, 0); + // Standard message + currentMessageLabel = new QLabel(statusBar()); + currentMessageLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + currentMessageLabel->setContentsMargins(0, 0, 0, 0); + // Current location of the mouse + mousePosLabel = new QLabel(statusBar()); + mousePosLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken); + mousePosLabel->setContentsMargins(2, 0, 0, 0); + // Add permanently into the statut bar + statusBar()->addPermanentWidget(currentMessageLabel, 5); + statusBar()->addPermanentWidget(undoLabel, 4); + statusBar()->addPermanentWidget(mousePosLabel, 3); + statusBar()->addPermanentWidget(sourceZoomLabel, 1); + statusBar()->addPermanentWidget(destinationZoomLabel, 1); + + // Update the status bar updateStatusBar(); } @@ -1795,15 +1979,11 @@ void MainWindow::readSettings() // new in 0.1.2: if (settings.contains("displayOutputWindow")) { - displayOutputWindowAction->setChecked(settings.value("displayOutputWindow").toBool()); - } - if (settings.contains("outputWindowFullScreen")) - { - outputWindowFullScreenAction->setChecked(settings.value("outputWindowFullScreen").toBool()); + outputFullScreenAction->setChecked(settings.value("displayOutputWindow").toBool()); } if (settings.contains("displayTestSignal")) { - displayOutputWindowAction->setChecked(settings.value("displayTestSignal").toBool()); + outputFullScreenAction->setChecked(settings.value("displayTestSignal").toBool()); } config_osc_receive_port = settings.value("osc_receive_port", 12345).toInt(); @@ -1814,6 +1994,10 @@ void MainWindow::readSettings() // new in 0.3.2 if (settings.contains("displayUndoStack")) displayUndoStackAction->setChecked(settings.value("displayUndoStack").toBool()); + if (settings.contains("zoomToolBar")) + displayZoomToolAction->setChecked(settings.value("zoomToolBar").toBool()); + if (settings.contains("showMenuBar")) + showMenuBarAction->setChecked(settings.value("showMenuBar").toBool()); } void MainWindow::writeSettings() @@ -1826,11 +2010,12 @@ void MainWindow::writeSettings() settings.setValue("mappingSplitter", mappingSplitter->saveState()); settings.setValue("canvasSplitter", canvasSplitter->saveState()); settings.setValue("outputWindow", outputWindow->saveGeometry()); - settings.setValue("displayOutputWindow", displayOutputWindowAction->isChecked()); - settings.setValue("outputWindowFullScreen", outputWindowFullScreenAction->isChecked()); + settings.setValue("displayOutputWindow", outputFullScreenAction->isChecked()); settings.setValue("displayTestSignal", displayTestSignalAction->isChecked()); settings.setValue("osc_receive_port", config_osc_receive_port); settings.setValue("displayUndoStack", displayUndoStackAction->isChecked()); + settings.setValue("zoomToolBar", displayZoomToolAction->isChecked()); + settings.setValue("showMenuBar", showMenuBarAction->isChecked()); } bool MainWindow::okToContinue() @@ -1838,9 +2023,9 @@ bool MainWindow::okToContinue() if (isWindowModified()) { int r = QMessageBox::warning(this, tr("MapMap"), - tr("The document has been modified.\n" - "Do you want to save your changes?"), - QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); + tr("The document has been modified.\n" + "Do you want to save your changes?"), + QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (r == QMessageBox::Yes) { return save(); @@ -1861,10 +2046,10 @@ bool MainWindow::loadFile(const QString &fileName) if (! file.open(QFile::ReadOnly | QFile::Text)) { QMessageBox::warning(this, tr("Error reading mapping project file"), - tr("Cannot read file %1:\n%2.") - .arg(fileName) - .arg(file.errorString())); - return false; + tr("Cannot read file %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); + return false; } // Clear current project. @@ -1875,9 +2060,9 @@ bool MainWindow::loadFile(const QString &fileName) if (! reader.readFile(&file)) { QMessageBox::warning(this, tr("Error reading mapping project file"), - tr("Parse error in file %1:\n\n%2") - .arg(fileName) - .arg(reader.errorString())); + tr("Parse error in file %1:\n\n%2") + .arg(fileName) + .arg(reader.errorString())); } else { @@ -1895,9 +2080,9 @@ bool MainWindow::saveFile(const QString &fileName) if (! file.open(QFile::WriteOnly | QFile::Text)) { QMessageBox::warning(this, tr("Error saving mapping project"), - tr("Cannot write file %1:\n%2.") - .arg(fileName) - .arg(file.errorString())); + tr("Cannot write file %1:\n%2.") + .arg(fileName) + .arg(file.errorString())); return false; } @@ -1937,15 +2122,15 @@ void MainWindow::setCurrentFile(const QString &fileName) void MainWindow::setCurrentVideo(const QString &fileName) { - curVideo = fileName; + curVideo = fileName; - recentVideos = settings.value("recentVideos").toStringList(); - recentVideos.removeAll(curVideo); - recentVideos.prepend(curVideo); - while (recentVideos.size() > MaxRecentVideo) - recentVideos.removeLast(); - settings.setValue("recentVideos", recentVideos); - updateRecentVideoActions(); + recentVideos = settings.value("recentVideos").toStringList(); + recentVideos.removeAll(curVideo); + recentVideos.prepend(curVideo); + while (recentVideos.size() > MaxRecentVideo) + recentVideos.removeLast(); + settings.setValue("recentVideos", recentVideos); + updateRecentVideoActions(); } void MainWindow::updateRecentFileActions() @@ -1956,14 +2141,14 @@ void MainWindow::updateRecentFileActions() for (int j = 0; j < numRecentFiles; ++j) { QString text = tr("&%1 %2") - .arg(j + 1) - .arg(strippedName(recentFiles[j])); + .arg(j + 1) + .arg(strippedName(recentFiles[j])); recentFileActions[j]->setText(text); recentFileActions[j]->setData(recentFiles[j]); recentFileActions[j]->setVisible(true); clearRecentFileActions->setVisible(true); } - + for (int i = numRecentFiles; i < MaxRecentFiles; ++i) { recentFileActions[i]->setVisible(false); @@ -1982,37 +2167,37 @@ void MainWindow::updateRecentFileActions() void MainWindow::updateRecentVideoActions() { - recentVideos = settings.value("recentVideos").toStringList(); - int numRecentVideos = qMin(recentVideos.size(), int(MaxRecentVideo)); + recentVideos = settings.value("recentVideos").toStringList(); + int numRecentVideos = qMin(recentVideos.size(), int(MaxRecentVideo)); - for (int i = 0; i < numRecentVideos; ++i) - { - QString text = tr("&%1 %2") - .arg(i + 1) - .arg(strippedName(recentVideos[i])); - recentVideoActions[i]->setText(text); - recentVideoActions[i]->setData(recentVideos[i]); - recentVideoActions[i]->setVisible(true); - } + for (int i = 0; i < numRecentVideos; ++i) + { + QString text = tr("&%1 %2") + .arg(i + 1) + .arg(strippedName(recentVideos[i])); + recentVideoActions[i]->setText(text); + recentVideoActions[i]->setData(recentVideos[i]); + recentVideoActions[i]->setVisible(true); + } - for (int j = numRecentVideos; j < MaxRecentVideo; ++j) - recentVideoActions[j]->setVisible(false); + for (int j = numRecentVideos; j < MaxRecentVideo; ++j) + recentVideoActions[j]->setVisible(false); - if (numRecentVideos > 0) - { - emptyRecentVideos->setVisible(false); - } + if (numRecentVideos > 0) + { + emptyRecentVideos->setVisible(false); + } } void MainWindow::clearRecentFileList() { - recentFiles = settings.value("recentFiles").toStringList(); + recentFiles = settings.value("recentFiles").toStringList(); - while (recentFiles.size() > 0) - recentFiles.clear(); + while (recentFiles.size() > 0) + recentFiles.clear(); - settings.setValue("recentFiles", recentFiles); - updateRecentFileActions(); + settings.setValue("recentFiles", recentFiles); + updateRecentFileActions(); } // TODO @@ -2025,6 +2210,9 @@ bool MainWindow::importMediaFile(const QString &fileName, bool isImage) QFile file(fileName); QDir currentDir; + if (!fileSupported(fileName, isImage)) + return false; + bool live = false; if (!file.open(QIODevice::ReadOnly)) { if (file.isSequential()) @@ -2121,8 +2309,8 @@ void MainWindow::addPaintItem(uid paintId, const QIcon& icon, const QString& nam paintPropertyPanel->setEnabled(true); // When paint value is changed, update canvases. -// connect(paintGui.get(), SIGNAL(valueChanged()), -// this, SLOT(updateCanvases())); + // connect(paintGui.get(), SIGNAL(valueChanged()), + // this, SLOT(updateCanvases())); connect(paintGui.data(), SIGNAL(valueChanged(Paint::ptr)), this, SLOT(handlePaintChanged(Paint::ptr))); @@ -2234,7 +2422,7 @@ void MainWindow::addMappingItem(uid mappingId) connect(destinationCanvas, SIGNAL(shapeChanged(MShape*)), mapper.data(), SLOT(updateShape(MShape*))); - + // Switch to mapping tab. contentTab->setCurrentWidget(mappingSplitter); @@ -2337,7 +2525,7 @@ void MainWindow::clearWindow() clearProject(); } -bool MainWindow::fileExists(const QString file) +bool MainWindow::fileExists(const QString &file) { QFileInfo checkFile(file); @@ -2347,6 +2535,25 @@ bool MainWindow::fileExists(const QString file) return false; } +bool MainWindow::fileSupported(const QString &file, bool isImage) +{ + QFileInfo fileInfo(file); + QString fileExtension = fileInfo.suffix(); + + if (isImage) { + if (MM::IMAGE_FILES_FILTER.contains(fileExtension, Qt::CaseInsensitive)) + return true; + } else { + if (MM::VIDEO_FILES_FILTER.contains(fileExtension, Qt::CaseInsensitive)) + return true; + } + + QMessageBox::warning(this, tr("Warning"), + tr("The following file is not supported: %1") + .arg(fileInfo.fileName())); + return false; +} + QString MainWindow::locateMediaFile(const QString &uri, bool isImage) { // Get more info about url @@ -2363,18 +2570,18 @@ QString MainWindow::locateMediaFile(const QString &uri, bool isImage) // Show a warning and offer to locate the file QMessageBox::warning(this, - tr("Cannot load movie"), - tr("Unable to use the file « %1 » \n" - "The original file is not found. Will you locate?") - .arg(filename)); + tr("Cannot load movie"), + tr("Unable to use the file « %1 » \n" + "The original file is not found. Will you locate?") + .arg(filename)); // Set the new uri url = QFileDialog::getOpenFileName(this, - tr("Locate file « %1 »").arg(filename), - directory, - tr("%1 files (%2)") - .arg(mediaType) - .arg(mediaFilter)); + tr("Locate file « %1 »").arg(filename), + directory, + tr("%1 files (%2)") + .arg(mediaType) + .arg(mediaFilter)); return url; } @@ -2397,8 +2604,14 @@ void MainWindow::updateCanvases() sourceCanvas->update(); destinationCanvas->update(); outputWindow->getCanvas()->update(); -} + // Update position of zoom toolbar + sourceCanvas->updateZoomToolbar(); + destinationCanvas->updateZoomToolbar(); + + // Update statut bar + updateStatusBar(); +} void MainWindow::enableDisplayControls(bool display) { @@ -2416,10 +2629,13 @@ void MainWindow::displayUndoStack(bool display) { _displayUndoStack = display; + // Create undo view. + undoView = new QUndoView(getUndoStack(), this); + if (display) { contentTab->addTab(undoView, tr("Undo stack")); } else { - contentTab->removeTab(contentTab->indexOf(undoView)); + contentTab->removeTab(2); } } @@ -2431,6 +2647,13 @@ void MainWindow::enableStickyVertices(bool value) void MainWindow::showMappingContextMenu(const QPoint &point) { QWidget *objectSender = dynamic_cast(sender()); + uid mappingId = getItemId(*mappingList->currentItem()); + Mapping::ptr mapping = mappingManager->getMappingById(mappingId); + + // Switch to right action check state + mappingLockedAction->setChecked(mapping->isLocked()); + mappingMuteAction->setChecked(!mapping->isVisible()); + mappingSoloAction->setChecked(mapping->isSolo()); if (objectSender != NULL && mappingList->count() > 0) mappingContextMenu->exec(objectSender->mapToGlobal(point)); @@ -2457,11 +2680,17 @@ void MainWindow::connectProjectWidgets() connect(paintList, SIGNAL(itemPressed(QListWidgetItem*)), this, SLOT(handleItemSelected(QListWidgetItem*))); -// connect(paintList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), -// this, SLOT(handleItemDoubleClicked(QListWidgetItem*))); + // connect(paintList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + // this, SLOT(handleItemDoubleClicked(QListWidgetItem*))); connect(paintList, SIGNAL(itemActivated(QListWidgetItem*)), this, SLOT(handleItemSelected(QListWidgetItem*))); + // Rename Paint with double click + connect(paintList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + this, SLOT(renamePaintItem())); + // When finish to edit mapping item + connect(paintList->itemDelegate(), SIGNAL(commitData(QWidget*)), + this, SLOT(paintListEditEnd(QWidget*))); connect(mappingList, SIGNAL(itemSelectionChanged()), this, SLOT(handleMappingItemSelectionChanged())); @@ -2480,12 +2709,12 @@ void MainWindow::connectProjectWidgets() connect(mappingList->model(), SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex &, int)), this, SLOT(handleMappingIndexesMoved())); - // Rename mapping with double click - connect(mappingList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), - this, SLOT(renameMappingItem())); - // Rename Paint with double click - connect(paintList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), - this, SLOT(renamePaintItem())); + // Rename mapping with double click + connect(mappingList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + this, SLOT(renameMappingItem())); + // When finish to edit mapping item + connect(mappingList->itemDelegate(), SIGNAL(commitData(QWidget*)), + this, SLOT(mappingListEditEnd(QWidget*))); } void MainWindow::disconnectProjectWidgets() @@ -2563,7 +2792,7 @@ QIcon MainWindow::createImageIcon(const QString& filename) { void MainWindow::setCurrentPaint(int uid) { if (uid == NULL_UID) - removeCurrentPaint(); + removeCurrentPaint(); else { if (currentPaintId != uid) { currentPaintId = uid; @@ -2606,7 +2835,11 @@ void MainWindow::startOscReceiver() int port = config_osc_receive_port; std::ostringstream os; os << port; - std::cout << "OSC port: " << port << std::endl; +#if QT_VERSION >= 0x050500 + QMessageLogger(__FILE__, __LINE__, 0).info() << "OSC port: " << port ; +#else + QMessageLogger(__FILE__, __LINE__, 0).debug() << "OSC port: " << port ; +#endif osc_interface.reset(new OscInterface(os.str())); if (port != 0) { @@ -2684,7 +2917,7 @@ void MainWindow::pollOscInterface() // std::cout << std::endl; // std::cout.flush(); // } -// +// // if (command.size() < 2) // return; // if (command.at(0).type() != QVariant::String) @@ -2693,7 +2926,7 @@ void MainWindow::pollOscInterface() // return; // std::string path = command.at(0).toString().toStdString(); // std::string typetags = command.at(1).toString().toStdString(); -// +// // // Handle all OSC messages here // if (path == "/image/uri" && typetags == "s") // { @@ -2714,38 +2947,38 @@ void MainWindow::pollOscInterface() bool MainWindow::setTextureUri(int texture_id, const std::string &uri) { - // TODO: const QString & + // TODO: const QString & - bool success = false; - Paint::ptr paint = this->mappingManager->getPaintById(texture_id); - if (paint.isNull()) + bool success = false; + Paint::ptr paint = this->mappingManager->getPaintById(texture_id); + if (paint.isNull()) + { + std::cout << "No such texture paint id " << texture_id << std::endl; + success = false; + } + else + { + if (paint->getType() == "media") { - std::cout << "No such texture paint id " << texture_id << std::endl; - success = false; + Media *media = static_cast(paint.data()); // FIXME: use sharedptr cast + videoTimer->stop(); + success = media->setUri(QString(uri.c_str())); + videoTimer->start(); + } + else if (paint->getType() == "image") + { + Image *media = (Image*) paint.data(); // FIXME: use sharedptr cast + videoTimer->stop(); + success = media->setUri(QString(uri.c_str())); + videoTimer->start(); } else { - if (paint->getType() == "media") - { - Media *media = static_cast(paint.data()); // FIXME: use sharedptr cast - videoTimer->stop(); - success = media->setUri(QString(uri.c_str())); - videoTimer->start(); - } - else if (paint->getType() == "image") - { - Image *media = (Image*) paint.data(); // FIXME: use sharedptr cast - videoTimer->stop(); - success = media->setUri(QString(uri.c_str())); - videoTimer->start(); - } - else - { - std::cout << "Paint id " << texture_id << " is not a media texture." << std::endl; - return false; - } + std::cout << "Paint id " << texture_id << " is not a media texture." << std::endl; + return false; } - return success; + } + return success; } bool MainWindow::setTextureRate(int texture_id, double rate) @@ -2753,23 +2986,23 @@ bool MainWindow::setTextureRate(int texture_id, double rate) Paint::ptr paint = this->mappingManager->getPaintById(texture_id); if (paint.isNull()) { - std::cout << "No such texture paint id " << texture_id << std::endl; - return false; + std::cout << "No such texture paint id " << texture_id << std::endl; + return false; } else { - if (paint->getType() == "media") - { - Media *media = static_cast(paint.data()); // FIXME: use sharedptr cast - videoTimer->stop(); - media->setRate(rate); - videoTimer->start(); - } - else - { - std::cout << "Paint id " << texture_id << " is not a media texture." << std::endl; - return false; - } + if (paint->getType() == "media") + { + Media *media = static_cast(paint.data()); // FIXME: use sharedptr cast + videoTimer->stop(); + media->setRate(rate); + videoTimer->start(); + } + else + { + std::cout << "Paint id " << texture_id << " is not a media texture." << std::endl; + return false; + } } return true; } @@ -2786,15 +3019,15 @@ bool MainWindow::setTextureVolume(int texture_id, double volume) { if (paint->getType() == "media") { - Media *media = static_cast(paint.data()); // FIXME: use sharedptr cast - videoTimer->stop(); - media->setVolume(volume); - videoTimer->start(); + Media *media = static_cast(paint.data()); // FIXME: use sharedptr cast + videoTimer->stop(); + media->setVolume(volume); + videoTimer->start(); } else { - std::cout << "Paint id " << texture_id << " is not a media texture." << std::endl; - return false; + std::cout << "Paint id " << texture_id << " is not a media texture." << std::endl; + return false; } } return true; diff --git a/MainWindow.h b/MainWindow.h index 537fa63..7dbda87 100644 --- a/MainWindow.h +++ b/MainWindow.h @@ -28,6 +28,7 @@ #include #include #include +#include #include "MM.h" @@ -39,6 +40,7 @@ #include "OutputGLWindow.h" #include "PreferencesDialog.h" +#include "ConsoleWindow.h" #include "MappingManager.h" @@ -70,6 +72,7 @@ protected: // Events /////////////////////////////////////////////////////////////////////////////////////////////////// void closeEvent(QCloseEvent *event); bool eventFilter(QObject *obj, QEvent *event); + void keyPressEvent(QKeyEvent *event); // Slots //////////////////////////////////////////////////////////////////////////////////////////////////// private slots: @@ -81,11 +84,11 @@ private slots: void preferences(); bool save(); bool saveAs(); - void importVideo(); - void importImage(); + void importMedia(); void addColor(); void about(); void updateStatusBar(); + void showMenuBar(bool shown); void openRecentFile(); void clearRecentFileList(); void openRecentVideo(); @@ -93,12 +96,17 @@ private slots: // Edit menu. void deleteItem(); // Context menu for mappings. - void cloneItem(); + void duplicateMappingItem(); void deleteMappingItem(); void renameMappingItem(); + void setMappingitemLocked(bool locked); + void setMappingitemVisible(bool visible); + void setMappingItemSolo(bool solo); + void mappingListEditEnd(QWidget* editor); // Context menu for paints void deletePaintItem(); void renamePaintItem(); + void paintListEditEnd(QWidget* editor); // Widget callbacks. void handlePaintItemSelectionChanged(); @@ -179,7 +187,7 @@ public slots: void deleteMapping(uid mappingId); /// Clone/duplicate a mapping - void cloneMappingItem(uid mappingId); + void duplicateMapping(uid mappingId); /// Deletes/removes a paint and all associated mappigns. void deletePaint(uid paintId, bool replace); @@ -240,11 +248,13 @@ public: void addPaintItem(uid paintId, const QIcon& icon, const QString& name); void updatePaintItem(uid paintId, const QIcon& icon, const QString& name); void removePaintItem(uid paintId); - void renameMappingItem(uid mappingId, QString name); - void renamePaintItem(uid paintId, QString name); + void renameMapping(uid mappingId, const QString& name); + void renamePaint(uid paintId, const QString& name); void clearWindow(); // Check if the file exists - bool fileExists(const QString file); + bool fileExists(const QString& file); + // Check if the file is supported + bool fileSupported(const QString& file, bool isImage); // Locate the file not found QString locateMediaFile(const QString& uri, bool isImage); @@ -273,32 +283,40 @@ private: QMenu *fileMenu; QMenu *editMenu; QMenu *viewMenu; - QMenu *runMenu; + QMenu *toolsMenu; + QMenu *playbackMenu; QMenu *helpMenu; QMenu *recentFileMenu; QMenu *recentVideoMenu; QMenu *mappingContextMenu; QMenu *paintContextMenu; + // Some menus when need to be separated + QMenu *sourceMenu; + QMenu *destinationMenu; + QMenu *toolBarsMenu; // Toolbar. QToolBar *mainToolBar; - QToolBar *runToolBar; // Actions. QAction *separatorAction; QAction *newAction; QAction *openAction; - QAction *importVideoAction; - QAction *importImageAction; + QAction *importMediaAction; QAction *addColorAction; QAction *saveAction; QAction *saveAsAction; QAction *exitAction; QAction *undoAction; QAction *redoAction; - QAction *cloneAction; + // Mappings context menu actions + QAction *cloneMappingAction; QAction *deleteMappingAction; QAction *renameMappingAction; + QAction *mappingSoloAction; + QAction *mappingLockedAction; + QAction *mappingMuteAction; + // Paints context menu action QAction *deletePaintAction; QAction *renamePaintAction; QAction *preferencesAction; @@ -314,13 +332,15 @@ private: QAction *pauseAction; QAction *rewindAction; - QAction *displayOutputWindowAction; - //QAction *outputWindowHasCursor; - QAction *outputWindowFullScreenAction; + QAction *outputFullScreenAction; QAction *displayControlsAction; QAction *displayTestSignalAction; QAction *stickyVerticesAction; QAction *displayUndoStackAction; + QAction *displayZoomToolAction; + QAction *openConsoleAction; + QAction *showMenuBarAction; + QAction *showToolBarAction; enum { MaxRecentFiles = 10 }; enum { MaxRecentVideo = 5 }; @@ -343,6 +363,7 @@ private: SourceGLCanvas* sourceCanvas; DestinationGLCanvas* destinationCanvas; OutputGLWindow* outputWindow; + ConsoleWindow* consoleWindow; QSplitter* mainSplitter; QSplitter* canvasSplitter; @@ -398,6 +419,9 @@ private: bool _displayUndoStack; + // Menu bar hidden state + bool _showMenuBar; + // Keeps track of the current selected item, wether it's a paint or mapping. QListWidgetItem* currentSelectedItem; QTimer *videoTimer; @@ -407,6 +431,12 @@ private: // UndoStack QUndoStack *undoStack; + // Labels for status bar + QLabel *destinationZoomLabel; + QLabel *sourceZoomLabel; + QLabel *undoLabel; + QLabel *currentMessageLabel; + QLabel *mousePosLabel; public: @@ -415,6 +445,8 @@ public: MappingGui::ptr getMappingGuiByMappingId(uint id) const { return mappers[id]; } uid getCurrentPaintId() const { return currentPaintId; } uid getCurrentMappingId() const { return currentMappingId; } + Mapping::ptr getCurrentMapping() const { return mappingManager->getMappingById(currentMappingId); } + Paint::ptr getCurrentPaint() const { return mappingManager->getPaintById(currentPaintId); } OutputGLWindow* getOutputWindow() const { return outputWindow; } bool hasCurrentPaint() const { return _hasCurrentPaint; } bool hasCurrentMapping() const { return _hasCurrentMapping; } diff --git a/MapperGLCanvas.cpp b/MapperGLCanvas.cpp index b72f899..2a41d7e 100644 --- a/MapperGLCanvas.cpp +++ b/MapperGLCanvas.cpp @@ -30,7 +30,8 @@ MapperGLCanvas::MapperGLCanvas(MainWindow* mainWindow, QWidget* parent, const QG _activeVertex(NO_VERTEX), _shapeGrabbed(false), // comment out? _shapeFirstGrab(false), // comment out? - _zoomLevel(0) + _zoomLevel(0), + _shapeIsAdapted(false) { // For now clicking on the window doesn't do anything. setDragMode(QGraphicsView::NoDrag); @@ -54,6 +55,11 @@ MapperGLCanvas::MapperGLCanvas(MainWindow* mainWindow, QWidget* parent, const QG setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers), this, shareWidget)); setViewportUpdateMode(QGraphicsView::FullViewportUpdate); + // Create zoom tools layout + createZoomToolsLayout(); + // Disable zoom tool buttons + enableZoomToolBar(false); + // TODO: do we need to delete scene (or call new QGraphicsScene(this)?) setScene(scene ? scene : new QGraphicsScene); @@ -63,12 +69,12 @@ MapperGLCanvas::MapperGLCanvas(MainWindow* mainWindow, QWidget* parent, const QG MShape::ptr MapperGLCanvas::getCurrentShape() { - return getShapeFromMappingId(MainWindow::instance()->getCurrentMappingId()); + return getShapeFromMapping(MainWindow::instance()->getCurrentMapping()); } QSharedPointer MapperGLCanvas::getCurrentShapeGraphicsItem() { - return getShapeGraphicsItemFromMappingId(MainWindow::instance()->getCurrentMappingId()); + return getShapeGraphicsItemFromMapping(MainWindow::instance()->getCurrentMapping()); } // Draws foreground (displays crosshair if needed). @@ -98,6 +104,97 @@ void MapperGLCanvas::currentShapeWasChanged() emit shapeChanged(getCurrentShape().data()); } +void MapperGLCanvas::applyZoomToView() +{ + // Re-bound zoom (for consistency). + qreal zoomFactor = getZoomFactor(); + // Get first of the list of all the views + QGraphicsView* view = this->scene()->views().first(); + // Resets the view transformation matrix + view->resetMatrix(); + // Scale the current view + view->scale(zoomFactor, zoomFactor); + // And update + view->update(); + // Update dropdown menu + updateDropdownMenu(); +} + +void MapperGLCanvas::createZoomToolsLayout() +{ + // Create zoom tool bar + _zoomToolBar = new QWidget(this); + _zoomToolBar->setObjectName("zoom-toolbox"); + + // Create vertical layout for widgets + QHBoxLayout* buttonsLayout = new QHBoxLayout; + buttonsLayout->setContentsMargins(0, 0, 5, 0); + // Create buttons + // Zoom In button + _zoomInButton = new QPushButton; + _zoomInButton->setIcon(QIcon(":/zoom-in")); + _zoomInButton->setIconSize(QSize(MM::ZOOM_TOOLBAR_ICON_SIZE, MM::ZOOM_TOOLBAR_ICON_SIZE)); + _zoomInButton->setToolTip(tr("Enlarge the shape")); + _zoomInButton->setFixedSize(MM::ZOOM_TOOLBAR_BUTTON_SIZE, MM::ZOOM_TOOLBAR_BUTTON_SIZE); + _zoomInButton->setObjectName("zoom-in"); + connect(_zoomInButton, SIGNAL(clicked()), this, SLOT(increaseZoomLevel())); + // Zoom Out button + _zoomOutButton = new QPushButton; + _zoomOutButton->setIcon(QIcon(":/zoom-out")); + _zoomOutButton->setIconSize(QSize(MM::ZOOM_TOOLBAR_ICON_SIZE, MM::ZOOM_TOOLBAR_ICON_SIZE)); + _zoomOutButton->setToolTip(tr("Shrink the shape")); + _zoomOutButton->setFixedSize(MM::ZOOM_TOOLBAR_BUTTON_SIZE, MM::ZOOM_TOOLBAR_BUTTON_SIZE); + _zoomOutButton->setObjectName("zoom-out"); + connect(_zoomOutButton, SIGNAL(clicked()), this, SLOT(decreaseZoomLevel())); + // Reset to normal size button. + _resetZoomButton = new QPushButton; + _resetZoomButton->setIcon(QIcon(":/reset-zoom")); + _resetZoomButton->setIconSize(QSize(MM::ZOOM_TOOLBAR_ICON_SIZE, MM::ZOOM_TOOLBAR_ICON_SIZE)); + _resetZoomButton->setToolTip(tr("Reset the shape to the normal size")); + _resetZoomButton->setFixedSize(MM::ZOOM_TOOLBAR_BUTTON_SIZE, MM::ZOOM_TOOLBAR_BUTTON_SIZE); + _resetZoomButton->setObjectName("reset-zoom"); + connect(_resetZoomButton, SIGNAL(clicked()), this, SLOT(resetZoomLevel())); + // Fit to view button + _fitToViewButton = new QPushButton; + _fitToViewButton->setIcon(QIcon(":/zoom-fit")); + _fitToViewButton->setIconSize(QSize(MM::ZOOM_TOOLBAR_ICON_SIZE, MM::ZOOM_TOOLBAR_ICON_SIZE)); + _fitToViewButton->setToolTip(tr("Fit the shape to content view")); + _fitToViewButton->setFixedSize(MM::ZOOM_TOOLBAR_BUTTON_SIZE, MM::ZOOM_TOOLBAR_BUTTON_SIZE); + _fitToViewButton->setObjectName("zoom-fit"); + connect(_fitToViewButton, SIGNAL(clicked()), this, SLOT(fitShapeInView())); + + // Create separator + QFrame *separator = new QFrame(_zoomToolBar); + separator->setFixedSize(5, 30); + separator->setFrameShape(QFrame::VLine); + + // Create the dropdowm menu + _dropdownMenu = new QComboBox; + // make some settings + _dropdownMenu->setObjectName("dropdown-menu"); + // Create if empty or update list + updateDropdownMenu(); + // And listen + connect(_dropdownMenu, SIGNAL(activated(QString)), this, SLOT(setZoomFromMenu(QString))); + + // Add widgets into layout + buttonsLayout->addWidget(_zoomInButton); + buttonsLayout->addWidget(_zoomOutButton); + buttonsLayout->addWidget(_resetZoomButton); + buttonsLayout->addWidget(_fitToViewButton); + buttonsLayout->addWidget(separator); + buttonsLayout->addWidget(_dropdownMenu); + + // Insert layout in widget + _zoomToolBar->setLayout(buttonsLayout); +} + +void MapperGLCanvas::updateZoomToolbar() +{ + _zoomToolBar->move(this->viewport()->width() - _zoomToolBar->width(), + this->viewport()->height() - _zoomToolBar->height()); +} + // void MapperGLCanvas::mousePressEvent(QMouseEvent* event) @@ -139,7 +236,8 @@ void MapperGLCanvas::mousePressEvent(QMouseEvent* event) _activeVertex = i; minDistance = dist; - _vertexGrabbed = true; + // Vertex can be grabbed only if the mapping is not locked + _vertexGrabbed = !shape->isLocked() ? false : true; mousePressedOnSomething = true; _grabbedObjectStartScenePosition = shape->getVertex(i); @@ -161,7 +259,7 @@ void MapperGLCanvas::mousePressEvent(QMouseEvent* event) QVector mappings = manager.getVisibleMappings(); for (QVector::const_iterator it = mappings.end() - 1; it >= mappings.begin(); --it) { - MShape::ptr shape = getShapeFromMappingId((*it)->getId()); + MShape::ptr shape = getShapeFromMapping(*it); // Check if mouse was pressed on that shape. if (shape && shape->includesPoint(pos)) @@ -190,12 +288,21 @@ void MapperGLCanvas::mousePressEvent(QMouseEvent* event) { if (selectedShape && selectedShape->includesPoint(pos)) { - _shapeGrabbed = true; + // Shape can be grabbed only if it is not locked + _shapeGrabbed = selectedShape->isLocked() ? false : true; _shapeFirstGrab = true; _grabbedObjectStartScenePosition = pos; } } + // Show the shape/mapping context menu + if (event->button() & Qt::RightButton) + { + if (selectedShape && selectedShape->includesPoint(pos)) + { + emit shapeContextMenuRequested(event->pos()); + } + } } if (mousePressedOnSomething) @@ -317,47 +424,95 @@ void MapperGLCanvas::keyPressEvent(QKeyEvent* event) { MShape::ptr shape = getCurrentShape(); QPoint pos = mapFromScene(shape->getVertex(_activeVertex)); + handledKey = true; - switch (event->key()) { - // TODO: key tab should switch to next vertex: not working because somehow caught at a higher level - // to switch between frames of the layout -// case Qt::Key_Tab: -// if (shape) -// _activeVertex = (_activeVertex + 1) % shape->nVertices(); -// p = shape->getVertex(_activeVertex); // reset to new vertex -// qDebug() << "New active vertex : " << _activeVertex << endl; -// break; - // Handle pixel-wise adjustments of vertex. - case Qt::Key_Up: - pos.ry()--; - break; - case Qt::Key_Down: - pos.ry()++; - break; - case Qt::Key_Right: - pos.rx()++; - break; - case Qt::Key_Left: - pos.rx()--; - break; - default: - if (event->matches(QKeySequence::Undo)) - undoStack->undo(); + if (event->modifiers() & Qt::ShiftModifier) { + + // SHIFT + directional keys allow move with large steps + if (event->key() == Qt::Key_Up) + pos.ry() -= MM::VERTEX_MOVES_STEP; + else if (event->key() == Qt::Key_Down) + pos.ry() += MM::VERTEX_MOVES_STEP; + else if (event->key() == Qt::Key_Right) + pos.rx() += MM::VERTEX_MOVES_STEP; + else if (event->key() == Qt::Key_Left) + pos.rx() -= MM::VERTEX_MOVES_STEP; + + // SHIFT+Space to switch between vertex + else if (event->key() == Qt::Key_Space) { + if (shape) + _activeVertex = (_activeVertex + 1) % shape->nVertices(); + pos = shape->getVertex(_activeVertex).toPoint(); // reset to new vertex + } - else if (event->matches(QKeySequence::Redo)) - undoStack->redo(); else handledKey = false; - break; + } + else + { + switch (event->key()) { + case Qt::Key_Up: + pos.ry()--; + break; + case Qt::Key_Down: + pos.ry()++; + break; + case Qt::Key_Right: + pos.rx()++; + break; + case Qt::Key_Left: + pos.rx()--; + break; + default: + handledKey = false; + break; + } } - // Remap window position to scene. - QPointF scenePos = mapToScene(pos); + if (handledKey) + // Enable to Undo and Redo when arrow keys move the position of vertices + undoStack->push(new MoveVertexCommand(this, TransformShapeCommand::STEP, _activeVertex, mapToScene(pos))); + } - // TODO: this will always be called even if no arrow key has been pressed (small performance issue). - // Enable to Undo and Redo when arrow keys move the position of vertices - undoStack->push(new MoveVertexCommand(this, TransformShapeCommand::STEP, _activeVertex, scenePos)); + else { + // Take scroll bar current coordinate + int scrollX = this->horizontalScrollBar()->value(); + int scrollY = this->verticalScrollBar()->value(); + + handledKey = true; + if (event->matches(QKeySequence::Undo)) + undoStack->undo(); + else if (event->matches(QKeySequence::Redo)) + undoStack->redo(); + // Case 1: zoom in with CTRL++ + else if (event->matches(QKeySequence::ZoomIn)) + increaseZoomLevel(); + else if (event->matches(QKeySequence::ZoomOut)) + decreaseZoomLevel(); + else if (event->modifiers() & Qt::ControlModifier) { + if(event->key() == Qt::Key_0) + resetZoomLevel(); + // Case 2: zoom in with CTRL+= + else if (event->key() == Qt::Key_Equal || + // Case 3: zoom in with CTRL+SHIFT++ + (event->modifiers() & Qt::ShiftModifier && event->key() == Qt::Key_Plus)) + increaseZoomLevel(); + } + else if(event->key() == Qt::Key_Up) + scrollY -= 50; + else if(event->key() == Qt::Key_Down) + scrollY += 50; + else if(event->key() == Qt::Key_Right) + scrollX += 50; + else if(event->key() == Qt::Key_Left) + scrollX -= 50; + else + handledKey = false; + + // Set scroll bar new value + this->verticalScrollBar()->setValue(scrollY); + this->horizontalScrollBar()->setValue(scrollX); } // Defer unhandled keys to parent. @@ -365,60 +520,7 @@ void MapperGLCanvas::keyPressEvent(QKeyEvent* event) { QWidget::keyPressEvent(event); } - -// std::cout << "Key pressed" << std::endl; -// int xMove = 0; -// int yMove = 0; -// switch (event->key()) { -// case Qt::Key_Tab: -// if (event->modifiers() & Qt::ControlModifier) -// switchImage( (Common::getCurrentSourceId() + 1) % Common::nImages()); -// else -// { -// Quad& quad = getQuad(); -// _active_vertex = (_active_vertex + 1 ) % 4; -// } -// break; -// case Qt::Key_Up: -// yMove = -1; -// break; -// case Qt::Key_Down: -// yMove = +1; -// break; -// case Qt::Key_Left: -// xMove = -1; -// break; -// case Qt::Key_Right: -// xMove = +1; -// break; -// default: -// std::cerr << "Unhandled key" << std::endl; -// QWidget::keyPressEvent(event); -// break; -// } -// -// Quad& quad = getQuad(); -// Point *p = quad.getVertex(_active_vertex); -// p->x += xMove; -// p->y += yMove; -// quad.setVertex(_active_vertex, p); -// -// update(); -// -// emit quadChanged(); } -// -//void MapperGLCanvas::paintEvent(QPaintEvent* ) -//{ -// makeCurrent(); -// -// QPainter painter(this); -// painter.setRenderHint(QPainter::Antialiasing); -// -// draw(&painter); -// -// painter.end(); -//} void MapperGLCanvas::updateCanvas() { @@ -426,31 +528,6 @@ void MapperGLCanvas::updateCanvas() scene()->update(); } -///* Stick vertex p of Shape orig to another Shape's vertex, if the 2 vertices are -// * close enough. The distance per coordinate is currently set in dist_stick -// * variable. Perhaps the sticky-sensitivity should be configurable through GUI */ -//void MapperGLCanvas::glueVertex(MShape *orig, QPointF *p) -//{ -// MappingManager manager = getMainWindow()->getMappingManager(); -// for (int i = 0; i < manager.nMappings(); i++) -// { -// MShape *shape = getShapeFromMappingId(manager.getMapping(i)->getId()); -// if (shape && shape != orig) -// { -// for (int vertex = 0; vertex < shape->nVertices(); vertex++) -// { -// const QPointF& v = shape->getVertex(vertex); -// if (distIsInside(v, *p, MM::VERTEX_STICK_RADIUS)) -// { -// p->setX(v.x()); -// p->setY(v.y()); -// } -// } -// } -// } -//} - - void MapperGLCanvas::deselectVertices() { _activeVertex = NO_VERTEX; @@ -466,38 +543,25 @@ void MapperGLCanvas::deselectAll() void MapperGLCanvas::wheelEvent(QWheelEvent *event) { + // [-120]-----[-1]|[1]++++++[120] + // See: http://doc.qt.io/qt-5/qwheelevent.html#angleDelta +#if QT_VERSION >= 0x050500 + int deltaLevel = event->angleDelta().y() / 120; +#else int deltaLevel = event->delta() / 120; - qreal zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); +#endif + if (deltaLevel > 0) { - // First check if we're already at max. - while (deltaLevel && zoomFactor < MM::ZOOM_MAX) { - _zoomLevel++; - deltaLevel--; - zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); - } - zoomFactor = qMin(zoomFactor, MM::ZOOM_MAX); + // Increase zoom level + increaseZoomLevel(deltaLevel); } else { - // First check if we're already at min. - while (deltaLevel && zoomFactor > MM::ZOOM_MIN) { - _zoomLevel--; - deltaLevel++; - zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); - } - zoomFactor = qMax(zoomFactor, MM::ZOOM_MIN); + // Decrease zoom level + decreaseZoomLevel(-deltaLevel); } - // Re-bound zoom (for consistency). - zoomFactor = getZoomFactor(); - - // Apply zoom to view. - QGraphicsView* view = scene()->views().first(); - view->resetMatrix(); - view->scale(zoomFactor, zoomFactor); - view->update(); - // Accept wheel scrolling event. event->accept(); } @@ -517,6 +581,121 @@ bool MapperGLCanvas::eventFilter(QObject *target, QEvent *event) } } +void MapperGLCanvas::increaseZoomLevel(int steps) +{ + qreal zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); + + while (steps > 0 && zoomFactor < MM::ZOOM_MAX) { + _zoomLevel++; + zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); + steps--; + } + zoomFactor = qMin(zoomFactor, MM::ZOOM_MAX); + + // Reset adaptation + _shapeIsAdapted = false; + + // Apply to view + applyZoomToView(); +} + +void MapperGLCanvas::decreaseZoomLevel(int steps) +{ + qreal zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); + + while (steps > 0 && zoomFactor > MM::ZOOM_MIN) { + _zoomLevel--; + zoomFactor = qPow(MM::ZOOM_FACTOR, _zoomLevel); + steps--; + } + zoomFactor = qMax(zoomFactor, MM::ZOOM_MIN); + + // Reset adaptation + _shapeIsAdapted = false; + + // Apply to view + applyZoomToView(); +} + +void MapperGLCanvas::resetZoomLevel() +{ + // Reset zoom level to zero + _zoomLevel = 0; + + // Reset adaptation + _shapeIsAdapted = false; + + // Apply to view + applyZoomToView(); +} + +void MapperGLCanvas::fitShapeInView() +{ + // Get first of the list of all the views + QGraphicsView* view = scene()->views().first(); + // Scales the view matrix + view->fitInView(this->scene()->itemsBoundingRect(), Qt::KeepAspectRatio); + // Get the horizontal scaling factor + _scalingFactor = view->matrix().m11(); + + // Adapt shape + _shapeIsAdapted = true; + + // Update zoom menu list + updateDropdownMenu(); +} + +void MapperGLCanvas::showZoomToolBar(bool visible) +{ + if (visible) + _zoomToolBar->show(); + else + _zoomToolBar->hide(); +} + +void MapperGLCanvas::enableZoomToolBar(bool enabled) +{ + // Enable/Disable all button + _zoomInButton->setEnabled(enabled); + _zoomOutButton->setEnabled(enabled); + _resetZoomButton->setEnabled(enabled); + _fitToViewButton->setEnabled(enabled); + _dropdownMenu->setEnabled(enabled); +} + +void MapperGLCanvas::setZoomFromMenu(const QString &text) +{ + // Get text choosen by user and convert it to double + qreal zoomFactor = text.mid(0, text.length() - 1).toDouble(); + // Set zoom factor + _scalingFactor = zoomFactor / 100; + + // Adapt shape + _shapeIsAdapted = true; + + // Apply to view + applyZoomToView(); +} + +void MapperGLCanvas::updateDropdownMenu() +{ + // Get current zoom factor percentage + QString zoomFactor = QString::number(int(getZoomFactor() * 100)).append(QChar('%')); + //Create list + QStringList zoomFactorList; + zoomFactorList << "400%" << "300%" << "200%" << "150%" << "125%" << + "100%" << "75%" << "50%" << "25%" << "12.5%"; + // Avoid duplicate + if (!zoomFactorList.contains(zoomFactor)) + zoomFactorList.append(zoomFactor); + // Clear if is not empty + _dropdownMenu->clear(); + // Add list item + _dropdownMenu->addItems(zoomFactorList); + // Select 100% by default + _dropdownMenu->setCurrentText(zoomFactor); +} + void MapperGLCanvas::_glueVertex(QPointF* p) { diff --git a/MapperGLCanvas.h b/MapperGLCanvas.h index 0124686..2738e21 100644 --- a/MapperGLCanvas.h +++ b/MapperGLCanvas.h @@ -54,8 +54,8 @@ public: /// Returns shape associated with mapping id. virtual bool isOutput() const = 0; - virtual MShape::ptr getShapeFromMappingId(uid mappingId) const = 0; - virtual QSharedPointer getShapeGraphicsItemFromMappingId(uid mappingId) const = 0; + virtual MShape::ptr getShapeFromMapping(const Mapping::ptr& mapping) const = 0; + virtual QSharedPointer getShapeGraphicsItemFromMapping(const Mapping::ptr& mapping) const = 0; MShape::ptr getCurrentShape(); QSharedPointer getCurrentShapeGraphicsItem(); @@ -86,11 +86,19 @@ public: bool shapeGrabbed() const { return _shapeGrabbed; } bool vertexGrabbed() const { return _vertexGrabbed; } - qreal getZoomFactor() const { return qBound(qPow(MM::ZOOM_FACTOR, _zoomLevel), MM::ZOOM_MIN, MM::ZOOM_MAX); } + //qreal getZoomFactor() const { return qBound(qPow(MM::ZOOM_FACTOR, _zoomLevel), MM::ZOOM_MIN, MM::ZOOM_MAX); } + qreal getZoomFactor() const { return _shapeIsAdapted + ? _scalingFactor + : qBound(MM::ZOOM_MIN, qPow(MM::ZOOM_FACTOR, _zoomLevel), MM::ZOOM_MAX); } /// This function needs to be called after a shape inside the canvas has been changed for appropriate signals to be activated. void currentShapeWasChanged(); + // Apply zoom to view + void applyZoomToView(); + // Refresh the zoom toolbar position + void updateZoomToolbar(); + protected: // void initializeGL(); // void resizeGL(int width, int height); @@ -147,12 +155,30 @@ private: // The zoom level (in number of steps). int _zoomLevel; + // The scaling factor + qreal _scalingFactor; + + bool _shapeIsAdapted; + // Pointer to MainWindow UndoStack QUndoStack *undoStack; + // Buttons for toolbox layout + QWidget* _zoomToolBar; + QPushButton* _zoomInButton; + QPushButton* _zoomOutButton; + QPushButton* _resetZoomButton; + QPushButton* _fitToViewButton; + // Dropdown menu + QComboBox* _dropdownMenu; + + // Create zoom tool buttons + void createZoomToolsLayout(); + signals: void shapeChanged(MShape*); void imageChanged(); + void shapeContextMenuRequested(const QPoint &pos); public slots: void updateCanvas(); @@ -168,6 +194,20 @@ public slots: // Event Filter bool eventFilter(QObject *target, QEvent *event); + // Zoom + void increaseZoomLevel(int steps=1); + void decreaseZoomLevel(int steps=1); + void resetZoomLevel(); + void fitShapeInView(); + + // Show/Hide zoom tool buttons + void showZoomToolBar(bool visible); + void enableZoomToolBar(bool enabled); + // Set zoom factor with drowmenu data + void setZoomFromMenu(const QString& text); + // Update and feedback zoom level + void updateDropdownMenu(); + protected: // TODO: Perhaps the sticky-sensitivity should be configurable through GUI void _glueVertex(QPointF* p); diff --git a/Mapping.h b/Mapping.h index 30f2904..618f0a3 100644 --- a/Mapping.h +++ b/Mapping.h @@ -165,6 +165,7 @@ public: virtual QString getType() const { return getShape()->getType() + "_color"; } + }; /** diff --git a/MappingManager.cpp b/MappingManager.cpp index b421f3f..e48a767 100644 --- a/MappingManager.cpp +++ b/MappingManager.cpp @@ -145,6 +145,31 @@ QVector MappingManager::getVisibleMappings() const return visible; } +/// Returns true iff the mapping is visible. +bool MappingManager::mappingIsVisible(Mapping::ptr mapping) const +{ + // Solo mappings are always visible. + if (mapping->isSolo()) + return true; + + // Non-solo invisible mappings are always invisible. + else if (!mapping->isVisible()) + return false; + + // Mapping is non-solo yet visible: check if another mapping is solo (which would thus make it invisible). + else + { + for (QVector::const_iterator it = mappingVector.begin(); it != mappingVector.end(); ++it) + { + if ((*it)->isSolo()) + return false; + } + + // Mapping is non-solo yet visible and there are no solo mappings. + return true; + } +} + void MappingManager::reorderMappings(QVector mappingIds) { // Both vector needs to have the same size. diff --git a/MappingManager.h b/MappingManager.h index 2f2a9c0..f81158f 100644 --- a/MappingManager.h +++ b/MappingManager.h @@ -100,8 +100,12 @@ public: /// Reorders the mappings according to given list of uids. QVector needs to void reorderMappings(QVector mappingIds); + /// Returns the ordered list of visible mappings, using both the "visible" and "solo" properties. QVector getVisibleMappings() const; + /// Returns true iff the mapping is visible. + bool mappingIsVisible(Mapping::ptr mapping) const; + void clearAll(); }; diff --git a/NEWS b/NEWS index 2f34c26..3cba2f3 100644 --- a/NEWS +++ b/NEWS @@ -3,7 +3,33 @@ Release notes for MapMap 2015-??-?? - MapMap 0.3.2 ------------------------- -* (none) +* Added zoom toolbar +* Added console window +* Fix #179: Paints and mappings renamings are now saved in file +* Fix #162: Zooming in the destination canvas changes the size of controls in output window. +* Fix #154: Problem with some video files: shape is size of single point +* Can rename paints and mappings via OSC +* Can rename paints and mappings with double click +* Fix #156: White rectangle around the fullscreen window +* Fix #152: The software frozen when we load a project and the video files are not found +* Abled to locate the video files if is not found when load a project +* Fix #149: Deleting a mapping actually deletes a paint when the paint tab is chosen +* Improved Test signal +* Enhancement #117: Ellipse conical projection +* Performance improvements +* OSC general bug fixes +* OSC support on OSX +* Done #174: Be able to rename a Paint +* DOne #72: Be able to name paints and mappings - for OSC controls +* Center the test signal #97 (must use all the space available) +* Fix #159: Program just freezes when importing corrupted video file +* Done #145: The user doesn't need to see the undo stack +* Done #40: Ctrl-Q should quit the application +* Done #183: Display logging output in a console and be able to turn it on and off +* Done #203: The toolbar can be shown or hidden at will by the user +* Done #147: Be able to delete a mapping with the delete key #147 (tested on Ubuntu) +* Done #184: Make sure names support UTF-8 characters +* Done #201 UX Design document: implement main toolbar improvements 2015-10-30 - MapMap 0.3.1 ------------------------- diff --git a/OscInterface.cpp b/OscInterface.cpp index a08242d..001901c 100644 --- a/OscInterface.cpp +++ b/OscInterface.cpp @@ -41,7 +41,7 @@ OscInterface::OscInterface( //if (listen_port != OSC_PORT_NONE) receiving_enabled_ = true; if (receiving_enabled_) { - std::cout << "Listening osc.udp://localhost:" << listen_port << std::endl; + QMessageLogger(__FILE__, __LINE__, 0).debug() << "Listening osc.udp://localhost:" << listen_port.c_str(); // receiver_.addHandler("/ping", "", ping_cb, this); // receiver_.addHandler("/pong", "", pong_cb, this); //receiver_.addHandler("/image/path", "ss", image_path_cb, this); @@ -60,7 +60,7 @@ int OscInterface::pong_cb(const char *path, const char * /*types*/, lo_arg ** /*argv*/, int /*argc*/, void * /*data*/, void *user_data) { OscInterface* context = static_cast(user_data); if (context->is_verbose()) - std::cout << "Got " << path << std::endl; + QMessageLogger(__FILE__, __LINE__, 0).debug() << "Got " << path; return 0; } @@ -71,7 +71,7 @@ int OscInterface::ping_cb(const char *path, const char * /*types*/, lo_arg ** /*argv*/, int /*argc*/, void * /*data*/, void *user_data) { OscInterface* context = static_cast(user_data); if (context->is_verbose()) - std::cout << "Got " << path << std::endl; + QMessageLogger(__FILE__, __LINE__, 0).debug() << "Got " << path; return 0; } diff --git a/OscInterface.h b/OscInterface.h index dfdc121..61415aa 100644 --- a/OscInterface.h +++ b/OscInterface.h @@ -27,8 +27,9 @@ #ifdef HAVE_OSC #include +#include -#include "concurrentqueue.h" +#include "ConcurrentQueue.h" #include "OscReceiver.h" class MainWindow; diff --git a/OutputGLCanvas.cpp b/OutputGLCanvas.cpp index d03fd50..8bd8236 100644 --- a/OutputGLCanvas.cpp +++ b/OutputGLCanvas.cpp @@ -30,6 +30,7 @@ OutputGLCanvas::OutputGLCanvas(MainWindow* mainWindow, QWidget* parent, const QG // Disable scrollbars. setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + showZoomToolBar(false); } void OutputGLCanvas::drawForeground(QPainter *painter , const QRectF &rect) diff --git a/OutputGLWindow.cpp b/OutputGLWindow.cpp index 7866224..7681983 100644 --- a/OutputGLWindow.cpp +++ b/OutputGLWindow.cpp @@ -36,9 +36,6 @@ OutputGLWindow:: OutputGLWindow(QWidget* parent, const DestinationGLCanvas* canv layout->addWidget(canvas); setLayout(layout); - // Save window geometry. - _geometry = saveGeometry(); - _pointerIsVisible = true; } @@ -87,61 +84,46 @@ void OutputGLWindow::closeEvent(QCloseEvent *event) void OutputGLWindow::setFullScreen(bool fullscreen) { - setCursorVisible(!fullscreen); - emit fullScreenToggled(fullscreen); - // Activate crosshair in fullscreen mode. - // should be only drawn if the controls should be shown - canvas->setDisplayCrosshair(fullscreen && canvas->getMainWindow()->displayControls()); - - // NOTE: The showFullScreen() method does not work well under Ubuntu Linux. The code below fixes the issue. - // Notice that there might be problems with the fullscreen in other OS / window managers. If so, please add - // the code to fix those issues here. - // See: http://qt-project.org/doc/qt-4.8/qwidget.html#showFullScreen - // Source: http://stackoverflow.com/questions/12645880/fullscreen-for-qdialog-from-within-mainwindow-only-working-sometimes -#ifdef Q_OS_UNIX - const QString session = QString(getenv("DESKTOP_SESSION")).toLower(); -#endif if (fullscreen) { - // Save window geometry. - _geometry = saveGeometry(); - //qDebug() << "Saving Geometry " << _geometry.toHex() << endl; - - // Move window to second screen before fullscreening it. + // Check if user is on multiple screen if (QApplication::desktop()->screenCount() > 1) + { + // Hide cursor + setCursorVisible(!fullscreen); + // Activate crosshair in fullscreen mode. + // should be only drawn if the controls should be shown + canvas->setDisplayCrosshair(fullscreen && canvas->getMainWindow()->displayControls()); + //Move window to second screen before fullscreening it. setGeometry(QApplication::desktop()->screenGeometry(1)); - -#ifdef Q_OS_UNIX - // Special case for Unity. - if (session == "ubuntu" || session == "cinnamon" || session == "default") { + //The problem related to the full screen on linux seems to be resolved + // with Qt 5.5 at least on Debian but define macro anyway +#ifdef Q_OS_LINUX setWindowFlags(Qt::Window); setVisible(true); setWindowState( windowState() ^ Qt::WindowFullScreen ); show(); - } else { - showFullScreen(); - } #else showFullScreen(); #endif + } + else + { + show(); + } } else { - // Restore geometry of window to what it was before full screen call. - //restoreGeometry(_geometry); - -#ifdef Q_OS_UNIX - // Special case for Unity. - if (session == "ubuntu") { - showNormal(); - } else { - restoreGeometry(_geometry); - showNormal(); + if (QApplication::desktop()->screenCount() > 1) { +#ifdef Q_OS_LINUX setWindowFlags( windowFlags() & ~Qt::Window ); - } #else - restoreGeometry(_geometry); showNormal(); #endif + } + else + { + hide(); + } } } diff --git a/OutputGLWindow.h b/OutputGLWindow.h index 4d078de..50dd624 100644 --- a/OutputGLWindow.h +++ b/OutputGLWindow.h @@ -50,7 +50,6 @@ protected: signals: void closed(); - void fullScreenToggled(bool fullScreen); public: DestinationGLCanvas* getCanvas() const { return canvas; } @@ -59,7 +58,6 @@ public: private: OutputGLCanvas* canvas; - QByteArray _geometry; bool _pointerIsVisible; }; diff --git a/README b/README index 5b7e56b..e3e1b3f 100644 --- a/README +++ b/README @@ -39,13 +39,14 @@ or else the menu will not show. Authors ------- * Sofian Audry: lead developer, user interface designer, project manager. +* Dame Diongue: developer. * Alexandre Quessy: release manager, developer, technical writer, project manager. * Mike Latona: user interface designer. -* Dame Diongue: developer. * Vasilis Liaskovitis: developer. Contributors ------------ +* Alex Barry: user experience design. * Maxime Damecour: inspiration. * Christian Ambaud: sponsor, inspiration. * Louis Desjardins: project manager. @@ -56,4 +57,3 @@ Contributors More info --------- Get more info from http://mapmap.info - diff --git a/Shape.cpp b/Shape.cpp index 2947ffd..23d3647 100644 --- a/Shape.cpp +++ b/Shape.cpp @@ -19,7 +19,7 @@ #include "Shape.h" -MShape::MShape(const QVector& vertices_) { +MShape::MShape(const QVector& vertices_) : _isLocked(false) { setVertices(vertices_); build(); } diff --git a/Shape.h b/Shape.h index 889c1a3..4c42528 100644 --- a/Shape.h +++ b/Shape.h @@ -48,11 +48,12 @@ class MShape : public Serializable { Q_OBJECT + Q_PROPERTY(bool locked READ isLocked WRITE setLocked) Q_PROPERTY(QVector vertices READ getVertices WRITE setVertices STORED false) public: typedef QSharedPointer ptr; - MShape() {} + MShape() : _isLocked(false) {} MShape(const QVector& vertices_); virtual ~MShape() {} @@ -98,6 +99,10 @@ public: virtual MShape* clone() const; + bool isLocked() const { return _isLocked; } + void setLocked(bool locked) { _isLocked = locked; } + void toggleLocked() { _isLocked = !_isLocked; } + const QVector& getVertices() const { return vertices; } virtual void setVertices(const QVector& vertices_) { @@ -111,6 +116,7 @@ public: protected: QVector vertices; + bool _isLocked; void _addVertex(const QPointF& vertex) { diff --git a/ShapeControlPainter.cpp b/ShapeControlPainter.cpp index e07df4b..6c50f41 100644 --- a/ShapeControlPainter.cpp +++ b/ShapeControlPainter.cpp @@ -36,16 +36,17 @@ void ShapeControlPainter::paint(QPainter *painter, MapperGLCanvas* canvas, const void ShapeControlPainter::_paintVertices(QPainter *painter, MapperGLCanvas* canvas, const QList& selectedVertices) { qreal zoomFactor = canvas->getZoomFactor(); - qreal selectRadius = MM::VERTEX_SELECT_RADIUS / zoomFactor; + qreal selectRadius = (getShape()->isLocked() ? MM::VERTEX_LOCKED_RADIUS : MM::VERTEX_SELECT_RADIUS) / zoomFactor; qreal strokeWidth = MM::VERTEX_SELECT_STROKE_WIDTH / zoomFactor; for (int i=0; inVertices(); i++) - Util::drawControlsVertex(painter, getShape()->getVertex(i), selectedVertices.contains(i), selectRadius, strokeWidth); + Util::drawControlsVertex(painter, getShape()->getVertex(i), selectedVertices.contains(i), getShape()->isLocked(), selectRadius, strokeWidth); } QPen ShapeControlPainter::getRescaledShapeStroke(MapperGLCanvas* canvas, bool innerStroke) { - return QPen(QBrush(MM::CONTROL_COLOR), (innerStroke ? MM::SHAPE_INNER_STROKE_WIDTH : MM::SHAPE_STROKE_WIDTH) / canvas->getZoomFactor()); + return QPen(getShape()->isLocked() ? QBrush(MM::CONTROL_LOCKED_COLOR) : QBrush(MM::CONTROL_COLOR), + (innerStroke ? MM::SHAPE_INNER_STROKE_WIDTH : MM::SHAPE_STROKE_WIDTH) / canvas->getZoomFactor()); } void PolygonControlPainter::_paintShape(QPainter *painter, MapperGLCanvas* canvas) diff --git a/ShapeGraphicsItem.cpp b/ShapeGraphicsItem.cpp index be8faae..605b6d2 100644 --- a/ShapeGraphicsItem.cpp +++ b/ShapeGraphicsItem.cpp @@ -38,6 +38,10 @@ bool ShapeGraphicsItem::isMappingCurrent() const { return MainWindow::instance()->getCurrentMappingId() == getMapping()->getId(); } +bool ShapeGraphicsItem::isMappingVisible() const { + return MainWindow::instance()->getMappingManager().mappingIsVisible(getMapping()); +} + void ShapeGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { diff --git a/ShapeGraphicsItem.h b/ShapeGraphicsItem.h index 9d07afc..4c0ce53 100644 --- a/ShapeGraphicsItem.h +++ b/ShapeGraphicsItem.h @@ -76,7 +76,7 @@ public: bool isMappingCurrent() const; /// Returns whether the mapping this shape is associated should be visible. - bool isMappingVisible() const { return getMapping()->isVisible(); } + bool isMappingVisible() const; /// Returns the bounding rectangle of this item. virtual QRectF boundingRect() const { return shape().boundingRect(); } diff --git a/SourceGLCanvas.cpp b/SourceGLCanvas.cpp index cfddc72..3f244a6 100644 --- a/SourceGLCanvas.cpp +++ b/SourceGLCanvas.cpp @@ -28,28 +28,14 @@ SourceGLCanvas::SourceGLCanvas(MainWindow* mainWindow, QWidget* parent) { } -MShape::ptr SourceGLCanvas::getShapeFromMappingId(uid mappingId) const +MShape::ptr SourceGLCanvas::getShapeFromMapping(const Mapping::ptr& mapping) const { - if (mappingId == NULL_UID) - return MShape::ptr(); - - else - { - Mapping::ptr mapping = getMainWindow()->getMappingManager().getMappingById(mappingId); - Q_CHECK_PTR(mapping); - return mapping->getInputShape(); - } + return (mapping.isNull() ? MShape::ptr() : mapping->getInputShape()); } -QSharedPointer SourceGLCanvas::getShapeGraphicsItemFromMappingId(uid mappingId) const +QSharedPointer SourceGLCanvas::getShapeGraphicsItemFromMapping(const Mapping::ptr& mapping) const { - if (mappingId == NULL_UID) - return QSharedPointer(); - - else - { - return MainWindow::instance()->getMappingGuiByMappingId(mappingId)->getInputGraphicsItem(); - } + return (mapping.isNull() ? QSharedPointer() : MainWindow::instance()->getMappingGuiByMappingId(mapping->getId())->getInputGraphicsItem()); } // diff --git a/SourceGLCanvas.h b/SourceGLCanvas.h index c0f0119..77a2cf0 100644 --- a/SourceGLCanvas.h +++ b/SourceGLCanvas.h @@ -36,8 +36,8 @@ public: virtual ~SourceGLCanvas() {} virtual bool isOutput() const { return false; } - virtual MShape::ptr getShapeFromMappingId(uid mappingId) const; - virtual QSharedPointer getShapeGraphicsItemFromMappingId(uid mappingId) const; + virtual MShape::ptr getShapeFromMapping(const Mapping::ptr& mapping) const; + virtual QSharedPointer getShapeGraphicsItemFromMapping(const Mapping::ptr& mapping) const; private: // virtual void doDraw(QPainter* painter); diff --git a/Util.cpp b/Util.cpp index 2e9f794..85d204a 100644 --- a/Util.cpp +++ b/Util.cpp @@ -150,11 +150,15 @@ Ellipse* createEllipseForColor(int frameWidth, int frameHeight) ); } -void drawControlsVertex(QPainter* painter, const QPointF& vertex, bool selected, qreal radius, qreal strokeWidth) +void drawControlsVertex(QPainter* painter, const QPointF& vertex, bool selected, bool locked, qreal radius, qreal strokeWidth) { // Init colors and stroke. - painter->setBrush(selected ? MM::VERTEX_SELECTED_BACKGROUND : MM::VERTEX_BACKGROUND); - painter->setPen(QPen(MM::CONTROL_COLOR, strokeWidth)); + if (locked) + painter->setBrush(MM::VERTEX_LOCKED_BACKGROUND); + else + painter->setBrush(selected ? MM::VERTEX_SELECTED_BACKGROUND : MM::VERTEX_BACKGROUND); + + painter->setPen(locked ? QPen(MM::CONTROL_LOCKED_COLOR) : QPen(MM::CONTROL_COLOR, strokeWidth)); // Draw ellipse. painter->drawEllipse(vertex, radius, radius); @@ -169,10 +173,10 @@ void drawControlsVertices(QPainter* painter, const QList* selectedVertices, { if (!selectedVertices) for (int i=0; icontains(i)); + drawControlsVertex(painter, shape.getVertex(i), selectedVertices->contains(i), shape.isLocked()); } void drawControlsEllipse(QPainter* painter, const QList* selectedVertices, const Ellipse& ellipse) @@ -276,7 +280,7 @@ bool eraseSettings() } else { - std::cout << "Erase MapMap settings." << std::endl; + QMessageLogger(__FILE__, __LINE__, 0).debug() << "Erase MapMap settings."; settingsFile.close(); return settingsFile.remove(); } diff --git a/Util.h b/Util.h index 4af1675..da6b8a2 100644 --- a/Util.h +++ b/Util.h @@ -58,7 +58,7 @@ Quad* createQuadForColor(int frameWidth, int frameHeight); Triangle* createTriangleForColor(int frameWidth, int frameHeight); Ellipse* createEllipseForColor(int frameWidth, int frameHeight); -void drawControlsVertex(QPainter* painter, const QPointF& vertex, bool selected, qreal radius = MM::VERTEX_SELECT_RADIUS, qreal strokeWidth = MM::VERTEX_SELECT_STROKE_WIDTH); +void drawControlsVertex(QPainter* painter, const QPointF& vertex, bool selected, bool locked, qreal radius = MM::VERTEX_SELECT_RADIUS, qreal strokeWidth = MM::VERTEX_SELECT_STROKE_WIDTH); void drawControlsVertices(QPainter* painter, const QList* selectedVertices, const MShape& shape); void drawControlsEllipse(QPainter* painter, const QList* selectedVertices, const Ellipse& ellipse); diff --git a/main.cpp b/main.cpp index 6c653f5..d3e91b1 100644 --- a/main.cpp +++ b/main.cpp @@ -65,8 +65,17 @@ void initRegistry() registry.add(); } +// Intercept all logging message and display it in the console +void logMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + ConsoleWindow::getInstance()->messageLog(type, context, msg); +} + int main(int argc, char *argv[]) { + // Install message handler + qInstallMessageHandler(logMessageHandler); + set_env_vars_if_needed(); // Initialize meta-object registry. @@ -141,10 +150,10 @@ int main(int argc, char *argv[]) // Create window. MainWindow* win = MainWindow::instance(); - - QFontDatabase db; - Q_ASSERT( QFontDatabase::addApplicationFont(":/base-font") != -1); - app.setFont(QFont(":/base-font", 10, QFont::Bold)); + // Add custom font + int id = QFontDatabase::addApplicationFont(":/base-font"); + QString family = QFontDatabase::applicationFontFamilies(id).at(0); + app.setFont(QFont(family, 11, QFont::Normal)); // Load stylesheet. QFile stylesheet(":/stylesheet"); diff --git a/mapmap.pro b/mapmap.pro index db3ba5b..a040b7f 100644 --- a/mapmap.pro +++ b/mapmap.pro @@ -3,11 +3,13 @@ TEMPLATE = app VERSION = 0.3.2 TARGET = mapmap QT += gui opengl xml -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets core DEFINES += UNICODE QT_THREAD_SUPPORT QT_CORE_LIB QT_GUI_LIB HEADERS = \ Commands.h \ + ConcurrentQueue.h \ + ConsoleWindow.h \ DestinationGLCanvas.h \ Element.h \ Ellipse.h \ @@ -46,6 +48,7 @@ HEADERS = \ SOURCES = \ Commands.cpp \ + ConsoleWindow.cpp \ DestinationGLCanvas.cpp \ Element.cpp \ Ellipse.cpp \ diff --git a/mapmap.qrc b/mapmap.qrc index 561c045..80914a8 100644 --- a/mapmap.qrc +++ b/mapmap.qrc @@ -1,53 +1,36 @@ - - resources/images/icons/newdoc_w.png - resources/images/icons/open_w.png - resources/images/icons/save_w.png - resources/images/icons/save_w.png - - resources/images/icons/fullscreen_w.png - resources/images/icons/nofullscreen_w.png - resources/images/icons/ctrl2_w.png - - resources/images/icons/add_movie_w.png - resources/images/icons/add_img_w.png - resources/images/icons/add_paint_w.png - - resources/images/shapes/add_quad.png - resources/images/shapes/add_triangle.png - resources/images/shapes/add_circle.png - - - - resources/images/icons/play_w.png - resources/images/icons/pause_w.png - resources/images/icons/rewind_w.png - - resources/images/shapes/add_quad.png - resources/images/shapes/add_triangle.png - resources/images/shapes/add_circle.png - - resources/images/shapes/add_circle.png - - resources/images/logo/logomapmap.png - resources/images/logo/logo_m_big_mapmap.png - resources/images/logo/splash.png - - resources/fonts/HelveticaNeueLTPro-Bd.otf - - resources/fonts/HelveticaNeueLTPro-Bd.otf - - resources/images/test-signal/test-signal.svg - - resources/qss/mapmap.qss - + + resources/images/icons/newdoc_w.png + resources/images/icons/open_w.png + resources/images/icons/save_w.png + resources/images/icons/save_w.png + resources/images/icons/fullscreen_w.png + resources/images/icons/nofullscreen_w.png + resources/images/icons/ctrl2_w.png + resources/images/icons/add_movie_w.png + resources/images/icons/add_img_w.png + resources/images/icons/add_paint_w.png + resources/images/shapes/add_quad.png + resources/images/shapes/add_triangle.png + resources/images/shapes/add_circle.png + resources/images/icons/play_w.png + resources/images/icons/pause_w.png + resources/images/icons/rewind_w.png + resources/images/shapes/add_quad.png + resources/images/shapes/add_triangle.png + resources/images/shapes/add_circle.png + resources/images/shapes/add_circle.png + resources/images/logo/logomapmap.png + resources/images/logo/logo_m_big_mapmap.png + resources/images/logo/splash.png + resources/fonts/DroidSans.otf + resources/fonts/DroidSans.otf + resources/images/test-signal/test-signal.svg + resources/qss/mapmap.qss + resources/images/icons/zoom_reset_w.png + resources/images/icons/zoom_in_w.png + resources/images/icons/zoom_out_w.png + resources/images/icons/zoom_fit_w.png + resources/fonts/Hack-Regular.otf + - diff --git a/resources/fonts/DroidSans.otf b/resources/fonts/DroidSans.otf new file mode 100644 index 0000000..ad1efca Binary files /dev/null and b/resources/fonts/DroidSans.otf differ diff --git a/resources/fonts/Hack-Regular.otf b/resources/fonts/Hack-Regular.otf new file mode 100644 index 0000000..5f08325 Binary files /dev/null and b/resources/fonts/Hack-Regular.otf differ diff --git a/resources/fonts/HelveticaNeueLTPro-Bd.otf b/resources/fonts/HelveticaNeueLTPro-Bd.otf deleted file mode 100644 index 0712e4d..0000000 Binary files a/resources/fonts/HelveticaNeueLTPro-Bd.otf and /dev/null differ diff --git a/resources/images/icons/zoom_fit.png b/resources/images/icons/zoom_fit.png new file mode 100644 index 0000000..65e6d8e Binary files /dev/null and b/resources/images/icons/zoom_fit.png differ diff --git a/resources/images/icons/zoom_fit_w.png b/resources/images/icons/zoom_fit_w.png new file mode 100644 index 0000000..55248a2 Binary files /dev/null and b/resources/images/icons/zoom_fit_w.png differ diff --git a/resources/images/icons/zoom_in.png b/resources/images/icons/zoom_in.png new file mode 100644 index 0000000..a42c5ec Binary files /dev/null and b/resources/images/icons/zoom_in.png differ diff --git a/resources/images/icons/zoom_in_w.png b/resources/images/icons/zoom_in_w.png new file mode 100644 index 0000000..0148bd9 Binary files /dev/null and b/resources/images/icons/zoom_in_w.png differ diff --git a/resources/images/icons/zoom_out.png b/resources/images/icons/zoom_out.png new file mode 100644 index 0000000..1b446cb Binary files /dev/null and b/resources/images/icons/zoom_out.png differ diff --git a/resources/images/icons/zoom_out_w.png b/resources/images/icons/zoom_out_w.png new file mode 100644 index 0000000..f41233e Binary files /dev/null and b/resources/images/icons/zoom_out_w.png differ diff --git a/resources/images/icons/zoom_reset.png b/resources/images/icons/zoom_reset.png new file mode 100644 index 0000000..ec5a42b Binary files /dev/null and b/resources/images/icons/zoom_reset.png differ diff --git a/resources/images/icons/zoom_reset_w.png b/resources/images/icons/zoom_reset_w.png new file mode 100644 index 0000000..d1f708c Binary files /dev/null and b/resources/images/icons/zoom_reset_w.png differ diff --git a/resources/qss/mapmap.qss b/resources/qss/mapmap.qss index 2bcb487..dbe2406 100644 --- a/resources/qss/mapmap.qss +++ b/resources/qss/mapmap.qss @@ -54,10 +54,43 @@ QListView::item:selected { background: #272a36; } -QGraphicsView { +QGraphicsView, +QPlainTextEdit { border-style: none; } +QPushButton#zoom-in, +QPushButton#zoom-out, +QPushButton#reset-zoom, +QPushButton#zoom-fit { + background-color: rgba(0, 0, 0, 0%); +} + +QPushButton:hover#zoom-in, QPushButton:checked#zoom-in, +QPushButton:hover#zoom-out, QPushButton:checked#zoom-out, +QPushButton:hover#reset-zoom, QPushButton:checked#reset-zoom, +QPushButton:hover#zoom-fit, QPushButton:checked#zoom-fit { + background-color: qlineargradient(x1: 0, y1: 0, x2: 1, y2: 1, + stop: 0 #0c0927, stop: 1 #191C28); + border-radius: 2px; + border: none; +} + +QPushButton:pressed#zoom-in, +QPushButton:pressed#zoom-out, +QPushButton:pressed#reset-zoom, +QPushButton:pressed#zoom-fit { + border: none; +} + +QWidget#zoom-toolbox { + background-color: #272a36; +} + +QStatusBar::item { + border: 0px solid black; +} + /* QListView::item:selected:!active { background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,