1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "MainWindow.h"
18 
19 #include "Global.h"
20 
21 #include "Scene.h"
22 #include "View3D.h"
23 #include "View.h"
24 #include "MultiView.h"
25 #include "Timeline.h"
26 #include "DevSettings.h"
27 #include "ObjectPropertiesWidget.h"
28 #include "AnimatedCycleWidget.h"
29 #include "EditCanvasSizeDialog.h"
30 #include "ExportPngDialog.h"
31 #include "AboutDialog.h"
32 #include "SelectionInfoWidget.h"
33 #include "Background/BackgroundWidget.h"
34 #include "VectorAnimationComplex/VAC.h"
35 #include "VectorAnimationComplex/InbetweenFace.h"
36 #include "LayersWidget.h"
37 #include "Layer.h"
38 #include "SvgParser.h"
39 #include "SvgImportDialog.h"
40 
41 #include "IO/FileVersionConverter.h"
42 #include "XmlStreamWriter.h"
43 #include "XmlStreamReader.h"
44 #include "SaveAndLoad.h"
45 
46 #include <QCoreApplication>
47 #include <QApplication>
48 #include <QtDebug>
49 #include <QStatusBar>
50 #include <QFileDialog>
51 #include <QMessageBox>
52 #include <QMenu>
53 #include <QMenuBar>
54 #include <QToolBar>
55 #include <QScrollArea>
56 #include <QDockWidget>
57 #include <QSettings>
58 #include <QStandardPaths>
59 #include <QProgressDialog>
60 #include <QDesktopServices>
61 #include <QShortcut>
62 
63 
64 /*********************************************************************
65  *                             Constructor
66  */
67 
68 
MainWindow()69 MainWindow::MainWindow() :
70     scene_(0),
71     multiView_(0),
72 
73     aboutDialog_(0),
74 
75     gettingStarted_(0),
76     userManual_(0),
77 
78     undoStack_(),
79     undoIndex_(-1),
80     savedUndoIndex_(-1),
81 
82     fileHeader_("---------- Vec File ----------"),
83     documentFilePath_(),
84     autosaveFilename_("0.vec"),
85     autosaveTimer_(),
86     autosaveIndex_(0),
87     autosaveOn_(true),
88     autosaveDir_(),
89 
90     clipboard_(0),
91 
92     view3D_(0),
93     timeline_(0),
94     selectionInfo_(0),
95     exportPngDialog_(0),
96     editCanvasSizeDialog_(0),
97     exportingPng_(false)
98 {
99     // Global object
100     Global::initialize(this);
101 
102     // Preferences
103     global()->readSettings();
104     new DevSettings();
105 
106     // Scene
107     scene_ = Scene::createDefaultScene();
108 
109     // Timeline (must exist before multiview is created, so that newly created views can register to timeline)
110     timeline_ = new Timeline(scene_, this);
111     connect(timeline_, SIGNAL(timeChanged()),
112             this, SLOT(updatePicking())); // maybe should avoid that when playing the animation
113     connect(timeline_, SIGNAL(timeChanged()),
114             this, SLOT(update())); // should be call in same order
115     connect(scene(), SIGNAL(changed()),
116             timeline_, SLOT(update()));
117     connect(scene(),SIGNAL(selectionChanged()),timeline_,SLOT(update()));
118 
119     // 2D Views
120     multiView_ = new MultiView(scene_, this);
121     connect(multiView_, SIGNAL(allViewsNeedToUpdate()), timeline_,SLOT(update()));
122     connect(multiView_, SIGNAL(allViewsNeedToUpdate()), this, SLOT(update()));
123     connect(multiView_, SIGNAL(allViewsNeedToUpdatePicking()), this, SLOT(updatePicking()));
124     setCentralWidget(multiView_); // views are drawn
125     connect(multiView_, SIGNAL(activeViewChanged()), this, SLOT(updateViewMenu()));
126     connect(multiView_, SIGNAL(activeViewChanged()), timeline_, SLOT(update()));
127 
128     connect(multiView_, SIGNAL(settingsChanged()), this, SLOT(updateViewMenu()));
129 
130     // 3D View
131     view3D_ = new View3D(scene_, nullptr);
132     view3D_->setParent(this, Qt::Window);
133     view3DSettingsWidget_ = new View3DSettingsWidget();
134     view3DSettingsWidget_->setParent(this, Qt::Window);
135     view3DSettingsWidget_->setViewSettings(view3D_->settings());
136     connect(view3DSettingsWidget_, SIGNAL(changed()), view3D_, SLOT(update()));
137 
138     //view3D_->show();
139     connect(view3D_, SIGNAL(allViewsNeedToUpdate()), this, SLOT(update()));
140     connect(view3D_, SIGNAL(allViewsNeedToUpdatePicking()), this, SLOT(updatePicking()));
141     connect(multiView_, SIGNAL(activeViewChanged()), view3D_, SLOT(update()));
142     connect(multiView_, SIGNAL(cameraChanged()), view3D_, SLOT(update()));
143 
144     // Selection Info
145     selectionInfo_ = new SelectionInfoWidget(0);
146     connect(scene(),SIGNAL(selectionChanged()),selectionInfo_,SLOT(updateInfo()));
147     //selectionInfo_->show();
148 
149     // Background Widget
150     backgroundWidget = new BackgroundWidget();
151     if (scene() && scene()->activeLayer())
152     {
153         backgroundWidget->setBackground(scene()->activeLayer()->background());
154     }
155     connect(scene(), SIGNAL(layerAttributesChanged()), this, SLOT(onSceneLayerAttributesChanged_()));
156 
157     // redraw when the scene changes
158     connect(scene_, SIGNAL(needUpdatePicking()),
159             this, SLOT(updatePicking()));
160     connect(scene_, SIGNAL(changed()),
161           this, SLOT(update()));
162 
163     // redraw when the settings change
164     connect(DevSettings::instance(), SIGNAL(changed()),
165           this, SLOT(updatePicking())); // hopefully this doesn't occur very often
166     connect(DevSettings::instance(), SIGNAL(changed()),
167             this, SLOT(update()));
168 
169     // initializations
170     createActions();
171     createDocks();
172     createStatusBar();
173     createToolbars();
174     createMenus();
175 
176     // handle undo/redo
177     resetUndoStack_();
178     connect(scene_, SIGNAL(checkpoint()), this, SLOT(addToUndoStack()));
179 
180     // Window icon
181     QGuiApplication::setWindowIcon(QIcon(":/images/icon-256.png"));
182 
183     // Help
184     gettingStarted_ = new QTextBrowser(this);
185     gettingStarted_->setWindowFlags(Qt::Window);
186     //gettingStarted_->setSource(QUrl("help/getting-started.htm"));
187     gettingStarted_->setSearchPaths(QStringList("help/"));
188     gettingStarted_->setMinimumSize(800,500);
189 
190     userManual_ = new QTextBrowser(this);
191     userManual_->setWindowFlags(Qt::Window);
192     //userManual_->setSource(QUrl("help/user-manual.htm"));
193     userManual_->setSearchPaths(QStringList("help/"));
194     userManual_->setMinimumSize(800,500);
195 
196     // Remove context menu on rightclick
197     setContextMenuPolicy(Qt::NoContextMenu);
198 
199     // Set initial focus
200     multiView_->setFocus(Qt::OtherFocusReason);
201 
202     // Autosave
203     autosaveBegin();
204 }
205 
updateObjectProperties()206 void MainWindow::updateObjectProperties()
207 {
208     VectorAnimationComplex::CellSet selectedCells;
209     VectorAnimationComplex::VAC * vac = scene()->activeVAC();
210     if (vac)
211     {
212         selectedCells = vac->selectedCells();
213     }
214     inspector->setObjects(selectedCells);
215 }
216 
activeView() const217 View * MainWindow::activeView() const
218 {
219     return multiView_->activeView();
220 }
221 
hoveredView() const222 View * MainWindow::hoveredView() const
223 {
224     return multiView_->hoveredView();
225 }
226 
timeline() const227 Timeline * MainWindow::timeline() const
228 {
229     return timeline_;
230 }
231 
isShowCanvasChecked() const232 bool MainWindow::isShowCanvasChecked() const
233 {
234     return actionShowCanvas->isChecked();
235 
236 }
autosave()237 void MainWindow::autosave()
238 {
239     save_(autosaveDir_.absoluteFilePath(autosaveFilename_));
240 }
241 
autosaveBegin()242 void MainWindow::autosaveBegin()
243 {
244     bool success = true;
245 
246     QString dataPath = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
247     QDir().mkpath(dataPath);
248     QDir dataDir(dataPath);
249     if(!dataDir.exists())
250         success = false;
251 
252     if(success)
253     {
254         if(!dataDir.exists("autosave"))
255         {
256             dataDir.mkdir("autosave");
257         }
258         dataDir.cd("autosave");
259         autosaveDir_ = dataDir;
260         if(!autosaveDir_.exists())
261         {
262             success = false;
263         }
264         else
265         {
266             QStringList nameFilters;
267             nameFilters << "*.vec";
268             autosaveDir_.setNameFilters(nameFilters);
269             QFileInfoList fileInfoList = autosaveDir_.entryInfoList(QDir::Files, QDir::Name);
270             if(fileInfoList.isEmpty())
271             {
272                 autosaveIndex_ = 0;
273             }
274             else
275             {
276                 QString filename = fileInfoList.last().fileName();
277                 QStringList splitted = filename.split('.');
278                 if(splitted.size() < 2)
279                 {
280                     qDebug() << "Warning: autosaved file matching *.vec has been found, but failed to be split into %1.vec";
281                     autosaveIndex_ = 0;
282                 }
283                 else
284                 {
285                     int lastIndex = splitted.first().toInt();
286                     autosaveIndex_ = lastIndex + 1;
287                 }
288             }
289             autosaveFilename_.setNum(autosaveIndex_);
290             autosaveFilename_ += QString(".vec");
291             while(autosaveDir_.exists(autosaveFilename_))
292             {
293                 autosaveIndex_++;
294                 autosaveFilename_.setNum(autosaveIndex_);
295                 autosaveFilename_ += QString(".vec");
296             }
297         }
298     }
299 
300     if(success)
301     {
302         autosaveOn_ = true;
303         autosaveTimer_.setInterval(60000); // every minute
304         connect(&autosaveTimer_,SIGNAL(timeout()), this, SLOT(autosave()));
305         autosaveTimer_.start();
306     }
307     else
308     {
309         autosaveOn_ = false;
310     }
311 
312     /*
313     QSettings settings(companyName_, appName_);
314     bool hasCrashed = settings.value("has-crashed",false).toBool();
315     if(hasCrashed)
316     {
317         QMessageBox::StandardButton reply;
318         reply = QMessageBox::question(this,
319                                       "Crash",
320                                       "Oops... it seems that VPaint has crashed during its previous execution :-(\n "
321                                       "We are terribly sorry and will do our best to fix the issue in future releases.\n\n"
322                                       "Fortunately, VPaint has saved your work before crashing. Do you want to open it?\n\n"
323                                       "Note: this message also appears if another instance of VPaint is running. In this case, press \"No\".",
324                                         QMessageBox::Yes|QMessageBox::No);
325         if (reply == QMessageBox::Yes)
326         {
327             doOpen(autosaveFilename_);
328         }
329     }
330     settings.setValue("has-crashed",true);
331     autosaveTimer_.setInterval(1000);
332     connect(&autosaveTimer_,SIGNAL(timeout()), this, SLOT(autosave()));
333     autosaveTimer_.start();
334     */
335 
336 }
337 
autosaveEnd()338 void MainWindow::autosaveEnd()
339 {
340     if(autosaveOn_)
341     {
342         autosaveDir_.remove(autosaveFilename_);
343     }
344 }
345 
~MainWindow()346 MainWindow::~MainWindow()
347 {
348     clearUndoStack_();
349     autosaveEnd();
350 }
351 
scene() const352 Scene * MainWindow::scene() const
353 {
354     return scene_;
355 }
356 
357 
addToUndoStack()358 void MainWindow::addToUndoStack()
359 {
360     undoIndex_++;
361     for(int j=undoStack_.size()-1; j>=undoIndex_; j--)
362     {
363         delete undoStack_[j].second;
364         undoStack_.removeLast();
365     }
366     undoStack_ << qMakePair(global()->documentDir(), new Scene());
367     undoStack_[undoIndex_].second->copyFrom(scene_);
368 
369     // Update window title
370     updateWindowTitle_();
371 }
372 
clearUndoStack_()373 void MainWindow::clearUndoStack_()
374 {
375     foreach(UndoItem p, undoStack_)
376         delete p.second;
377 
378     undoStack_.clear();
379     undoIndex_ = -1;
380 }
381 
resetUndoStack_()382 void MainWindow::resetUndoStack_()
383 {
384     clearUndoStack_();
385     addToUndoStack();
386     setUnmodified_();
387 }
388 
goToUndoIndex_(int undoIndex)389 void MainWindow::goToUndoIndex_(int undoIndex)
390 {
391     // Set new undo index
392     undoIndex_ = undoIndex;
393 
394     // Remap relative paths in history
395     if (undoStack_[undoIndex].first != global()->documentDir())
396     {
397         undoStack_[undoIndex].second->relativeRemap(
398             undoStack_[undoIndex_].first,
399             global()->documentDir());
400         undoStack_[undoIndex].first = global()->documentDir();
401     }
402 
403     // Set scene data from undo history
404     scene_->copyFrom(undoStack_[undoIndex].second);
405 
406     // Update window title
407     updateWindowTitle_();
408 }
409 
undo()410 void MainWindow::undo()
411 {
412     if(undoIndex_>0)
413     {
414         goToUndoIndex_(undoIndex_ - 1);
415     }
416     else
417     {
418         statusBar()->showMessage(tr("Nothing to undo"));
419     }
420 }
421 
redo()422 void MainWindow::redo()
423 {
424     if(undoIndex_<undoStack_.size()-1)
425     {
426         goToUndoIndex_(undoIndex_ + 1);
427     }
428     else
429     {
430         statusBar()->showMessage(tr("Nothing to redo"));
431     }
432 }
433 
cut()434 void MainWindow::cut()
435 {
436     scene_->cut(clipboard_);
437 }
438 
copy()439 void MainWindow::copy()
440 {
441     scene_->copy(clipboard_);
442 }
443 
paste()444 void MainWindow::paste()
445 {
446     scene_->paste(clipboard_);
447 }
448 
motionPaste()449 void MainWindow::motionPaste()
450 {
451     scene_->motionPaste(clipboard_);
452 }
453 
editAnimatedCycle(VectorAnimationComplex::InbetweenFace * inbetweenFace,int indexCycle)454 void MainWindow::editAnimatedCycle(VectorAnimationComplex::InbetweenFace * inbetweenFace, int indexCycle)
455 {
456     // Make this animated cycle the one edited in the editor
457     animatedCycleEditor->setAnimatedCycle(inbetweenFace,indexCycle);
458 
459     // Show editor
460     if(!dockAnimatedCycleEditor->isVisible())
461     {
462         //if(dockAnimatedCycleEditor->isFloating)
463         //addDockWidget(Qt::RightDockWidgetArea, dockAnimatedCycleEditor);
464         dockAnimatedCycleEditor->show();
465     }
466 }
467 
createInbetweenFace()468 void MainWindow::createInbetweenFace()
469 {
470     // Create inbetween face with one (invalid for now) animated cycle
471     InbetweenFace * inbetweenFace = scene_->createInbetweenFace();
472     inbetweenFace->addAnimatedCycle();
473 
474     // Set as edited cycle
475     editAnimatedCycle(inbetweenFace,0);
476 }
477 
478 
displayModeChanged()479 void MainWindow::displayModeChanged()
480 {
481     updatePicking();
482     update();
483 }
484 
setDisplayModeNormal()485 void MainWindow::setDisplayModeNormal()
486 {
487     multiView_->setDisplayMode(ViewSettings::ILLUSTRATION);
488 }
489 
setDisplayModeNormalOutline()490 void MainWindow::setDisplayModeNormalOutline()
491 {
492     multiView_->setDisplayMode(ViewSettings::ILLUSTRATION_OUTLINE);
493 }
494 
setDisplayModeOutline()495 void MainWindow::setDisplayModeOutline()
496 {
497     multiView_->setDisplayMode(ViewSettings::OUTLINE);
498 }
499 
setOnionSkinningEnabled(bool enabled)500 void MainWindow::setOnionSkinningEnabled(bool enabled)
501 {
502     multiView_->setOnionSkinningEnabled(enabled);
503 }
504 
505 
506 
toggleShowCanvas(bool)507 void MainWindow::toggleShowCanvas(bool)
508 {
509     update();
510 }
511 
isEditCanvasSizeVisible() const512 bool MainWindow::isEditCanvasSizeVisible() const
513 {
514     bool res = false;
515 
516     if(editCanvasSizeDialog_)
517         res = res || editCanvasSizeDialog_->isVisible();
518 
519     if(exportPngDialog_)
520         res = res || exportPngDialog_->isVisible();
521 
522     if(exportingPng_)
523         res = true;
524 
525     return res;
526 }
527 
editCanvasSize()528 void MainWindow::editCanvasSize()
529 {
530     if(isEditCanvasSizeVisible())
531         return;
532 
533     if(!editCanvasSizeDialog_)
534     {
535         editCanvasSizeDialog_ = new EditCanvasSizeDialog(scene());
536         editCanvasSizeDialog_->setParent(this, Qt::Dialog);
537         editCanvasSizeDialog_->setModal(false);
538     }
539 
540     if(!actionShowCanvas->isChecked())
541         actionShowCanvas->setChecked(true);
542 
543     editCanvasSizeDialog_->show();
544 }
545 
546 /*********************************************************************
547  *                       Overloaded event methods
548  */
549 
550 
keyPressEvent(QKeyEvent * event)551 void MainWindow::keyPressEvent(QKeyEvent *event)
552 {
553     // Early catch of overloaded standard shortcut to prevent the "ambiguous shortcut" popup to be shown
554 
555     // ignore the event
556     event->ignore();
557 }
558 
keyReleaseEvent(QKeyEvent * event)559 void MainWindow::keyReleaseEvent(QKeyEvent *event)
560 {
561     // ignore the event
562     event->ignore();
563 }
564 
update()565 void MainWindow::update()
566 {
567     multiView_->update();
568     if(view3D_ && view3D_->isVisible())
569     {
570         view3D_->update();
571     }
572 }
573 
updatePicking()574 void MainWindow::updatePicking()
575 {
576     multiView_->updatePicking();
577 }
578 
eventFilter(QObject * object,QEvent * event)579 bool MainWindow::eventFilter(QObject *object, QEvent *event)
580 {
581     qDebug() << "event filter";
582 
583     if(event->type() == QEvent::Shortcut)
584     {
585         qDebug() << "Shortcut event";
586     }
587     return QMainWindow::eventFilter(object, event);
588 }
589 
590 
591 
592 /*********************************************************************
593  *                     Save / Load / Close
594  */
595 
closeEvent(QCloseEvent * event)596 void MainWindow::closeEvent(QCloseEvent *event)
597 {
598     if (maybeSave_())
599     {
600         global()->writeSettings();
601         event->accept();
602         selectionInfo_->close();
603     }
604     else
605     {
606         event->ignore();
607     }
608 }
609 
maybeSave_()610 bool MainWindow::maybeSave_()
611 {
612     if (isModified_())
613     {
614         QMessageBox::StandardButton ret;
615         ret = QMessageBox::warning(this, tr("Pending changes"),
616                                    tr("The document has been modified.\n"
617                                       "Do you want to save your changes?"),
618                                    QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
619         if (ret == QMessageBox::Save)
620             return save();
621         else if (ret == QMessageBox::Cancel)
622             return false;
623     }
624     return true;
625 }
626 
newDocument()627 void MainWindow::newDocument()
628 {
629     if (maybeSave_())
630     {
631         // Set document file path
632         setDocumentFilePath_("");
633 
634         // Set empty scene
635         Scene * newScene = Scene::createDefaultScene();
636         scene_->copyFrom(newScene);
637         delete newScene;
638 
639         // Add to undo stack
640         resetUndoStack_();
641     }
642 }
643 
open()644 void MainWindow::open()
645 {
646     if (maybeSave_())
647     {
648         // Browse for a file to open
649         QString filePath = QFileDialog::getOpenFileName(this, tr("Open"), global()->documentDir().path(), tr("Vec files (*.vec)"));
650 
651         // Open file
652         if (!filePath.isEmpty())
653             open_(filePath);
654     }
655 }
656 
importSvg()657 void MainWindow::importSvg()
658 {
659     QString filePath = QFileDialog::getOpenFileName(this, tr("Import as SVG"), global()->documentDir().path(), tr("SVG files (*.svg)"));
660     // Open file
661     if (!filePath.isEmpty())
662         doImportSvg(filePath);
663 }
664 
save()665 bool MainWindow::save()
666 {
667     if(isNewDocument_())
668     {
669         return saveAs();
670     }
671     else
672     {
673         bool success = save_(documentFilePath_);
674 
675         if(success)
676         {
677             statusBar()->showMessage(tr("File %1 successfully saved.").arg(documentFilePath_));
678             setUnmodified_();
679             return true;
680         }
681         else
682         {
683             QMessageBox::warning(this, tr("Error"), tr("File %1 not saved: couldn't write file").arg(documentFilePath_));
684             return false;
685         }
686     }
687 }
688 
saveAs()689 bool MainWindow::saveAs()
690 {
691     QString filename = QFileDialog::getSaveFileName(this, tr("Save As"), global()->documentDir().path());
692 
693     if (filename.isEmpty())
694         return false;
695 
696     if(!filename.endsWith(".vec"))
697         filename.append(".vec");
698 
699     bool relativeRemap = true;
700     bool success = save_(filename, relativeRemap);
701 
702     if(success)
703     {
704         statusBar()->showMessage(tr("File %1 successfully saved.").arg(filename));
705         setUnmodified_();
706         setDocumentFilePath_(filename);
707         return true;
708     }
709     else
710     {
711         QMessageBox::warning(this, tr("Error"), tr("File %1 not saved: couldn't write file").arg(filename));
712         return false;
713     }
714 }
715 
716 
exportSVG()717 bool MainWindow::exportSVG()
718 {
719     QString filename = QFileDialog::getSaveFileName(this, tr("Export as SVG"), global()->documentDir().path());
720     if (filename.isEmpty())
721         return false;
722 
723     if(!filename.endsWith(".svg"))
724         filename.append(".svg");
725 
726     bool success = doExportSVG(filename);
727 
728     if(success)
729     {
730         return true;
731     }
732     else
733     {
734         QMessageBox::warning(this, tr("Error"), tr("File %1 not saved: couldn't write file").arg(filename));
735         return false;
736     }
737 }
738 
exportPNG()739 bool MainWindow::exportPNG()
740 {
741     exportPngFilename_ = QFileDialog::getSaveFileName(this, tr("Export as PNG"), global()->documentDir().path());
742     if (exportPngFilename_.isEmpty())
743         return false;
744 
745     if(!exportPngFilename_.endsWith(".png"))
746         exportPngFilename_.append(".png");
747 
748     if(!exportPngDialog_)
749     {
750         exportPngDialog_ = new ExportPngDialog(scene());
751         exportPngDialog_->setParent(this, Qt::Dialog);
752         exportPngDialog_->setModal(false);
753         connect(exportPngDialog_, SIGNAL(accepted()), this, SLOT(acceptExportPNG()));
754         connect(exportPngDialog_, SIGNAL(rejected()), this, SLOT(rejectExportPNG()));
755     }
756 
757     exportPngCanvasWasVisible_ = actionShowCanvas->isChecked();
758     if(!exportPngCanvasWasVisible_)
759         actionShowCanvas->setChecked(true);
760 
761     exportPngDialog_->show();
762 
763     // Note: the dialog is modeless to allow user to pan/zoom the image while changing
764     //       canvas size and resolution.
765     //       But this mean that we can't return here whether or not the export was done
766 
767     // The return value doesn't actually make sense here. Maybe this function
768     // shouldn't return anything instead.
769     return true;
770 }
771 
acceptExportPNG()772 bool MainWindow::acceptExportPNG()
773 {
774     exportingPng_ = true; // This is necessary so that isEditCanvasSizeVisible() returns true
775                           // so that global()->toolMode() returns EDIT_CANVAS_SIZE so that
776                           // selection is not rendered as selected
777     bool success = doExportPNG(exportPngFilename_);
778     exportingPng_ = false;
779 
780 
781     if(!success)
782     {
783         QMessageBox::warning(this, tr("Error"), tr("File %1 not saved: couldn't write file").arg(exportPngFilename_));
784     }
785 
786     if(!exportPngCanvasWasVisible_)
787         actionShowCanvas->setChecked(false);
788 
789     updatePicking();
790     update();
791 
792     return success;
793 }
794 
rejectExportPNG()795 bool MainWindow::rejectExportPNG()
796 {
797     if(!exportPngCanvasWasVisible_)
798         actionShowCanvas->setChecked(false);
799 
800     updatePicking();
801     update();
802 
803     return false;
804 }
805 
setDocumentFilePath_(const QString & filePath)806 void MainWindow::setDocumentFilePath_(const QString & filePath)
807 {
808     documentFilePath_ = filePath;
809 
810     QFileInfo fileInfo(filePath);
811     if (fileInfo.exists() && fileInfo.isFile())
812     {
813         global()->setDocumentDir(fileInfo.dir());
814     }
815     else
816     {
817         global()->setDocumentDir(QDir::home());
818     }
819 
820     updateWindowTitle_();
821 }
822 
isNewDocument_() const823 bool MainWindow::isNewDocument_() const
824 {
825     return documentFilePath_.isEmpty();
826 }
827 
setUnmodified_()828 void MainWindow::setUnmodified_()
829 {
830     savedUndoIndex_ = undoIndex_;
831     updateWindowTitle_();
832 }
833 
isModified_() const834 bool MainWindow::isModified_() const
835 {
836     return savedUndoIndex_ != undoIndex_;
837 }
838 
updateWindowTitle_()839 void MainWindow::updateWindowTitle_()
840 {
841     setWindowFilePath(isNewDocument_() ? tr("New Document") : documentFilePath_);
842     setWindowModified(isModified_());
843 }
844 
open_(const QString & filePath)845 void MainWindow::open_(const QString & filePath)
846 {
847     // Convert to newest version if necessary
848     bool conversionSuccessful = FileVersionConverter(filePath).convertToVersion(qApp->applicationVersion(), this);
849 
850     // Open (possibly converted) file
851     if (conversionSuccessful)
852     {
853         QFile file(filePath);
854         if (!file.open(QFile::ReadOnly | QFile::Text))
855         {
856             qDebug() << "Error: cannot open file";
857             QMessageBox::warning(this, tr("Error"), tr("Error: couldn't open file %1").arg(filePath));
858             return;
859         }
860 
861         // Set document file path. This must be done before read(xml) because
862         // read(xml) causes the scene to change, which causes a redraw, which
863         // requires a correct document file path to resolve relative file paths
864         setDocumentFilePath_(filePath);
865 
866         // Create XML stream reader and proceed
867         XmlStreamReader xml(&file);
868         read(xml);
869 
870         // Close file
871         file.close();
872 
873         // Add to undo stack
874         resetUndoStack_();
875     }
876 }
877 
doImportSvg(const QString & filePath)878 void MainWindow::doImportSvg(const QString & filePath)
879 {
880     SvgImportDialog * dialog = new SvgImportDialog(this);
881     dialog->exec();
882 
883     QFile file(filePath);
884     if (!file.open(QFile::ReadOnly | QFile::Text))
885     {
886         qDebug() << "Error: cannot open file";
887         return;
888     }
889 
890     XmlStreamReader xml(&file);
891     SvgParser::readSvg(xml, SvgImportDialog::params());
892 
893     // Close file
894     file.close();
895 
896     updatePicking();
897     scene_->emitChanged();
898     scene_->emitCheckpoint();
899 }
900 
save_(const QString & filePath,bool relativeRemap)901 bool MainWindow::save_(const QString & filePath, bool relativeRemap)
902 {
903     // Open file to save to
904     QFile file(filePath);
905     if (!file.open(QIODevice::WriteOnly | QFile::Truncate | QFile::Text))
906     {
907         qWarning("Couldn't write file.");
908         return false;
909     }
910 
911     // Remap relative paths if need be
912     if (relativeRemap)
913     {
914         QFileInfo fileInfo(file);
915         QDir oldDocumentDir = global()->documentDir();
916         QDir newDocumentDir = fileInfo.dir();
917         if (oldDocumentDir != newDocumentDir)
918         {
919             global()->setDocumentDir(newDocumentDir);
920             scene()->relativeRemap(oldDocumentDir, newDocumentDir);
921         }
922     }
923 
924     // Write to file
925     XmlStreamWriter xmlStream(&file);
926     write(xmlStream);
927 
928     // Close file
929     file.close();
930 
931     // Success
932     return true;
933 }
934 
read_DEPRECATED(QTextStream & in)935 void MainWindow::read_DEPRECATED(QTextStream & in)
936 {
937     // Buffer variables
938     QChar cskip;
939     QString field;
940 
941     // Header
942     QString header = in.readLine();
943     if(header != fileHeader_)
944         QMessageBox::warning(this, tr("Warning"), tr("Incorrect file header. I'm still trying to open the file but it might be corrupted."));
945 
946     // Version
947     int minor, major;
948     field = Read::field(in);
949     in >> major >> cskip >> minor;
950     if(major!=1 || minor!=0)
951         QMessageBox::warning(this, tr("Warning"), tr("Incorrect file version. I'm still trying to open the file but it might be corrupted."));
952 
953     // Scene
954     field = Read::field(in);
955     Read::skipBracket(in);
956     scene_->read(in);
957     Read::skipBracket(in);
958 }
959 
write_DEPRECATED(QTextStream & out)960 void MainWindow::write_DEPRECATED(QTextStream & out)
961 {
962     Save::resetIndent();
963 
964     // Header
965     out << fileHeader_;
966 
967     // Version
968     out << Save::newField("Version")
969         << 1 << "." << 0;
970 
971     // Scene
972     out << Save::newField("Scene");
973     out << Save::openCurlyBrackets();
974     scene_->save(out);
975     out << Save::closeCurlyBrackets();
976 }
977 
write(XmlStreamWriter & xml)978 void MainWindow::write(XmlStreamWriter &xml)
979 {
980     // Start XML Document
981     xml.writeStartDocument();
982 
983     // Header
984     xml.writeComment(" Created with VPaint (http://www.vpaint.org) ");
985     xml.writeCharacters("\n\n");
986 
987     // Document
988     xml.writeStartElement("vec");
989     {
990         Version version(qApp->applicationVersion());
991         bool ignorePatch = true;
992         xml.writeAttribute("version", version.toString(ignorePatch));
993 
994         // Metadata such as author and license? Different options:
995         //   1) as comments in header (issue: not part of document or XML spec, cross-editor compatibility issues)
996         //   2) as attributes of vec
997         //   3) as its own XML element
998         // "metadata" or "properties"? Probably metadata. even in PDF when this info is often accessed
999         // in File > Properties, it is still sotred as "metadata"
1000         // Resources:
1001         //   https://helpx.adobe.com/acrobat/using/pdf-properties-metadata.html)
1002         //   http://www.w3.org/TR/SVG/metadata.html
1003         //xmlStream.writeStartElement("metadata");
1004         //xmlStream.writeAttribute("author", "Boris Dalstein");
1005         //xmlStream.writeAttribute("license", "CC BY-SA");
1006         //xmlStream.writeEndElement();
1007 
1008         // Playback
1009         xml.writeStartElement("playback");
1010         timeline()->write(xml);
1011         xml.writeEndElement();
1012 
1013         // Canvas
1014         xml.writeStartElement("canvas");
1015         scene()->writeCanvas(xml);
1016         xml.writeEndElement();
1017 
1018         // Layers
1019         scene()->writeAllLayers(xml);
1020     }
1021     xml.writeEndElement();
1022 
1023     // End XML Document
1024     xml.writeEndDocument();
1025 }
1026 
read(XmlStreamReader & xml)1027 void MainWindow::read(XmlStreamReader & xml)
1028 {
1029     scene_->clear();
1030 
1031     if (xml.readNextStartElement())
1032     {
1033         if (xml.name() != "vec")
1034         {
1035             QMessageBox::warning(this,
1036                 "Cannot open file",
1037                 "Sorry, the file you are trying to open is an invalid VEC file.");
1038             return;
1039         }
1040 
1041         while (xml.readNextStartElement())
1042         {
1043             // Playback
1044             if (xml.name() == "playback")
1045             {
1046                 timeline_->read(xml);
1047             }
1048 
1049             // Canvas
1050             else if (xml.name() == "canvas")
1051             {
1052                 scene_->readCanvas(xml);
1053             }
1054 
1055             // Layer
1056             else if (xml.name() == "layer")
1057             {
1058                 scene_->readOneLayer(xml);
1059             }
1060 
1061             // Unknown
1062             else
1063             {
1064                 xml.skipCurrentElement();
1065             }
1066         }
1067     }
1068 }
1069 
doExportSVG(const QString & filename)1070 bool MainWindow::doExportSVG(const QString & filename)
1071 {
1072     QFile data(filename);
1073     if (data.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
1074 
1075         QTextStream out(&data);
1076 
1077         QString header = QString(
1078                 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
1079                 "<!-- Created with VPaint (http://www.vpaint.org/) -->\n\n"
1080 
1081                 "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"
1082                 "  \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
1083                 "<svg \n"
1084                 "  viewBox=\"%1 %2 %3 %4\"\n"
1085                 "  xmlns=\"http://www.w3.org/2000/svg\"\n"
1086                 "  xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n")
1087 
1088                 .arg(scene_->left())
1089                 .arg(scene_->top())
1090                 .arg(scene_->width())
1091                 .arg(scene_->height());
1092 
1093         QString footer = "</svg>";
1094 
1095         out << header;
1096         scene_->exportSVG(multiView_->activeView()->activeTime(), out);
1097         out << footer;
1098 
1099         statusBar()->showMessage(tr("File %1 successfully saved.").arg(filename));
1100         return true;
1101     }
1102     else
1103     {
1104         qDebug() << "Error: cannot open file";
1105         return false;
1106     }
1107 }
1108 
doExportPNG(const QString & filename)1109 bool MainWindow::doExportPNG(const QString & filename)
1110 {
1111     if(!exportPngDialog_->exportSequence())
1112     {
1113         // Export single frame
1114 
1115         QImage img = multiView_->activeView()->drawToImage(
1116                     scene()->left(), scene()->top(), scene()->width(), scene()->height(),
1117                     exportPngDialog_->pngWidth(), exportPngDialog_->pngHeight(),
1118                     exportPngDialog_->useViewSettings());
1119 
1120         img.save(filename);
1121     }
1122     else
1123     {
1124         // Export sequence of frames
1125 
1126         // Decompose filename into basename + suffix. Example:
1127         //     abc_1234_5678.de.png  ->   abc_1234  +  de.png
1128         QFileInfo info(filename);
1129         QString baseName = info.baseName();
1130         QString suffix = info.suffix();
1131         // Decompose basename into cleanedbasename + numbering. Examples:
1132         //     abc_1234_5678  ->     abc_1234 + 5678
1133         int iNumbering = baseName.indexOf(QRegExp("_[0-9]*$"));
1134         if(iNumbering != -1)
1135         {
1136             baseName.chop(baseName.length() - iNumbering);
1137         }
1138 
1139         // Get dir
1140         QDir dir = info.absoluteDir();
1141 
1142         // Get and delete files from previous export
1143         QString nameFilter = baseName + QString("_*.") +  suffix;
1144         QStringList nameFilters = QStringList() << nameFilter;
1145         QStringList prevFiles = dir.entryList(nameFilters, QDir::Files);
1146         foreach(QString prevFile, prevFiles)
1147             dir.remove(prevFile);
1148 
1149         // Get frame numbers to export
1150         int firstFrame = timeline()->firstFrame();
1151         int lastFrame = timeline()->lastFrame();
1152 
1153         // Create Progress dialog for feedback
1154         QProgressDialog progress("Export sequence as PNGs...", "Abort", 0, lastFrame-firstFrame+1, this);
1155         progress.setWindowModality(Qt::WindowModal);
1156 
1157         // Export all frames in the sequence
1158         for(int i=firstFrame; i<=lastFrame; ++i)
1159         {
1160             progress.setValue(i-firstFrame);
1161 
1162             if (progress.wasCanceled())
1163                 break;
1164 
1165             QString number = QString("%1").arg(i, 4, 10, QChar('0'));
1166             QString filePath = dir.absoluteFilePath(
1167                         baseName + QString("_") + number + QString(".") + suffix);
1168 
1169             QImage img = multiView_->activeView()->drawToImage(
1170                         Time(i),
1171                         scene()->left(), scene()->top(), scene()->width(), scene()->height(),
1172                         exportPngDialog_->pngWidth(), exportPngDialog_->pngHeight(),
1173                         exportPngDialog_->useViewSettings());
1174 
1175             img.save(filePath);
1176         }
1177         progress.setValue(lastFrame-firstFrame+1);
1178 
1179     }
1180 
1181     return true;
1182 }
1183 
onlineDocumentation()1184 void MainWindow::onlineDocumentation()
1185 {
1186     QDesktopServices::openUrl(QUrl("http://www.vpaint.org/doc"));
1187 }
1188 
gettingStarted()1189 void MainWindow::gettingStarted()
1190 {
1191     gettingStarted_->setSource(QUrl("help/getting-started.htm"));
1192     gettingStarted_->show();
1193 }
1194 
manual()1195 void MainWindow::manual()
1196 {
1197     gettingStarted_->setSource(QUrl("help/user-manual.htm"));
1198     userManual_->show();
1199 }
1200 
onSceneLayerAttributesChanged_()1201 void MainWindow::onSceneLayerAttributesChanged_()
1202 {
1203     backgroundWidget->setBackground(scene()->activeBackground());
1204 }
1205 
about()1206 void MainWindow::about()
1207 {
1208     if(!aboutDialog_)
1209     {
1210         aboutDialog_ = new AboutDialog(global()->settings().showAboutDialogAtStartup());
1211         aboutDialog_->setParent(this, Qt::Dialog);
1212     }
1213 
1214     aboutDialog_->exec();
1215 
1216     if(aboutDialog_)
1217     {
1218         global()->settings().setShowAboutDialogAtStartup(aboutDialog_->showAtStartup());
1219     }
1220 }
1221 
openClose3D()1222 void MainWindow::openClose3D()
1223 {
1224     if(view3D_)
1225     {
1226         if(view3D_->isVisible())
1227         {
1228             view3D_->hide();
1229         }
1230         else
1231         {
1232             view3D_->show();
1233         }
1234     }
1235 
1236     updateView3DActionCheckState();
1237 }
1238 
updateView3DActionCheckState()1239 void MainWindow::updateView3DActionCheckState()
1240 {
1241     if(view3D_)
1242     {
1243         if(view3D_->isVisible())
1244         {
1245             view3DActionSetChecked();
1246         }
1247         else
1248         {
1249             view3DActionSetUnchecked();
1250         }
1251     }
1252 }
1253 
view3DActionSetUnchecked()1254 void MainWindow::view3DActionSetUnchecked()
1255 {
1256     actionOpenClose3D->setChecked(false);
1257 }
1258 
view3DActionSetChecked()1259 void MainWindow::view3DActionSetChecked()
1260 {
1261     actionOpenClose3D->setChecked(true);
1262 }
1263 
openClose3DSettings()1264 void MainWindow::openClose3DSettings()
1265 {
1266     if(view3DSettingsWidget_)
1267     {
1268         // toggle visibility
1269         bool visible = view3DSettingsWidget_->isVisible();
1270         view3DSettingsWidget_->setVisible(!visible);
1271     }
1272 
1273     updateView3DSettingsActionCheckState();
1274 }
1275 
updateView3DSettingsActionCheckState()1276 void MainWindow::updateView3DSettingsActionCheckState()
1277 {
1278     if(view3DSettingsWidget_)
1279     {
1280         if(view3DSettingsWidget_->isVisible())
1281         {
1282             view3DSettingsActionSetChecked();
1283         }
1284         else
1285         {
1286             view3DSettingsActionSetUnchecked();
1287         }
1288     }
1289 }
1290 
view3DSettingsActionSetUnchecked()1291 void MainWindow::view3DSettingsActionSetUnchecked()
1292 {
1293     actionOpenCloseView3DSettings->setChecked(false);
1294 }
1295 
view3DSettingsActionSetChecked()1296 void MainWindow::view3DSettingsActionSetChecked()
1297 {
1298     actionOpenCloseView3DSettings->setChecked(true);
1299 }
1300 
updateViewMenu()1301 void MainWindow::updateViewMenu()
1302 {
1303     // Display mode
1304     ViewSettings::DisplayMode mode = multiView_->activeView()->viewSettings().displayMode();
1305     switch(mode)
1306     {
1307     case ViewSettings::ILLUSTRATION:
1308         actionDisplayModeNormal->setChecked(true);
1309         break;
1310     case ViewSettings::ILLUSTRATION_OUTLINE:
1311         actionDisplayModeNormalOutline->setChecked(true);
1312         break;
1313     case ViewSettings::OUTLINE:
1314         actionDisplayModeOutline->setChecked(true);
1315         break;
1316     }
1317 
1318     // Onion skinning
1319     actionOnionSkinning->setChecked(multiView_->activeView()->viewSettings().onionSkinningIsEnabled());
1320 }
1321 
1322 /*********************************************************************
1323  *                             Actions
1324  */
1325 
1326 
createActions()1327 void MainWindow::createActions()
1328 {
1329     ///////////////        FILE        ///////////////
1330 
1331     // New
1332     actionNew = new QAction(/*QIcon(":/iconLoad"),*/ tr("&New"), this);
1333     actionNew->setStatusTip(tr("Create a new file."));
1334     actionNew->setShortcut(QKeySequence::New);
1335     connect(actionNew, SIGNAL(triggered()), this, SLOT(newDocument()));
1336 
1337     // Open
1338     actionOpen = new QAction(/*QIcon(":/iconLoad"),*/ tr("&Open..."), this);
1339     actionOpen->setStatusTip(tr("Open an existing file."));
1340     actionOpen->setShortcut(QKeySequence::Open);
1341     connect(actionOpen, SIGNAL(triggered()), this, SLOT(open()));
1342 
1343     // Import SVG
1344     actionImportSvg = new QAction(/*QIcon(":/iconLoad"),*/ tr("SVG [Beta]"), this);
1345     actionImportSvg->setStatusTip(tr("Import an existing SVG file."));
1346     connect(actionImportSvg, SIGNAL(triggered()), this, SLOT(importSvg()));
1347 
1348     // Save
1349     actionSave = new QAction(/*QIcon(":/iconSave"),*/ tr("&Save"), this);
1350     actionSave->setStatusTip(tr("Save current illustration."));
1351     actionSave->setShortcut(QKeySequence::Save);
1352     connect(actionSave, SIGNAL(triggered()), this, SLOT(save()));
1353 
1354     // Save As
1355     actionSaveAs = new QAction(/*QIcon(":/iconSave"),*/ tr("Save &As..."), this);
1356     actionSaveAs->setStatusTip(tr("Save current illustration with a new name."));
1357     actionSaveAs->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_S));
1358     connect(actionSaveAs, SIGNAL(triggered()), this, SLOT(saveAs()));
1359 
1360     // Export SVG
1361     actionExportSVG = new QAction(/*QIcon(":/iconSave"),*/ tr("SVG (frame) [Beta]"), this);
1362     actionExportSVG->setStatusTip(tr("Save the current illustration in the SVG file format."));
1363     //actionExportSVG->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
1364     connect(actionExportSVG, SIGNAL(triggered()), this, SLOT(exportSVG()));
1365 
1366     // Export PNG
1367     actionExportPNG = new QAction(/*QIcon(":/iconSave"),*/ tr("PNG (frame or sequence)"), this);
1368     actionExportPNG->setStatusTip(tr("Save the current illustration in the PNG file format."));
1369     //actionExportPNG->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_E));
1370     connect(actionExportPNG, SIGNAL(triggered()), this, SLOT(exportPNG()));
1371 
1372     // Preferences
1373     /*
1374     actionPreferences = new QAction(tr("&Preferences..."), this);
1375     actionPreferences->setStatusTip(tr("Modify the settings of VPaint."));
1376     actionPreferences->setShortcut(QKeySequence::Preferences);
1377     connect(actionPreferences, SIGNAL(triggered()), global(), SLOT(openPreferencesDialog()));
1378     */
1379 
1380     // Quit
1381     actionQuit = new QAction(/*QIcon(":/iconQuit"),*/ tr("&Quit"), this);
1382     actionQuit->setStatusTip(tr("Quit VPaint."));
1383     actionQuit->setShortcut(QKeySequence::Quit);
1384     connect(actionQuit, SIGNAL(triggered()), this, SLOT(close()));
1385 
1386 
1387     ///////////////        EDIT        ///////////////
1388 
1389     // Undo
1390     actionUndo = new QAction(/*QIcon(":/iconUndo"),*/ tr("&Undo"), this);
1391     actionUndo->setStatusTip(tr("Undo the last action."));
1392     actionUndo->setShortcut(QKeySequence::Undo);
1393     connect(actionUndo, SIGNAL(triggered()), this, SLOT(undo()));
1394 
1395     // Redo
1396     actionRedo = new QAction(/*QIcon(":/iconRedo"),*/ tr("&Redo"), this);
1397     actionRedo->setStatusTip(tr("Redo an undone action."));
1398     actionRedo->setShortcut(QKeySequence::Redo);
1399     connect(actionRedo, SIGNAL(triggered()), this, SLOT(redo()));
1400 
1401     // Cut
1402     actionCut = new QAction(tr("Cut"), this);
1403     actionCut->setStatusTip(tr("Move selected objects to the clipboard."));
1404     actionCut->setShortcut(QKeySequence::Cut);
1405     connect(actionCut, SIGNAL(triggered()), this, SLOT(cut()));
1406 
1407     // Copy
1408     actionCopy = new QAction(tr("Copy"), this);
1409     actionCopy->setStatusTip(tr("Copy the selected objects to the clipboard."));
1410     actionCopy->setShortcut(QKeySequence::Copy);
1411     connect(actionCopy, SIGNAL(triggered()), this, SLOT(copy()));
1412 
1413     // Paste
1414     actionPaste = new QAction(tr("Paste"), this);
1415     actionPaste->setStatusTip(tr("Paste the objects from the clipboard."));
1416     actionPaste->setShortcut(QKeySequence::Paste);
1417     connect(actionPaste, SIGNAL(triggered()), this, SLOT(paste()));
1418 
1419 
1420     // Smart Delete
1421     actionSmartDelete = new QAction(tr("Delete"), this);
1422     actionSmartDelete->setStatusTip(tr("Delete the selected objects, merging adjacent objects when possible."));
1423 #ifdef Q_OS_MAC
1424     actionSmartDelete->setShortcut(QKeySequence(Qt::Key_Delete));
1425 #else
1426     actionSmartDelete->setShortcut(QKeySequence::Delete);
1427 #endif
1428     actionSmartDelete->setShortcutContext(Qt::ApplicationShortcut);
1429     connect(actionSmartDelete, SIGNAL(triggered()), scene_, SLOT(smartDelete()));
1430 
1431     // Hard Delete
1432     actionHardDelete = new QAction(tr("Hard Delete"), this);
1433     actionHardDelete->setStatusTip(tr("Delete the selected objects and adjacent objects together."));
1434     actionHardDelete->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Delete));
1435     actionHardDelete->setShortcutContext(Qt::ApplicationShortcut);
1436     connect(actionHardDelete, SIGNAL(triggered()), scene_, SLOT(deleteSelectedCells()));
1437 
1438     // Hard Delete
1439     actionTest = new QAction(tr("Test"), this);
1440     actionTest->setStatusTip(tr("For development tests: quick and dirty function."));
1441     actionTest->setShortcut(QKeySequence(Qt::Key_T));
1442     actionTest->setShortcutContext(Qt::ApplicationShortcut);
1443     connect(actionTest, SIGNAL(triggered()), scene_, SLOT(test()));
1444 
1445     ///////////////        VIEW        ///////////////
1446 
1447     // Zoom In
1448     actionZoomIn = new QAction(tr("Zoom in"), this);
1449     actionZoomIn->setStatusTip(tr("Makes objects appear bigger."));
1450     actionZoomIn->setShortcut(QKeySequence::ZoomIn);
1451     actionZoomIn->setShortcutContext(Qt::ApplicationShortcut);
1452     connect(actionZoomIn, SIGNAL(triggered()), multiView_, SLOT(zoomIn()));
1453 
1454     // Zoom Out
1455     actionZoomOut = new QAction(tr("Zoom out"), this);
1456     actionZoomOut->setStatusTip(tr("Makes objects appear smaller."));
1457     actionZoomOut->setShortcut(QKeySequence::ZoomOut);
1458     actionZoomOut->setShortcutContext(Qt::ApplicationShortcut);
1459     connect(actionZoomOut, SIGNAL(triggered()), multiView_, SLOT(zoomOut()));
1460 
1461     actionShowCanvas = new QAction(tr("Display canvas"), this);
1462     actionShowCanvas->setStatusTip(tr("Show or hide the canvas borders."));
1463     actionShowCanvas->setCheckable(true);
1464     actionShowCanvas->setChecked(true);
1465     //actionShowCanvas->setShortcut(QKeySequence::ZoomOut);
1466     //actionShowCanvas->setShortcutContext(Qt::ApplicationShortcut);
1467     connect(actionShowCanvas, SIGNAL(triggered(bool)), this, SLOT(toggleShowCanvas(bool)));
1468 
1469     actionEditCanvasSize = new QAction(tr("Edit canvas size..."), this);
1470     actionEditCanvasSize->setStatusTip(tr("Edit the size of the canvas."));
1471     //actionEditCanvasSize->setShortcut(QKeySequence::ZoomOut);
1472     //actionEditCanvasSize->setShortcutContext(Qt::ApplicationShortcut);
1473     connect(actionEditCanvasSize, SIGNAL(triggered()), this, SLOT(editCanvasSize()));
1474 
1475     // Fit Illustration In Window
1476     actionFitAllInWindow = new QAction(tr("Fit illustration in window"), this);
1477     actionFitAllInWindow->setStatusTip(tr("Automatically select an appropriate zoom to see the whole illustration."));
1478     //actionFitAllInWindow->setShortcut(QKeySequence::ZoomOut);
1479     //actionFitAllInWindow->setShortcutContext(Qt::ApplicationShortcut);
1480     connect(actionFitAllInWindow, SIGNAL(triggered()), multiView_, SLOT(fitAllInWindow()));
1481 
1482     // Fit Selection In Window
1483     actionFitSelectionInWindow = new QAction(tr("Fit selection in window"), this);
1484     actionFitSelectionInWindow->setStatusTip(tr("Automatically select an appropriate zoom to see the selected objects."));
1485     //actionFitSelectionInWindow->setShortcut(QKeySequence::ZoomOut);
1486     //actionFitSelectionInWindow->setShortcutContext(Qt::ApplicationShortcut);
1487     connect(actionFitSelectionInWindow, SIGNAL(triggered()), multiView_, SLOT(fitSelectionInWindow()));
1488 
1489     actionToggleOutline = new QAction(tr("Toggle outline"), this);
1490     actionToggleOutline->setStatusTip(tr("Toggle the outline of the illustration"));
1491     actionToggleOutline->setShortcut(QKeySequence(Qt::Key_Space));
1492     actionToggleOutline->setShortcutContext(Qt::ApplicationShortcut);
1493     connect(actionToggleOutline, SIGNAL(triggered()), multiView_, SLOT(toggleOutline()));
1494 
1495     actionToggleOutlineOnly = new QAction(tr("Toggle only outline"), this);
1496     actionToggleOutlineOnly->setStatusTip(tr("Toggle only the outline of the illustration"));
1497     actionToggleOutlineOnly->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Space));
1498     actionToggleOutlineOnly->setShortcutContext(Qt::ApplicationShortcut);
1499     connect(actionToggleOutlineOnly, SIGNAL(triggered()), multiView_, SLOT(toggleOutlineOnly()));
1500 
1501     actionDisplayModeNormal = new QAction(tr("Normal"), this);
1502     actionDisplayModeNormal->setCheckable(true);
1503     actionDisplayModeNormal->setStatusTip(tr("Switch to normal display mode for the active view"));
1504     actionDisplayModeNormal->setShortcut(QKeySequence(Qt::Key_1));
1505     actionDisplayModeNormal->setShortcutContext(Qt::ApplicationShortcut);
1506     connect(actionDisplayModeNormal, SIGNAL(triggered()), this, SLOT(setDisplayModeNormal()));
1507 
1508     actionDisplayModeNormalOutline = new QAction(tr("Normal+Outline"), this);
1509     actionDisplayModeNormalOutline->setCheckable(true);
1510     actionDisplayModeNormalOutline->setStatusTip(tr("Switch to normal+outline display mode for the active view"));
1511     actionDisplayModeNormalOutline->setShortcut(QKeySequence(Qt::Key_2));
1512     actionDisplayModeNormalOutline->setShortcutContext(Qt::ApplicationShortcut);
1513     connect(actionDisplayModeNormalOutline, SIGNAL(triggered()), this, SLOT(setDisplayModeNormalOutline()));
1514 
1515     actionDisplayModeOutline = new QAction(tr("Outline"), this);
1516     actionDisplayModeOutline->setCheckable(true);
1517     actionDisplayModeOutline->setStatusTip(tr("Switch to outline display mode for the active view"));
1518     actionDisplayModeOutline->setShortcut(QKeySequence(Qt::Key_3));
1519     actionDisplayModeOutline->setShortcutContext(Qt::ApplicationShortcut);
1520     connect(actionDisplayModeOutline, SIGNAL(triggered()), this, SLOT(setDisplayModeOutline()));
1521 
1522     QActionGroup * displayModeGroup = new QActionGroup(this);
1523     displayModeGroup->addAction(actionDisplayModeNormal);
1524     displayModeGroup->addAction(actionDisplayModeNormalOutline);
1525     displayModeGroup->addAction(actionDisplayModeOutline);
1526     actionDisplayModeNormal->setChecked(true);
1527 
1528     actionOnionSkinning = new QAction(tr("Onion skinning"), this);
1529     actionOnionSkinning->setCheckable(true);
1530     actionOnionSkinning->setChecked(false);
1531     actionOnionSkinning->setStatusTip(tr("Toggle the display of onion skins"));
1532     actionOnionSkinning->setShortcut(QKeySequence(Qt::Key_O));
1533     actionOnionSkinning->setShortcutContext(Qt::ApplicationShortcut);
1534     connect(actionOnionSkinning, SIGNAL(triggered(bool)), this, SLOT(setOnionSkinningEnabled(bool)));
1535 
1536     actionOpenCloseView3DSettings = new QAction(tr("3D View Settings [Beta]"), this);
1537     actionOpenCloseView3DSettings->setCheckable(true);
1538     actionOpenCloseView3DSettings->setStatusTip(tr("Open or Close the settings dialog for the 3D view"));
1539     //actionOpenView3DSettings->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_5));
1540     //actionOpenView3DSettings->setShortcutContext(Qt::ApplicationShortcut);
1541     connect(actionOpenCloseView3DSettings, SIGNAL(triggered()), this, SLOT(openClose3DSettings()));
1542     connect(view3DSettingsWidget_, SIGNAL(closed()), this, SLOT(view3DSettingsActionSetUnchecked()));
1543 
1544     actionOpenClose3D = new QAction(tr("3D View [Beta]"), this);
1545     actionOpenClose3D->setCheckable(true);
1546     actionOpenClose3D->setStatusTip(tr("Open or Close the 3D inbetween View"));
1547     connect(actionOpenClose3D, SIGNAL(triggered()), this, SLOT(openClose3D()));
1548     connect(view3D_, SIGNAL(closed()), this, SLOT(view3DActionSetUnchecked()));
1549 
1550 
1551     // Splitting
1552     actionSplitClose = new QAction(tr("Close active view"), this);
1553     actionSplitClose->setStatusTip(tr("Close the active view"));
1554     actionSplitClose->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_0));
1555     actionSplitClose->setShortcutContext(Qt::ApplicationShortcut);
1556     connect(actionSplitClose, SIGNAL(triggered()), multiView_, SLOT(splitClose()));
1557 
1558     actionSplitOne = new QAction(tr("Close all but active view"), this);
1559     actionSplitOne->setStatusTip(tr("Close all views except the active view"));
1560     actionSplitOne->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_1));
1561     actionSplitOne->setShortcutContext(Qt::ApplicationShortcut);
1562     connect(actionSplitOne, SIGNAL(triggered()), multiView_, SLOT(splitOne()));
1563 
1564     actionSplitVertical = new QAction(tr("Split view vertically"), this);
1565     actionSplitVertical->setStatusTip(tr("Split the active view vertically"));
1566     actionSplitVertical->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_2));
1567     actionSplitVertical->setShortcutContext(Qt::ApplicationShortcut);
1568     connect(actionSplitVertical, SIGNAL(triggered()), multiView_, SLOT(splitVertical()));
1569 
1570     actionSplitHorizontal = new QAction(tr("Split view horizontally"), this);
1571     actionSplitHorizontal->setStatusTip(tr("Split the active view horizontally"));
1572     actionSplitHorizontal->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_3));
1573     actionSplitHorizontal->setShortcutContext(Qt::ApplicationShortcut);
1574     connect(actionSplitHorizontal, SIGNAL(triggered()), multiView_, SLOT(splitHorizontal()));
1575 
1576 
1577 
1578     ///////////////        SELECTION        ///////////////
1579 
1580     // Select All In Frame
1581     actionSelectAllInFrame = new QAction(tr("Select all (current frame)"), this);
1582     actionSelectAllInFrame->setStatusTip(tr("Select all the objects in the current frame."));
1583     actionSelectAllInFrame->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_A));
1584     actionSelectAllInFrame->setShortcutContext(Qt::ApplicationShortcut);
1585     connect(actionSelectAllInFrame, SIGNAL(triggered()), scene_, SLOT(selectAllInFrame()));
1586 
1587     // Select All In Animation
1588     actionSelectAllInAnimation = new QAction(tr("Select all (whole animation)"), this);
1589     actionSelectAllInAnimation->setStatusTip(tr("Select all the objects in the whole animation."));
1590     actionSelectAllInAnimation->setShortcut(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_A));
1591     actionSelectAllInAnimation->setShortcutContext(Qt::ApplicationShortcut);
1592     connect(actionSelectAllInAnimation, SIGNAL(triggered()), scene_, SLOT(selectAllInAnimation()));
1593 
1594     // Deselect All
1595     actionDeselectAll = new QAction(tr("Deselect all"), this);
1596     actionDeselectAll->setStatusTip(tr("Deselect all the objects."));
1597     actionDeselectAll->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_A));
1598     actionDeselectAll->setShortcutContext(Qt::ApplicationShortcut);
1599     connect(actionDeselectAll, SIGNAL(triggered()), scene_, SLOT(deselectAll()));
1600 
1601     // Invert Selection
1602     actionInvertSelection = new QAction(tr("Invert Selection"), this);
1603     actionInvertSelection->setStatusTip(tr("Deselect all the selected objects and select all the other objects."));
1604     actionInvertSelection->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I));
1605     actionInvertSelection->setShortcutContext(Qt::ApplicationShortcut);
1606     connect(actionInvertSelection, SIGNAL(triggered()), scene_, SLOT(invertSelection()));
1607 
1608     // Select Connected Objects
1609     actionSelectConnected = new QAction(tr("Select connected objects"), this);
1610     actionSelectConnected->setStatusTip(tr("Select all the objects that are connected to at least one selected object."));
1611     actionSelectConnected->setShortcut(Qt::Key_Tab);
1612     actionSelectConnected->setShortcutContext(Qt::ApplicationShortcut);
1613     connect(actionSelectConnected, SIGNAL(triggered()), scene_, SLOT(selectConnected()));
1614 
1615     // Select Closure
1616     actionSelectClosure = new QAction(tr("Add boundary to selection"), this);
1617     actionSelectClosure->setStatusTip(tr("Add the boundary of the selected objects to the selection."));
1618     actionSelectClosure->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Tab));
1619     actionSelectClosure->setShortcutContext(Qt::ApplicationShortcut);
1620     connect(actionSelectClosure, SIGNAL(triggered()), scene_, SLOT(selectClosure()));
1621 
1622     // Select Vertices
1623     actionSelectVertices = new QAction(tr("Select vertices"), this);
1624     actionSelectVertices->setStatusTip(tr("Deselect all the objects in the current selection other than vertices."));
1625     actionSelectVertices->setShortcut(QKeySequence(Qt::Key_S, Qt::Key_V));
1626     actionSelectVertices->setShortcutContext(Qt::ApplicationShortcut);
1627     connect(actionSelectVertices, SIGNAL(triggered()), scene_, SLOT(selectVertices()));
1628 
1629     // Select Edges
1630     actionSelectEdges = new QAction(tr("Select edges"), this);
1631     actionSelectEdges->setStatusTip(tr("Deselect all the objects in the current selection other than edges."));
1632     actionSelectEdges->setShortcut(QKeySequence(Qt::Key_S, Qt::Key_E));
1633     actionSelectEdges->setShortcutContext(Qt::ApplicationShortcut);
1634     connect(actionSelectEdges, SIGNAL(triggered()), scene_, SLOT(selectEdges()));
1635 
1636     // Select Faces
1637     actionSelectFaces = new QAction(tr("Select faces"), this);
1638     actionSelectFaces->setStatusTip(tr("Deselect all the objects in the current selection other than faces."));
1639     actionSelectFaces->setShortcut(QKeySequence(Qt::Key_S, Qt::Key_F));
1640     actionSelectFaces->setShortcutContext(Qt::ApplicationShortcut);
1641     connect(actionSelectFaces, SIGNAL(triggered()), scene_, SLOT(selectFaces()));
1642 
1643     // Deselect Vertices
1644     actionDeselectVertices = new QAction(tr("Deselect vertices"), this);
1645     actionDeselectVertices->setStatusTip(tr("Deselect all vertices."));
1646     actionDeselectVertices->setShortcut(QKeySequence(Qt::Key_S, Qt::SHIFT + Qt::Key_V));
1647     actionDeselectVertices->setShortcutContext(Qt::ApplicationShortcut);
1648     connect(actionDeselectVertices, SIGNAL(triggered()), scene_, SLOT(deselectVertices()));
1649 
1650     // Deselect Edges
1651     actionDeselectEdges = new QAction(tr("Deselect edges"), this);
1652     actionDeselectEdges->setStatusTip(tr("Deselect all edges."));
1653     actionDeselectEdges->setShortcut(QKeySequence(Qt::Key_S, Qt::SHIFT + Qt::Key_E));
1654     actionDeselectEdges->setShortcutContext(Qt::ApplicationShortcut);
1655     connect(actionDeselectEdges, SIGNAL(triggered()), scene_, SLOT(deselectEdges()));
1656 
1657     // Deselect Faces
1658     actionDeselectFaces = new QAction(tr("Deselect faces"), this);
1659     actionDeselectFaces->setStatusTip(tr("Deselect all faces."));
1660     actionDeselectFaces->setShortcut(QKeySequence(Qt::Key_S, Qt::SHIFT + Qt::Key_F));
1661     actionDeselectFaces->setShortcutContext(Qt::ApplicationShortcut);
1662     connect(actionDeselectFaces, SIGNAL(triggered()), scene_, SLOT(deselectFaces()));
1663 
1664     ///////////////        DEPTH        ///////////////
1665 
1666     // Raise
1667     actionRaise = new QAction(tr("Raise"), this);
1668     actionRaise->setStatusTip(tr("Raise the selected objects."));
1669     actionRaise->setShortcut(QKeySequence(Qt::Key_PageUp));
1670     actionRaise->setShortcutContext(Qt::ApplicationShortcut);
1671     connect(actionRaise, SIGNAL(triggered()), scene_, SLOT(raise()));
1672 
1673     // Lower
1674     actionLower = new QAction(tr("Lower"), this);
1675     actionLower->setStatusTip(tr("Lower the selected objects."));
1676     actionLower->setShortcut(QKeySequence(Qt::Key_PageDown));
1677     actionLower->setShortcutContext(Qt::ApplicationShortcut);
1678     connect(actionLower, SIGNAL(triggered()), scene_, SLOT(lower()));
1679 
1680     // Raise To Top
1681     actionRaiseToTop = new QAction(tr("Raise to top"), this);
1682     actionRaiseToTop->setStatusTip(tr("Raise the selected objects to the foreground."));
1683     actionRaiseToTop->setShortcut(QKeySequence(Qt::Key_Home));
1684     actionRaiseToTop->setShortcutContext(Qt::ApplicationShortcut);
1685     connect(actionRaiseToTop, SIGNAL(triggered()), scene_, SLOT(raiseToTop()));
1686 
1687     // Lower To Bottom
1688     actionLowerToBottom = new QAction(tr("Lower to bottom"), this);
1689     actionLowerToBottom->setStatusTip(tr("Lower the selected objects to the background."));
1690     actionLowerToBottom->setShortcut(QKeySequence(Qt::Key_End));
1691     actionLowerToBottom->setShortcutContext(Qt::ApplicationShortcut);
1692     connect(actionLowerToBottom, SIGNAL(triggered()), scene_, SLOT(lowerToBottom()));
1693 
1694     // Alternative Raise
1695     actionAltRaise = new QAction(tr("Alternative Raise"), this);
1696     actionAltRaise->setStatusTip(tr("Raise the selected objects, "
1697                                     "without enforcing that they stay below their boundary."));
1698     actionAltRaise->setShortcut(QKeySequence(Qt::ALT + Qt::Key_PageUp));
1699     actionAltRaise->setShortcutContext(Qt::ApplicationShortcut);
1700     connect(actionAltRaise, SIGNAL(triggered()), scene_, SLOT(altRaise()));
1701 
1702     // Alternative Lower
1703     actionAltLower = new QAction(tr("Alternative Lower"), this);
1704     actionAltLower->setStatusTip(tr("Lower the selected objects, "
1705                                     "without enforcing that they stay below their boundary."));
1706     actionAltLower->setShortcut(QKeySequence(Qt::ALT + Qt::Key_PageDown));
1707     actionAltLower->setShortcutContext(Qt::ApplicationShortcut);
1708     connect(actionAltLower, SIGNAL(triggered()), scene_, SLOT(altLower()));
1709 
1710     // Alternative Raise To Top
1711     actionAltRaiseToTop = new QAction(tr("Alternative Raise to top"), this);
1712     actionAltRaiseToTop->setStatusTip(tr("Raise the selected objects to the foreground, "
1713                                          "without enforcing that they stay below their boundary."));
1714     actionAltRaiseToTop->setShortcut(QKeySequence(Qt::ALT + Qt::Key_Home));
1715     actionAltRaiseToTop->setShortcutContext(Qt::ApplicationShortcut);
1716     connect(actionAltRaiseToTop, SIGNAL(triggered()), scene_, SLOT(altRaiseToTop()));
1717 
1718     // Alternative Lower To Bottom
1719     actionAltLowerToBottom = new QAction(tr("Alternative Lower to bottom"), this);
1720     actionAltLowerToBottom->setStatusTip(tr("Lower the selected objects to the background, "
1721                                             "without enforcing that they stay below their boundary."));
1722     actionAltLowerToBottom->setShortcut(QKeySequence(Qt::ALT + Qt::Key_End));
1723     actionAltLowerToBottom->setShortcutContext(Qt::ApplicationShortcut);
1724     connect(actionAltLowerToBottom, SIGNAL(triggered()), scene_, SLOT(altLowerToBottom()));
1725 
1726 
1727     ///////////////        ANIMATION        ///////////////
1728 
1729     // Keyframe
1730     actionKeyframeSelection = new QAction(tr("Keyframe selection"), this);
1731     actionKeyframeSelection->setStatusTip(tr("Insert a key to all selected objects at current time."));
1732     actionKeyframeSelection->setShortcut(QKeySequence(Qt::Key_K));
1733     actionKeyframeSelection->setShortcutContext(Qt::ApplicationShortcut);
1734     connect(actionKeyframeSelection, SIGNAL(triggered()), scene_, SLOT(keyframeSelection()));
1735 
1736     // Motion Paste
1737     actionMotionPaste = new QAction(tr("Motion paste"), this);
1738     actionMotionPaste->setStatusTip(tr("Paste the cells in the clipboard, and inbetween them with the copied cells."));
1739     actionMotionPaste->setShortcut(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_V));
1740     actionMotionPaste->setShortcutContext(Qt::ApplicationShortcut);
1741     connect(actionMotionPaste, SIGNAL(triggered()), this, SLOT(motionPaste()));
1742 
1743     // Inbetween
1744     actionInbetweenSelection = new QAction(tr("Inbetween selection [Beta]"), this);
1745     actionInbetweenSelection->setStatusTip(tr("Automatically create inbetweens to interpolate the selection."));
1746     actionInbetweenSelection->setShortcut(QKeySequence(Qt::Key_I));
1747     actionInbetweenSelection->setShortcutContext(Qt::ApplicationShortcut);
1748     connect(actionInbetweenSelection, SIGNAL(triggered()), scene_, SLOT(inbetweenSelection()));
1749 
1750     // Create inbetween Face
1751     actionCreateInbetweenFace = new QAction(tr("Create inbetween face [Beta]"), this);
1752     actionCreateInbetweenFace->setStatusTip(tr("Open the animated cycle editor to create a new inbetween face."));
1753     actionCreateInbetweenFace->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_F));
1754     actionCreateInbetweenFace->setShortcutContext(Qt::ApplicationShortcut);
1755     connect(actionCreateInbetweenFace, SIGNAL(triggered()), this, SLOT(createInbetweenFace()));
1756 
1757 
1758     ///////////////        HELP        ///////////////
1759 
1760     // Online Documentation
1761     actionOnlineDocumentation = new QAction(tr("Online Documentation"), this);
1762     actionOnlineDocumentation->setStatusTip(tr("Redirects you to the online documentation of VPaint."));
1763     connect(actionOnlineDocumentation, SIGNAL(triggered()), this, SLOT(onlineDocumentation()));
1764 
1765     // Getting Started
1766     actionGettingStarted = new QAction(tr("Getting Started"), this);
1767     actionGettingStarted->setStatusTip(tr("First-time user? This is for you! Learn the basics of VPaint from scratch, in a few minutes."));
1768     connect(actionGettingStarted, SIGNAL(triggered()), this, SLOT(gettingStarted()));
1769 
1770     // Manual
1771     actionManual = new QAction(tr("User Manual"), this);
1772     actionManual->setStatusTip(tr("Learn every feature of VPaint."));
1773     connect(actionManual, SIGNAL(triggered()), this, SLOT(manual()));
1774 
1775     // About
1776     actionAbout = new QAction(tr("About VPaint"), this);
1777     actionAbout->setStatusTip(tr("Information about VPaint."));
1778     connect(actionAbout, SIGNAL(triggered()), this, SLOT(about()));
1779 }
1780 
1781 
1782 
1783 /*********************************************************************
1784  *                            Menus
1785  */
1786 
1787 
createMenus()1788 void MainWindow::createMenus()
1789 {
1790     /// ---- FILE ----
1791     menuFile = new QMenu(tr("&File"));
1792     menuFile->addAction(actionNew);
1793     menuFile->addAction(actionOpen);
1794     menuFile->addSeparator();
1795     menuFile->addAction(actionSave);
1796     menuFile->addAction(actionSaveAs);
1797     menuFile->addSeparator();
1798     QMenu * importMenu = menuFile->addMenu(tr("Import")); {
1799         importMenu->addAction(actionImportSvg);
1800     }
1801     QMenu * exportMenu = menuFile->addMenu(tr("Export")); {
1802         exportMenu->addAction(actionExportPNG);
1803         exportMenu->addAction(actionExportSVG);
1804     }
1805     //menuFile->addSeparator();
1806     //menuFile->addAction(actionPreferences);
1807     menuFile->addSeparator();
1808     menuFile->addAction(actionQuit);
1809     menuBar()->addMenu(menuFile);
1810 
1811 
1812     /// ---- EDIT ----
1813     menuEdit = new QMenu(tr("&Edit"));
1814     menuEdit->addAction(actionUndo);
1815     menuEdit->addAction(actionRedo);
1816     menuEdit->addSeparator();
1817     menuEdit->addAction(actionCut);
1818     menuEdit->addAction(actionCopy);
1819     menuEdit->addAction(actionPaste);
1820     menuEdit->addSeparator();
1821     menuEdit->addAction(actionSmartDelete);
1822     menuEdit->addAction(actionHardDelete);
1823     //menuEdit->addAction(actionTest);
1824     menuBar()->addMenu(menuEdit);
1825 
1826 
1827     /// ---- VIEW ----
1828     menuView = new QMenu(tr("&View"));
1829     menuView->addAction(actionZoomIn);
1830     menuView->addAction(actionZoomOut);
1831 
1832     menuView->addSeparator();
1833     menuView->addAction(actionShowCanvas);
1834     menuView->addAction(actionEditCanvasSize);
1835 
1836     menuView->addSeparator();
1837     QMenu * displayModeMenu = menuView->addMenu(tr("Display Mode")); {
1838         displayModeMenu->addAction(actionDisplayModeNormal);
1839         displayModeMenu->addAction(actionDisplayModeNormalOutline);
1840         displayModeMenu->addAction(actionDisplayModeOutline);
1841     }
1842     menuView->addAction(actionOnionSkinning);
1843 
1844     menuView->addSeparator();
1845     menuView->addAction(actionSplitClose);
1846     menuView->addAction(actionSplitOne);
1847     menuView->addAction(actionSplitVertical);
1848     menuView->addAction(actionSplitHorizontal);
1849 
1850     menuView->addSeparator();
1851     menuView->addAction(global()->toolBar()->toggleViewAction());
1852     menuView->addAction(global()->toolModeToolBar()->toggleViewAction());
1853     menuView->addAction(dockTimeLine->toggleViewAction());
1854     menuView->addAction(dockBackgroundWidget->toggleViewAction());
1855     menuView->addAction(dockLayersWidget->toggleViewAction());
1856     advancedViewMenu = menuView->addMenu(tr("Advanced [Beta]")); {
1857         advancedViewMenu->addAction(dockInspector->toggleViewAction());
1858         advancedViewMenu->addAction(dockAdvancedSettings->toggleViewAction());
1859         advancedViewMenu->addAction(dockAnimatedCycleEditor->toggleViewAction());
1860         advancedViewMenu->addAction(actionOpenClose3D);
1861         advancedViewMenu->addAction(actionOpenCloseView3DSettings);
1862     }
1863 
1864     menuBar()->addMenu(menuView);
1865 
1866     /// ---- SELECTION ----
1867     menuSelection = new QMenu(tr("&Selection"));
1868     menuSelection->addAction(actionSelectAllInFrame);
1869     menuSelection->addAction(actionSelectAllInAnimation);
1870     menuSelection->addAction(actionDeselectAll);
1871     menuSelection->addAction(actionInvertSelection);
1872     menuSelection->addSeparator();
1873     menuSelection->addAction(actionSelectConnected);
1874     menuSelection->addAction(actionSelectClosure);
1875     menuSelection->addSeparator();
1876     menuSelection->addAction(actionSelectVertices);
1877     menuSelection->addAction(actionSelectEdges);
1878     menuSelection->addAction(actionSelectFaces);
1879     menuSelection->addAction(actionDeselectVertices);
1880     menuSelection->addAction(actionDeselectEdges);
1881     menuSelection->addAction(actionDeselectFaces);
1882     menuBar()->addMenu(menuSelection);
1883 
1884     /// ---- DEPTH ----
1885     menuDepth = new QMenu(tr("&Depth"));
1886     menuDepth->addAction(actionRaise);
1887     menuDepth->addAction(actionLower);
1888     menuDepth->addAction(actionRaiseToTop);
1889     menuDepth->addAction(actionLowerToBottom);
1890     menuDepth->addSeparator();
1891     menuDepth->addAction(actionAltRaise);
1892     menuDepth->addAction(actionAltLower);
1893     menuDepth->addAction(actionAltRaiseToTop);
1894     menuDepth->addAction(actionAltLowerToBottom);
1895     menuBar()->addMenu(menuDepth);
1896 
1897     /// ---- ANIMATION ----
1898     menuAnimation = new QMenu(tr("&Animation"));
1899     menuAnimation->addAction(actionMotionPaste);
1900     menuAnimation->addAction(actionKeyframeSelection);
1901     menuAnimation->addAction(actionInbetweenSelection);
1902     menuAnimation->addAction(actionCreateInbetweenFace);
1903     menuBar()->addMenu(menuAnimation);
1904 
1905     /// ---- PLAYBACK ----
1906     menuPlayback = new QMenu(tr("&Playback"));
1907     menuPlayback->addAction(timeline()->actionGoToFirstFrame());
1908     menuPlayback->addAction(timeline()->actionGoToPreviousFrame());
1909     menuPlayback->addAction(timeline()->actionPlayPause());
1910     menuPlayback->addAction(timeline()->actionGoToNextFrame());
1911     menuPlayback->addAction(timeline()->actionGoToLastFrame());
1912     menuBar()->addMenu(menuPlayback);
1913 
1914     /// ---- HELP ----
1915     menuHelp = new QMenu(tr("&Help"));
1916     menuHelp->addAction(actionOnlineDocumentation);
1917     //menuHelp->addAction(actionGettingStarted);
1918     //menuHelp->addAction(actionManual);
1919     //menuHelp->addSeparator();
1920     menuHelp->addAction(actionAbout);
1921     //menuHelp->addAction(actionAboutQt);
1922     menuBar()->addMenu(menuHelp);
1923 }
1924 
1925 
1926 
1927 
1928 
1929 
1930 
1931 
1932 
1933 /*********************************************************************
1934  *               Dock Windows
1935  */
1936 
createDocks()1937 void MainWindow::createDocks()
1938 {
1939     setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
1940 
1941     // ----- Settings ---------
1942 
1943     QScrollArea *dockSettings_scrollArea = new QScrollArea();
1944     dockSettings_scrollArea->setWidget(DevSettings::instance());
1945     dockSettings_scrollArea->setFrameShape(QFrame::NoFrame);
1946     dockSettings_scrollArea->setWidgetResizable(false);
1947     dockAdvancedSettings = new QDockWidget(tr("Advanced Settings [Beta]"));
1948     dockAdvancedSettings->setAllowedAreas(Qt::LeftDockWidgetArea |
1949                                   Qt::RightDockWidgetArea);
1950     dockAdvancedSettings->setWidget(dockSettings_scrollArea);
1951     addDockWidget(Qt::RightDockWidgetArea, dockAdvancedSettings);
1952     dockAdvancedSettings->hide();
1953 
1954     // ----- Object Properties ---------
1955 
1956     // Widget
1957     inspector = new ObjectPropertiesWidget();
1958 
1959     // Scroll area
1960     QScrollArea * dockObjectProperties_scrollArea = new QScrollArea();
1961     dockObjectProperties_scrollArea->setWidget(inspector);
1962     dockObjectProperties_scrollArea->setWidgetResizable(true);
1963 
1964     // Dock
1965     dockInspector = new QDockWidget(tr("Inspector [Beta]"));
1966     dockInspector->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1967     dockInspector->setWidget(dockObjectProperties_scrollArea);
1968     addDockWidget(Qt::RightDockWidgetArea, dockInspector);
1969     dockInspector->hide();
1970 
1971     // Signal/Slot connection
1972     connect(scene(),SIGNAL(selectionChanged()),this,SLOT(updateObjectProperties()));
1973 
1974     // ----- Animated cycle editor ---------
1975 
1976     // Widget
1977     animatedCycleEditor = new AnimatedCycleWidget();
1978 
1979     // Dock
1980     dockAnimatedCycleEditor = new QDockWidget(tr("Animated Cycle Editor [Beta]"));
1981     dockAnimatedCycleEditor->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1982     dockAnimatedCycleEditor->setWidget(animatedCycleEditor);
1983     addDockWidget(Qt::RightDockWidgetArea, dockAnimatedCycleEditor);
1984     dockAnimatedCycleEditor->hide();
1985 
1986     // ----- Background ---------
1987 
1988     // Dock
1989     dockBackgroundWidget = new QDockWidget(tr("Background"));
1990     dockBackgroundWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
1991     dockBackgroundWidget->setWidget(backgroundWidget);
1992     addDockWidget(Qt::RightDockWidgetArea, dockBackgroundWidget);
1993     //dockBackgroundWidget->hide(); todo: uncomment (commented for convenience while developing)
1994 
1995     // ----- Layers ---------
1996 
1997     // Widget
1998     layersWidget = new LayersWidget(scene());
1999 
2000     // Dock
2001     dockLayersWidget = new QDockWidget(tr("Layers"));
2002     dockLayersWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea);
2003     dockLayersWidget->setWidget(layersWidget);
2004     addDockWidget(Qt::RightDockWidgetArea, dockLayersWidget);
2005 
2006     // ----- TimeLine -------------
2007 
2008     dockTimeLine = new QDockWidget(tr("Timeline"));
2009     dockTimeLine->setWidget(timeline_);
2010     dockTimeLine->setAllowedAreas(Qt::BottomDockWidgetArea);
2011     dockTimeLine->setFeatures(QDockWidget::DockWidgetClosable);
2012     dockTimeLine->setTitleBarWidget(new QWidget());
2013     addDockWidget(Qt::BottomDockWidgetArea, dockTimeLine);
2014 }
2015 
2016 
2017 
2018 
2019 /*********************************************************************
2020  *                          Status Bar
2021  */
2022 
createStatusBar()2023 void MainWindow::createStatusBar()
2024 {
2025       //statusBar()->showMessage(tr("Hello! How are you doing today?"),2000);
2026 }
2027 
2028 
2029 
2030 
2031 /*********************************************************************
2032  *                           Toolbars
2033  */
2034 
2035 
createToolbars()2036 void MainWindow::createToolbars()
2037 {
2038     global()->createToolBars();
2039 }
2040