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