All Classes Namespaces Functions Variables Enumerations Properties Pages
mainwindow2.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2008-2009 Mj Mendoza IV
6 Copyright (C) 2012-2020 Matthew Chiawen Chang
7 
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; version 2 of the License.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 */
18 
19 #include "mainwindow2.h"
20 #include "ui_mainwindow2.h"
21 
22 // Qt headers
23 #include <QDir>
24 #include <QList>
25 #include <QMenu>
26 #include <QFile>
27 #include <QMessageBox>
28 #include <QProgressDialog>
29 #include <QTabletEvent>
30 #include <QStandardPaths>
31 #include <QDateTime>
32 #include <QLabel>
33 #include <QClipboard>
34 #include <QToolBar>
35 
36 // core_lib headers
37 #include "pencildef.h"
38 #include "pencilsettings.h"
39 #include "object.h"
40 #include "editor.h"
41 
42 #include "filemanager.h"
43 #include "colormanager.h"
44 #include "layermanager.h"
45 #include "toolmanager.h"
46 #include "playbackmanager.h"
47 #include "selectionmanager.h"
48 #include "soundmanager.h"
49 #include "viewmanager.h"
50 #include "selectionmanager.h"
51 
52 #include "actioncommands.h"
53 #include "fileformat.h" //contains constants used by Pencil File Format
54 #include "util.h"
55 #include "backupelement.h"
56 
57 // app headers
58 #include "colorbox.h"
59 #include "colorinspector.h"
60 #include "colorpalettewidget.h"
61 #include "displayoptionwidget.h"
62 #include "tooloptionwidget.h"
63 #include "preferencesdialog.h"
64 #include "timeline.h"
65 #include "toolbox.h"
66 #include "onionskinwidget.h"
67 #include "pegbaralignmentdialog.h"
68 #include "repositionframesdialog.h"
69 
70 //#include "preview.h"
71 //#include "timeline2.h"
72 #include "errordialog.h"
73 #include "filedialog.h"
74 #include "importimageseqdialog.h"
75 #include "importlayersdialog.h"
76 #include "importpositiondialog.h"
77 #include "layeropacitydialog.h"
78 #include "recentfilemenu.h"
79 #include "shortcutfilter.h"
80 #include "app_util.h"
81 #include "presetdialog.h"
82 #include "pegbaralignmentdialog.h"
83 
84 
85 #ifdef GIT_TIMESTAMP
86 #define BUILD_DATE S__GIT_TIMESTAMP
87 #else
88 #define BUILD_DATE __DATE__
89 #endif
90 
91 #if defined(PENCIL2D_RELEASE_BUILD)
92 #define PENCIL_WINDOW_TITLE QString("[*]Pencil2D v%1").arg(APP_VERSION)
93 #elif defined(PENCIL2D_NIGHTLY_BUILD)
94 #define PENCIL_WINDOW_TITLE QString("[*]Pencil2D Nightly Build %1").arg(BUILD_DATE)
95 #else
96 #define PENCIL_WINDOW_TITLE QString("[*]Pencil2D Development Build %1").arg(BUILD_DATE)
97 #endif
98 
99 
100 
101 MainWindow2::MainWindow2(QWidget* parent) :
102  QMainWindow(parent),
103  ui(new Ui::MainWindow2)
104 {
105  ui->setupUi(this);
106 
107  // Initialize order
108  // 1. editor 2. object 3. scribble area 4. other widgets
109  mEditor = new Editor(this);
110  mEditor->setScribbleArea(ui->scribbleArea);
111  mEditor->init();
112 
113  newObject();
114 
115  ui->scribbleArea->setEditor(mEditor);
116  ui->scribbleArea->init();
117 
118  ui->statusBar->setEditor(mEditor);
119  ui->statusBar->updateZoomStatus();
120  ui->statusBar->setVisible(mEditor->preference()->isOn(SETTING::SHOW_STATUS_BAR));
121 
122  mCommands = new ActionCommands(this);
123  mCommands->setCore(mEditor);
124 
125  createDockWidgets();
126  createMenus();
127  createToolbars();
128  setupKeyboardShortcuts();
129 
130  readSettings();
131 
132  selectionChanged();
133 
134  connect(mEditor, &Editor::needSave, this, &MainWindow2::autoSave);
135  connect(mToolBox, &ToolBoxWidget::clearButtonClicked, mEditor, &Editor::clearCurrentFrame);
136 
137  mEditor->tools()->setDefaultTool();
138  ui->background->init(mEditor->preference());
139 
140  setWindowTitle(PENCIL_WINDOW_TITLE);
141 }
142 
143 MainWindow2::~MainWindow2()
144 {
145  delete ui;
146 }
147 
148 void MainWindow2::createDockWidgets()
149 {
150  mTimeLine = new TimeLine(this);
151  mTimeLine->setObjectName("TimeLine");
152 
153  mColorBox = new ColorBox(this);
154  mColorBox->setToolTip(tr("color palette:<br>use <b>(C)</b><br>toggle at cursor"));
155  mColorBox->setObjectName("ColorWheel");
156 
157  mColorInspector = new ColorInspector(this);
158  mColorInspector->setToolTip(tr("Color inspector"));
159  mColorInspector->setObjectName("Color Inspector");
160 
161  mColorPalette = new ColorPaletteWidget(this);
162  mColorPalette->setCore(mEditor);
163  mColorPalette->setObjectName("ColorPalette");
164 
165  mDisplayOptionWidget = new DisplayOptionWidget(this);
166  mDisplayOptionWidget->setObjectName("DisplayOption");
167 
168  mOnionSkinWidget = new OnionSkinWidget(this);
169  mOnionSkinWidget->setObjectName("Onion Skin");
170 
171  mToolOptions = new ToolOptionWidget(this);
172  mToolOptions->setObjectName("ToolOption");
173 
174  mToolBox = new ToolBoxWidget(this);
175  mToolBox->setObjectName("ToolBox");
176 
177  /*
178  mTimeline2 = new Timeline2;
179  mTimeline2->setObjectName( "Timeline2" );
180  mDockWidgets.append( mTimeline2 );
181  */
182 
183  mDockWidgets
184  << mTimeLine
185  << mColorBox
186  << mColorInspector
187  << mColorPalette
188  << mDisplayOptionWidget
189  << mOnionSkinWidget
190  << mToolOptions
191  << mToolBox;
192 
193  mStartIcon = QIcon(":icons/controls/play.png");
194  mStopIcon = QIcon(":icons/controls/stop.png");
195 
196  for (BaseDockWidget* pWidget : mDockWidgets)
197  {
198  pWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
199  pWidget->setFeatures(QDockWidget::AllDockWidgetFeatures);
200  pWidget->setFocusPolicy(Qt::NoFocus);
201 
202  pWidget->setEditor(mEditor);
203  pWidget->initUI();
204  pWidget->show();
205  qDebug() << "Init Dock widget: " << pWidget->objectName();
206  }
207 
209  addDockWidget(Qt::RightDockWidgetArea, mColorInspector);
210  addDockWidget(Qt::RightDockWidgetArea, mColorPalette);
212  addDockWidget(Qt::LeftDockWidgetArea, mToolOptions);
213  addDockWidget(Qt::LeftDockWidgetArea, mDisplayOptionWidget);
214  addDockWidget(Qt::LeftDockWidgetArea, mOnionSkinWidget);
216  setDockNestingEnabled(true);
217 
218  /*
219  mPreview = new PreviewWidget( this );
220  mPreview->setImage( mScribbleArea->mBufferImg );
221  mPreview->setFeatures( QDockWidget::DockWidgetFloatable );
222  mPreview->setFocusPolicy( Qt::NoFocus );
223  addDockWidget( Qt::RightDockWidgetArea, mPreview );
224  */
225 
226  makeConnections(mEditor, ui->scribbleArea);
227  makeConnections(mEditor);
228  makeConnections(mEditor, mTimeLine);
229  makeConnections(mEditor, mColorBox);
230  makeConnections(mEditor, mColorInspector);
231  makeConnections(mEditor, mColorPalette);
232  makeConnections(mEditor, mToolOptions);
233  makeConnections(mEditor, mDisplayOptionWidget);
234  makeConnections(mEditor, ui->statusBar);
235 
236  for (BaseDockWidget* w : mDockWidgets)
237  {
238  w->updateUI();
239  w->setFloating(false);
240  }
241 }
242 
243 void MainWindow2::createMenus()
244 {
245  //--- File Menu ---
246  connect(ui->actionNew, &QAction::triggered, this, &MainWindow2::newDocument);
247  connect(ui->actionOpen, &QAction::triggered, this, &MainWindow2::openDocument);
248  connect(ui->actionSave_as, &QAction::triggered, this, &MainWindow2::saveAsNewDocument);
249  connect(ui->actionSave, &QAction::triggered, this, &MainWindow2::saveDocument);
250  connect(ui->actionExit, &QAction::triggered, this, &MainWindow2::close);
251 
252  //--- Export Menu ---
253  connect(ui->actionExport_Image, &QAction::triggered, mCommands, &ActionCommands::exportImage);
254  connect(ui->actionExport_ImageSeq, &QAction::triggered, mCommands, &ActionCommands::exportImageSequence);
255  connect(ui->actionExport_Movie, &QAction::triggered, mCommands, &ActionCommands::exportMovie);
256  connect(ui->actionExport_Animated_GIF, &QAction::triggered, mCommands, &ActionCommands::exportGif);
257 
258  connect(ui->actionExport_Palette, &QAction::triggered, this, &MainWindow2::exportPalette);
259 
260  //--- Import Menu ---
261  //connect( ui->actionExport_Svg_Image, &QAction::triggered, editor, &Editor::saveSvg );
262  connect(ui->actionImport_Image, &QAction::triggered, this, &MainWindow2::importImage);
263  connect(ui->actionImport_ImageSeq, &QAction::triggered, this, &MainWindow2::importImageSequence);
264  connect(ui->actionImport_ImageSeqNum, &QAction::triggered, this, &MainWindow2::importPredefinedImageSet);
265  connect(ui->actionImportLayers_from_pclx, &QAction::triggered, this, &MainWindow2::importLayers);
266  connect(ui->actionImport_MovieVideo, &QAction::triggered, this, &MainWindow2::importMovieVideo);
267  connect(ui->actionImport_Gif, &QAction::triggered, this, &MainWindow2::importGIF);
268 
269  connect(ui->actionImport_Sound, &QAction::triggered, [=] { mCommands->importSound(FileType::SOUND); });
270  connect(ui->actionImport_MovieAudio, &QAction::triggered, [=] { mCommands->importSound(FileType::MOVIE); });
271 
272  connect(ui->actionImport_Append_Palette, &QAction::triggered, this, &MainWindow2::importPalette);
273  connect(ui->actionImport_Replace_Palette, &QAction::triggered, this, &MainWindow2::openPalette);
274 
275  //--- Edit Menu ---
276  connect(mEditor, &Editor::updateBackup, this, &MainWindow2::undoActSetText);
277  connect(ui->actionUndo, &QAction::triggered, mEditor, &Editor::undo);
278  connect(ui->actionRedo, &QAction::triggered, mEditor, &Editor::redo);
279  connect(ui->actionCut, &QAction::triggered, mEditor, &Editor::copyAndCut);
280  connect(ui->actionCopy, &QAction::triggered, mEditor, &Editor::copy);
281  connect(ui->actionPaste_Previous, &QAction::triggered, mEditor, &Editor::pasteFromPreviousFrame);
282  connect(ui->actionPaste, &QAction::triggered, mEditor, &Editor::paste);
283  connect(ui->actionClearFrame, &QAction::triggered, mEditor, &Editor::clearCurrentFrame);
284  connect(mEditor->select(), &SelectionManager::selectionChanged, this, &MainWindow2::selectionChanged);
285  connect(ui->actionFlip_X, &QAction::triggered, mCommands, &ActionCommands::flipSelectionX);
286  connect(ui->actionFlip_Y, &QAction::triggered, mCommands, &ActionCommands::flipSelectionY);
287  connect(ui->actionPegbarAlignment, &QAction::triggered, this, &MainWindow2::openPegAlignDialog);
288  connect(ui->actionSelect_All, &QAction::triggered, mCommands, &ActionCommands::selectAll);
289  connect(ui->actionDeselect_All, &QAction::triggered, mCommands, &ActionCommands::deselectAll);
290  connect(ui->actionReposition_Selected_Frames, &QAction::triggered, this, &MainWindow2::openRepositionDialog);
291  connect(ui->actionPreference, &QAction::triggered, [=] { preferences(); });
292 
293  //--- Layer Menu ---
294  connect(ui->actionNew_Bitmap_Layer, &QAction::triggered, mCommands, &ActionCommands::addNewBitmapLayer);
295  connect(ui->actionNew_Vector_Layer, &QAction::triggered, mCommands, &ActionCommands::addNewVectorLayer);
296  connect(ui->actionNew_Sound_Layer, &QAction::triggered, mCommands, &ActionCommands::addNewSoundLayer);
297  connect(ui->actionNew_Camera_Layer, &QAction::triggered, mCommands, &ActionCommands::addNewCameraLayer);
298  connect(ui->actionDelete_Current_Layer, &QAction::triggered, mCommands, &ActionCommands::deleteCurrentLayer);
299  connect(ui->actionChangeLineColorCurrent_keyframe, &QAction::triggered, mCommands, &ActionCommands::changeKeyframeLineColor);
300  connect(ui->actionChangeLineColorAll_keyframes_on_layer, &QAction::triggered, mCommands, &ActionCommands::changeallKeyframeLineColor);
301  connect(ui->actionChangeLayerOpacity, &QAction::triggered, this, &MainWindow2::openLayerOpacityDialog);
302 
303  QList<QAction*> visibilityActions = ui->menuLayer_Visibility->actions();
304  auto visibilityGroup = new QActionGroup(this);
305  visibilityGroup->setExclusive(true);
306  for (int i = 0; i < visibilityActions.size(); i++) {
307  QAction* action = visibilityActions[i];
308  visibilityGroup->addAction(action);
309  connect(action, &QAction::triggered, [=] { mCommands->setLayerVisibilityIndex(i); });
310  }
311  visibilityActions[mEditor->preference()->getInt(SETTING::LAYER_VISIBILITY)]->setChecked(true);
312  connect(mEditor->preference(), &PreferenceManager::optionChanged, [=](SETTING e) {
313  if (e == SETTING::LAYER_VISIBILITY) {
314  visibilityActions[mEditor->preference()->getInt(SETTING::LAYER_VISIBILITY)]->setChecked(true);
315  }
316  });
317 
318  // --- View Menu ---
319  connect(ui->actionZoom_In, &QAction::triggered, mCommands, &ActionCommands::ZoomIn);
320  connect(ui->actionZoom_Out, &QAction::triggered, mCommands, &ActionCommands::ZoomOut);
321  connect(ui->actionRotate_Clockwise, &QAction::triggered, mCommands, &ActionCommands::rotateClockwise);
322  connect(ui->actionRotate_Anticlockwise, &QAction::triggered, mCommands, &ActionCommands::rotateCounterClockwise);
323  connect(ui->actionReset_Rotation, &QAction::triggered, mEditor->view(), &ViewManager::resetRotation);
324  connect(ui->actionReset_View, &QAction::triggered, mEditor->view(), &ViewManager::resetView);
325  connect(ui->actionCenter_View, &QAction::triggered, mEditor->view(), &ViewManager::centerView);
326  connect(ui->actionZoom400, &QAction::triggered, mEditor->view(), &ViewManager::scale400);
327  connect(ui->actionZoom300, &QAction::triggered, mEditor->view(), &ViewManager::scale300);
328  connect(ui->actionZoom200, &QAction::triggered, mEditor->view(), &ViewManager::scale200);
329  connect(ui->actionZoom100, &QAction::triggered, mEditor->view(), &ViewManager::scale100);
330  connect(ui->actionZoom50, &QAction::triggered, mEditor->view(), &ViewManager::scale50);
331  connect(ui->actionZoom33, &QAction::triggered, mEditor->view(), &ViewManager::scale33);
332  connect(ui->actionZoom25, &QAction::triggered, mEditor->view(), &ViewManager::scale25);
333  connect(ui->actionHorizontal_Flip, &QAction::triggered, mEditor->view(), &ViewManager::flipHorizontal);
334  connect(ui->actionVertical_Flip, &QAction::triggered, mEditor->view(), &ViewManager::flipVertical);
335  connect(mEditor->view(), &ViewManager::viewFlipped, this, &MainWindow2::viewFlipped);
336 
337  PreferenceManager* prefs = mEditor->preference();
338  connect(ui->actionStatusBar, &QAction::triggered, ui->statusBar, &QStatusBar::setVisible);
339  bindPreferenceSetting(ui->actionStatusBar, prefs, SETTING::SHOW_STATUS_BAR);
340  bindPreferenceSetting(ui->actionGrid, prefs, SETTING::GRID);
341  bindPreferenceSetting(ui->actionOnionPrev, prefs, SETTING::PREV_ONION);
342  bindPreferenceSetting(ui->actionOnionNext, prefs, SETTING::NEXT_ONION);
343 
344  //--- Animation Menu ---
345  PlaybackManager* pPlaybackManager = mEditor->playback();
346  connect(ui->actionPlay, &QAction::triggered, mCommands, &ActionCommands::PlayStop);
347 
348  connect(ui->actionLoop, &QAction::triggered, pPlaybackManager, &PlaybackManager::setLooping);
349  connect(ui->actionLoopControl, &QAction::triggered, pPlaybackManager, &PlaybackManager::enableRangedPlayback);
350  connect(pPlaybackManager, &PlaybackManager::loopStateChanged, ui->actionLoop, &QAction::setChecked);
351  connect(pPlaybackManager, &PlaybackManager::loopStateChanged, mTimeLine, &TimeLine::setLoop);
352  connect(pPlaybackManager, &PlaybackManager::rangedPlaybackStateChanged, ui->actionLoopControl, &QAction::setChecked);
353  connect(pPlaybackManager, &PlaybackManager::rangedPlaybackStateChanged, mTimeLine, &TimeLine::setRangeState);
354  connect(pPlaybackManager, &PlaybackManager::playStateChanged, mTimeLine, &TimeLine::setPlaying);
355  connect(pPlaybackManager, &PlaybackManager::playStateChanged, this, &MainWindow2::changePlayState);
356  connect(pPlaybackManager, &PlaybackManager::playStateChanged, ui->scribbleArea, &ScribbleArea::onPlayStateChanged);
357  connect(ui->actionFlip_inbetween, &QAction::triggered, pPlaybackManager, &PlaybackManager::playFlipInBetween);
358  connect(ui->actionFlip_rolling, &QAction::triggered, pPlaybackManager, &PlaybackManager::playFlipRoll);
359 
360  connect(ui->actionAdd_Frame, &QAction::triggered, mCommands, &ActionCommands::insertKeyFrameAtCurrentPosition);
361  connect(ui->actionRemove_Frame, &QAction::triggered, mCommands, &ActionCommands::removeKey);
362  connect(ui->actionAdd_Frame_Exposure, &QAction::triggered, mCommands, &ActionCommands::addExposureToSelectedFrames);
363  connect(ui->actionSubtract_Frame_Exposure, &QAction::triggered, mCommands, &ActionCommands::subtractExposureFromSelectedFrames);
364  connect(ui->actionNext_Frame, &QAction::triggered, mCommands, &ActionCommands::GotoNextFrame);
365  connect(ui->actionPrevious_Frame, &QAction::triggered, mCommands, &ActionCommands::GotoPrevFrame);
366  connect(ui->actionNext_KeyFrame, &QAction::triggered, mCommands, &ActionCommands::GotoNextKeyFrame);
367  connect(ui->actionPrev_KeyFrame, &QAction::triggered, mCommands, &ActionCommands::GotoPrevKeyFrame);
368  connect(ui->actionDuplicate_Frame, &QAction::triggered, mCommands, &ActionCommands::duplicateKey);
369  connect(ui->actionMove_Frame_Forward, &QAction::triggered, mCommands, &ActionCommands::moveFrameForward);
370  connect(ui->actionMove_Frame_Backward, &QAction::triggered, mCommands, &ActionCommands::moveFrameBackward);
371 
372  connect(ui->actionReverse_Frames_Order, &QAction::triggered, mCommands, &ActionCommands::reverseSelectedFrames);
373  connect(ui->actionRemove_Frames, &QAction::triggered, mCommands, &ActionCommands::removeSelectedFrames);
374 
375  //--- Tool Menu ---
376  connect(ui->actionMove, &QAction::triggered, mToolBox, &ToolBoxWidget::moveOn);
377  connect(ui->actionSelect, &QAction::triggered, mToolBox, &ToolBoxWidget::selectOn);
378  connect(ui->actionBrush, &QAction::triggered, mToolBox, &ToolBoxWidget::brushOn);
379  connect(ui->actionPolyline, &QAction::triggered, mToolBox, &ToolBoxWidget::polylineOn);
380  connect(ui->actionSmudge, &QAction::triggered, mToolBox, &ToolBoxWidget::smudgeOn);
381  connect(ui->actionPen, &QAction::triggered, mToolBox, &ToolBoxWidget::penOn);
382  connect(ui->actionHand, &QAction::triggered, mToolBox, &ToolBoxWidget::handOn);
383  connect(ui->actionPencil, &QAction::triggered, mToolBox, &ToolBoxWidget::pencilOn);
384  connect(ui->actionBucket, &QAction::triggered, mToolBox, &ToolBoxWidget::bucketOn);
385  connect(ui->actionEyedropper, &QAction::triggered, mToolBox, &ToolBoxWidget::eyedropperOn);
386  connect(ui->actionEraser, &QAction::triggered, mToolBox, &ToolBoxWidget::eraserOn);
387  connect(ui->actionResetToolsDefault, &QAction::triggered, mEditor->tools(), &ToolManager::resetAllTools);
388 
389  //--- Window Menu ---
390  QMenu* winMenu = ui->menuWindows;
391  const std::vector<QAction*> actions
392  {
393  mToolBox->toggleViewAction(),
394  mToolOptions->toggleViewAction(),
395  mColorBox->toggleViewAction(),
396  mColorPalette->toggleViewAction(),
397  mTimeLine->toggleViewAction(),
398  mDisplayOptionWidget->toggleViewAction(),
399  mColorInspector->toggleViewAction(),
400  mOnionSkinWidget->toggleViewAction()
401  };
402 
403  for (QAction* action : actions)
404  {
405  action->setMenuRole(QAction::NoRole);
406  winMenu->addAction(action);
407  }
408  connect(ui->actionResetWindows, &QAction::triggered, this, &MainWindow2::resetAndDockAllSubWidgets);
409  connect(ui->actionLockWindows, &QAction::toggled, this, &MainWindow2::lockWidgets);
410  bindPreferenceSetting(ui->actionLockWindows, prefs, SETTING::LAYOUT_LOCK);
411 
412  //--- Help Menu ---
413  connect(ui->actionHelp, &QAction::triggered, mCommands, &ActionCommands::help);
414  connect(ui->actionQuick_Guide, &QAction::triggered, mCommands, &ActionCommands::quickGuide);
415  connect(ui->actionWebsite, &QAction::triggered, mCommands, &ActionCommands::website);
416  connect(ui->actionForum, &QAction::triggered, mCommands, &ActionCommands::forum);
417  connect(ui->actionDiscord, &QAction::triggered, mCommands, &ActionCommands::discord);
418  connect(ui->actionCheck_for_Updates, &QAction::triggered, mCommands, &ActionCommands::checkForUpdates);
419  connect(ui->actionReport_Bug, &QAction::triggered, mCommands, &ActionCommands::reportbug);
420  connect(ui->actionOpen_Temporary_Directory, &QAction::triggered, mCommands, &ActionCommands::openTemporaryDirectory);
421  connect(ui->actionAbout, &QAction::triggered, mCommands, &ActionCommands::about);
422 
423  //--- Menus ---
424  mRecentFileMenu = new RecentFileMenu(tr("Open Recent"), this);
425  mRecentFileMenu->loadFromDisk();
426  ui->menuFile->insertMenu(ui->actionSave, mRecentFileMenu);
427 
428  connect(mRecentFileMenu, &RecentFileMenu::loadRecentFile, this, &MainWindow2::openFile);
429 }
430 
431 void MainWindow2::setOpacity(int opacity)
432 {
433  mEditor->preference()->set(SETTING::WINDOW_OPACITY, 100 - opacity);
434  setWindowOpacity(opacity / 100.0);
435 }
436 
437 void MainWindow2::updateSaveState()
438 {
439  const bool hasUnsavedChanges = mEditor->currentBackup() != mBackupAtSave;
440  setWindowModified(hasUnsavedChanges);
441  ui->statusBar->updateModifiedStatus(hasUnsavedChanges);
442 }
443 
444 void MainWindow2::openPegAlignDialog()
445 {
446  if (mPegAlign != nullptr)
447  {
448  QMessageBox::information(this, nullptr,
449  tr("Dialog is already open!"),
451  return;
452  }
453 
454  mPegAlign = new PegBarAlignmentDialog(mEditor, this);
456 
457  Qt::WindowFlags flags = mPegAlign->windowFlags();
458  flags |= Qt::WindowStaysOnTopHint;
459  flags &= (~Qt::WindowContextHelpButtonHint);
460  mPegAlign->setWindowFlags(flags);
461  mPegAlign->show();
463  {
464  mPegAlign = nullptr;
465  });
466 }
467 
468 void MainWindow2::openLayerOpacityDialog()
469 {
470  if (mLayerOpacityDialog != nullptr)
471  {
472  QMessageBox::information(this, nullptr,
473  tr("Dialog is already open!"),
475  return;
476  }
477  mLayerOpacityDialog = new LayerOpacityDialog(this);
478  mLayerOpacityDialog->setAttribute(Qt::WA_DeleteOnClose);
479  mLayerOpacityDialog->setCore(mEditor);
480  mLayerOpacityDialog->initUI();
481  mLayerOpacityDialog->setWindowFlags(mLayerOpacityDialog->windowFlags() | Qt::WindowStaysOnTopHint);
482  mLayerOpacityDialog->show();
483 
484  connect(mLayerOpacityDialog, &LayerOpacityDialog::finished, [=]
485  {
486  mLayerOpacityDialog = nullptr;
487  });
488 }
489 
490 void MainWindow2::openRepositionDialog()
491 {
492  if (mEditor->layers()->currentLayer()->getSelectedFramesByPos().count() < 2)
493  {
494  QMessageBox::information(this, nullptr,
495  tr("Please select at least 2 frames!"),
497  return;
498  }
499  if (mReposDialog != nullptr)
500  {
501  return;
502  }
503 
504  mReposDialog = new RepositionFramesDialog(this);
505  mReposDialog->setAttribute(Qt::WA_DeleteOnClose);
507  hideQuestionMark(*mReposDialog);
508  mReposDialog->setCore(mEditor);
509  mReposDialog->initUI();
510  mEditor->tools()->setCurrentTool(ToolType::MOVE);
511  connect(mReposDialog, &RepositionFramesDialog::finished, this, &MainWindow2::closeRepositionDialog);
512  mReposDialog->show();
513 }
514 
515 void MainWindow2::closeRepositionDialog()
516 {
517  selectionChanged();
518  mReposDialog = nullptr;
519 }
520 
521 void MainWindow2::currentLayerChanged()
522 {
523  bool isBitmap = (mEditor->layers()->currentLayer()->type() == Layer::BITMAP);
524  ui->menuChange_line_color->setEnabled(isBitmap);
525 }
526 
527 void MainWindow2::selectionChanged()
528 {
529  bool somethingSelected = mEditor->select()->somethingSelected();
530  ui->menuSelection->setEnabled(somethingSelected);
531 }
532 
533 void MainWindow2::viewFlipped()
534 {
535  ui->actionHorizontal_Flip->setChecked(mEditor->view()->isFlipHorizontal());
536  ui->actionVertical_Flip->setChecked(mEditor->view()->isFlipVertical());
537 }
538 
539 void MainWindow2::closeEvent(QCloseEvent* event)
540 {
541  if (m2ndCloseEvent)
542  {
543  // https://bugreports.qt.io/browse/QTBUG-43344
544  event->accept();
545  return;
546  }
547 
548  if (!maybeSave())
549  {
550  event->ignore();
551  return;
552  }
553  writeSettings();
554  event->accept();
555  m2ndCloseEvent = true;
556 }
557 
558 void MainWindow2::tabletEvent(QTabletEvent* event)
559 {
560  event->ignore();
561 }
562 
563 void MainWindow2::newDocument()
564 {
565  if (maybeSave() && !tryLoadPreset())
566  {
567  newObject();
568  }
569 }
570 
571 void MainWindow2::openDocument()
572 {
573  if (!maybeSave())
574  {
575  return;
576  }
577  QString fileName = FileDialog::getOpenFileName(this, FileType::ANIMATION);
578  if (!fileName.isEmpty())
579  {
580  openObject(fileName);
581  }
582 }
583 
584 bool MainWindow2::saveAsNewDocument()
585 {
586  QString fileName = FileDialog::getSaveFileName(this, FileType::ANIMATION);
587  if (fileName.isEmpty())
588  {
589  return false;
590  }
591  return saveObject(fileName);
592 }
593 
594 void MainWindow2::openStartupFile(const QString& filename)
595 {
596  if (tryRecoverUnsavedProject())
597  {
598  return;
599  }
600 
601  if (!filename.isEmpty() && openObject(filename))
602  {
603  return;
604  }
605 
606  loadMostRecent() || tryLoadPreset();
607 }
608 
609 void MainWindow2::openFile(const QString& filename)
610 {
611  if (maybeSave())
612  {
613  openObject(filename);
614  }
615 }
616 
617 bool MainWindow2::openObject(const QString& strFilePath)
618 {
619  QProgressDialog progress(tr("Opening document..."), tr("Abort"), 0, 100, this);
620  hideQuestionMark(progress);
621  progress.setWindowModality(Qt::WindowModal);
622  progress.show();
623 
624  Status s = mEditor->openObject(strFilePath, [&progress](int p)
625  {
626  progress.setValue(p);
628  }, [&progress](int max)
629  {
630  progress.setRange(0, max);
631  });
632 
633  if (!s.ok())
634  {
635  ErrorDialog errorDialog(s.title(), s.description(), s.details().str());
636  errorDialog.exec();
637  emptyDocumentWhenErrorOccurred();
638  return false;
639  }
640 
641  QSettings settings(PENCIL2D, PENCIL2D);
642  settings.setValue(LAST_PCLX_PATH, mEditor->object()->filePath());
643 
644  // Add to recent file list, but only if we are
645  if (!mEditor->object()->filePath().isEmpty())
646  {
647  mRecentFileMenu->addRecentFile(mEditor->object()->filePath());
648  mRecentFileMenu->saveToDisk();
649  }
650 
651  closeDialogs();
652 
653  setWindowTitle(mEditor->object()->filePath().prepend("[*]"));
654  setWindowModified(false);
655  ui->statusBar->updateModifiedStatus(false);
656 
657  progress.setValue(progress.maximum());
658 
659  updateSaveState();
660  undoActSetText();
661 
662  if (!QFileInfo(strFilePath).isWritable())
663  {
664  QMessageBox::warning(this, tr("Warning"),
665  tr("This program does not currently have permission to write to the file you have selected. "
666  "Please make sure you have write permission for this file before attempting to save it. "
667  "Alternatively, you can use the Save As... menu option to save to a writable location."),
669  }
670 
671  return true;
672 }
673 
674 bool MainWindow2::saveObject(QString strSavedFileName)
675 {
676  QProgressDialog progress(tr("Saving document..."), tr("Abort"), 0, 100, this);
677  hideQuestionMark(progress);
678  progress.setWindowModality(Qt::WindowModal);
679  progress.show();
680 
681  mEditor->prepareSave();
682 
683  FileManager fm(this);
684 
685  connect(&fm, &FileManager::progressChanged, [&progress](int p)
686  {
687  progress.setValue(p);
689  });
690  connect(&fm, &FileManager::progressRangeChanged, [&progress](int max)
691  {
692  progress.setRange(0, max + 3);
693  });
694 
695  Status st = fm.save(mEditor->object(), strSavedFileName);
696 
697 
698  if (!st.ok())
699  {
700 #if QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)
702 #else
704 #endif
705  errorLogFolder.mkpath("./logs");
706  errorLogFolder.cd("logs");
707 
709  dt.setTimeSpec(Qt::UTC);
710  QFile eLog(errorLogFolder.absoluteFilePath(QString("error-%1.txt").arg(dt.toString(Qt::ISODate))));
711  if (eLog.open(QIODevice::WriteOnly | QIODevice::Text))
712  {
713  QTextStream out(&eLog);
714  out << st.details().str();
715  }
716  eLog.close();
717 
718  ErrorDialog errorDialog(st.title(),
719  st.description().append(tr("<br><br>An error has occurred and your file may not have saved successfully."
720  "If you believe that this error is an issue with Pencil2D, please create a new issue at:"
721  "<br><a href='https://github.com/pencil2d/pencil/issues'>https://github.com/pencil2d/pencil/issues</a><br>"
722  "Please be sure to include the following details in your issue:")), st.details().html());
723  errorDialog.exec();
724  return false;
725  }
726 
727  mEditor->object()->setFilePath(strSavedFileName);
728  mEditor->object()->setModified(false);
729 
730  mEditor->clearTemporary();
731 
732  QSettings settings(PENCIL2D, PENCIL2D);
733  settings.setValue(LAST_PCLX_PATH, strSavedFileName);
734 
735  mRecentFileMenu->addRecentFile(strSavedFileName);
736  mRecentFileMenu->saveToDisk();
737 
738  mTimeLine->updateContent();
739 
740  setWindowTitle(strSavedFileName.prepend("[*]"));
741  mBackupAtSave = mEditor->currentBackup();
742  updateSaveState();
743 
744  progress.setValue(progress.maximum());
745 
746  mEditor->resetAutoSaveCounter();
747 
748  return true;
749 }
750 
751 bool MainWindow2::saveDocument()
752 {
753  if (!mEditor->object()->filePath().isEmpty())
754  return saveObject(mEditor->object()->filePath());
755  else
756  return saveAsNewDocument();
757 }
758 
759 bool MainWindow2::maybeSave()
760 {
761  if (mEditor->currentBackup() == mBackupAtSave)
762  {
763  return true;
764  }
765 
766  int ret = QMessageBox::warning(this, tr("Warning"),
767  tr("This animation has been modified.\n Do you want to save your changes?"),
769  if (ret == QMessageBox::Save)
770  return saveDocument();
771  else
772  return ret == QMessageBox::Discard;
773 }
774 
775 bool MainWindow2::autoSave()
776 {
777  if (!mEditor->object()->filePath().isEmpty())
778  {
779  return saveDocument();
780  }
781 
782  if (mEditor->autoSaveNeverAskAgain())
783  return false;
784 
785  if(mSuppressAutoSaveDialog)
786  return false;
787 
788  QMessageBox msgBox(this);
789  msgBox.setIcon(QMessageBox::Question);
790  msgBox.setWindowTitle(tr("AutoSave Reminder"));
791  msgBox.setText(tr("The animation is not saved yet.\n Do you want to save now?"));
792  msgBox.addButton(tr("Never ask again", "AutoSave reminder button"), QMessageBox::RejectRole);
793  msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
794  msgBox.setDefaultButton(QMessageBox::Yes);
795 
796  int ret = msgBox.exec();
797  if (ret == QMessageBox::Yes)
798  {
799  return saveDocument();
800  }
801  if (ret != QMessageBox::No) // Never ask again
802  {
803  mEditor->dontAskAutoSave(true);
804  }
805 
806  return false;
807 }
808 
809 void MainWindow2::emptyDocumentWhenErrorOccurred()
810 {
811  newObject();
812 
813  setWindowTitle(PENCIL_WINDOW_TITLE);
814  updateSaveState();
815 }
816 
817 void MainWindow2::importImage()
818 {
819  QString strFilePath = FileDialog::getOpenFileName(this, FileType::IMAGE);
820 
821  if (strFilePath.isEmpty()) { return; }
822  if (!QFile::exists(strFilePath)) { return; }
823 
824  ImportPositionDialog* positionDialog = new ImportPositionDialog(this);
825  OnScopeExit(delete positionDialog)
826 
827  positionDialog->setCore(mEditor);
828  positionDialog->exec();
829 
830  if (positionDialog->result() != QDialog::Accepted)
831  {
832  return;
833  }
834 
835  bool ok = mEditor->importImage(strFilePath);
836  if (!ok)
837  {
839  tr("Warning"),
840  tr("Unable to import image.<br><b>TIP:</b> Use Bitmap layer to import bitmaps."),
843  return;
844  }
845 
846  ui->scribbleArea->updateCurrentFrame();
847  mTimeLine->updateContent();
848 }
849 
850 void MainWindow2::importImageSequence()
851 {
852  mSuppressAutoSaveDialog = true;
853 
854  ImportImageSeqDialog* imageSeqDialog = new ImportImageSeqDialog(this);
855  OnScopeExit(delete imageSeqDialog)
856  imageSeqDialog->setCore(mEditor);
857 
858  connect(imageSeqDialog, &ImportImageSeqDialog::notifyAnimationLengthChanged, mEditor->layers(), &LayerManager::notifyAnimationLengthChanged);
859 
860  imageSeqDialog->exec();
861  if (imageSeqDialog->result() == QDialog::Rejected)
862  {
863  return;
864  }
865 
866  ImportPositionDialog* positionDialog = new ImportPositionDialog(this);
867  OnScopeExit(delete positionDialog)
868 
869  positionDialog->setCore(mEditor);
870  positionDialog->exec();
871  if (positionDialog->result() != QDialog::Accepted)
872  {
873  return;
874  }
875 
876  imageSeqDialog->importArbitrarySequence();
877 
878  mSuppressAutoSaveDialog = false;
879 }
880 
881 void MainWindow2::importPredefinedImageSet()
882 {
883  ImportImageSeqDialog* imageSeqDialog = new ImportImageSeqDialog(this, ImportExportDialog::Import, FileType::IMAGE, ImportCriteria::PredefinedSet);
884  OnScopeExit(delete imageSeqDialog)
885  imageSeqDialog->setCore(mEditor);
886 
887  connect(imageSeqDialog, &ImportImageSeqDialog::notifyAnimationLengthChanged, mEditor->layers(), &LayerManager::notifyAnimationLengthChanged);
888 
889  mSuppressAutoSaveDialog = true;
890  imageSeqDialog->exec();
891  if (imageSeqDialog->result() == QDialog::Rejected)
892  {
893  return;
894  }
895 
896  ImportPositionDialog* positionDialog = new ImportPositionDialog(this);
897  OnScopeExit(delete positionDialog)
898 
899  positionDialog->setCore(mEditor);
900  positionDialog->exec();
901  if (positionDialog->result() != QDialog::Accepted)
902  {
903  return;
904  }
905 
906  imageSeqDialog->importPredefinedSet();
907  mSuppressAutoSaveDialog = false;
908 }
909 
910 void MainWindow2::importLayers()
911 {
912  ImportLayersDialog* importLayers = new ImportLayersDialog(this);
913  importLayers->setCore(mEditor);
914  importLayers->setAttribute(Qt::WA_DeleteOnClose);
915  importLayers->open();
916 }
917 
918 void MainWindow2::importGIF()
919 {
920  auto gifDialog = new ImportImageSeqDialog(this, ImportExportDialog::Import, FileType::GIF);
921  gifDialog->exec();
922  if (gifDialog->result() == QDialog::Rejected)
923  {
924  return;
925  }
926 
927  // Flag this so we don't prompt the user about auto-save in the middle of the import.
928  mSuppressAutoSaveDialog = true;
929 
930  ImportPositionDialog* positionDialog = new ImportPositionDialog(this);
931  OnScopeExit(delete positionDialog)
932 
933  positionDialog->setCore(mEditor);
934  positionDialog->exec();
935  if (positionDialog->result() != QDialog::Accepted)
936  {
937  return;
938  }
939 
940  int space = gifDialog->getSpace();
941 
942  // Show a progress dialog, as this could take a while if the gif is huge
943  QProgressDialog progress(tr("Importing Animated GIF..."), tr("Abort"), 0, 100, this);
944  hideQuestionMark(progress);
945  progress.setWindowModality(Qt::WindowModal);
946  progress.show();
947 
948  QString strImgFileLower = gifDialog->getFilePath();
949  bool importOK = strImgFileLower.toLower().endsWith(".gif");
950 
951  if (importOK)
952  {
953  bool ok = mEditor->importGIF(strImgFileLower, space);
954  if (!ok)
955  importOK = false;
956 
957  progress.setValue(50);
958  QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // Required to make progress bar update
959  }
960 
961  if (!importOK)
962  {
964  tr("Warning"),
965  tr("was unable to import %1").arg(strImgFileLower),
968  }
969 
970  progress.setValue(100);
971  progress.close();
972 
973  mSuppressAutoSaveDialog = false;
974 }
975 
976 void MainWindow2::lockWidgets(bool shouldLock)
977 {
979 
980  for (QDockWidget* d : mDockWidgets)
981  {
982  d->setFeatures(feat);
983 
984  // https://doc.qt.io/qt-5/qdockwidget.html#setTitleBarWidget
985  // A empty QWidget looks like the tittle bar is hidden.
986  // nullptr means removing the custom title bar and restoring the default one
987  QWidget* customTitleBarWidget = shouldLock ? (new QWidget) : nullptr;
988  d->setTitleBarWidget(customTitleBarWidget);
989  }
990 }
991 
992 void MainWindow2::preferences()
993 {
994  if (mPrefDialog)
995  {
996  mPrefDialog->activateWindow();
997  mPrefDialog->raise();
998  return;
999  }
1000  mPrefDialog = new PreferencesDialog(this);
1002  mPrefDialog->setAttribute(Qt::WA_DeleteOnClose);
1003  mPrefDialog->init(mEditor->preference());
1004 
1005  connect(mPrefDialog, &PreferencesDialog::windowOpacityChange, this, &MainWindow2::setOpacity);
1006  connect(mPrefDialog, &PreferencesDialog::soundScrubChanged, mEditor->playback(), &PlaybackManager::setSoundScrubActive);
1007  connect(mPrefDialog, &PreferencesDialog::soundScrubMsecChanged, mEditor->playback(), &PlaybackManager::setSoundScrubMsec);
1008  connect(mPrefDialog, &PreferencesDialog::finished, [&]
1009  {
1010  clearKeyboardShortcuts();
1011  setupKeyboardShortcuts();
1012  ui->scribbleArea->updateCanvasCursor();
1013  mPrefDialog = nullptr;
1014  });
1015 
1016  mPrefDialog->show();
1017 }
1018 
1019 void MainWindow2::resetAndDockAllSubWidgets()
1020 {
1021  QSettings settings(PENCIL2D, PENCIL2D);
1022  settings.remove(SETTING_WINDOW_GEOMETRY);
1023  settings.remove(SETTING_WINDOW_STATE);
1024 
1025  for (BaseDockWidget* dock : mDockWidgets)
1026  {
1027  dock->setFloating(false);
1028  dock->raise();
1029  dock->show();
1030  }
1031 
1033  addDockWidget(Qt::RightDockWidgetArea, mColorInspector);
1034  addDockWidget(Qt::RightDockWidgetArea, mColorPalette);
1036  addDockWidget(Qt::LeftDockWidgetArea, mToolOptions);
1037  addDockWidget(Qt::LeftDockWidgetArea, mDisplayOptionWidget);
1038  addDockWidget(Qt::LeftDockWidgetArea, mOnionSkinWidget);
1040 }
1041 
1042 void MainWindow2::newObject()
1043 {
1044  auto object = new Object();
1045  object->init();
1046 
1047  // default layers
1048  object->addNewCameraLayer();
1049  object->addNewBitmapLayer();
1050  // Layers are counted bottom up
1051  // 0 - Camera Layer
1052  // 1 - Bitmap Layer
1053  object->data()->setCurrentLayer(1);
1054 
1055  mEditor->setObject(object);
1056 
1057  closeDialogs();
1058 
1059  setWindowTitle(PENCIL_WINDOW_TITLE);
1060  undoActSetText();
1061 }
1062 
1063 bool MainWindow2::newObjectFromPresets(int presetIndex)
1064 {
1065  QString presetFilePath = PresetDialog::getPresetPath(presetIndex);
1066 
1067  if (presetFilePath.isEmpty())
1068  {
1069  return false;
1070  }
1071 
1072  FileManager fm(this);
1073  Object* object = fm.load(presetFilePath);
1074 
1075  if (!fm.error().ok() || object == nullptr)
1076  {
1077  return false;
1078  }
1079 
1080  mEditor->setObject(object);
1081  object->setFilePath(QString());
1082 
1083  setWindowTitle(PENCIL_WINDOW_TITLE);
1084  updateSaveState();
1085  undoActSetText();
1086 
1087  return true;
1088 }
1089 
1090 bool MainWindow2::loadMostRecent()
1091 {
1092  if(!mEditor->preference()->isOn(SETTING::LOAD_MOST_RECENT))
1093  {
1094  return false;
1095  }
1096 
1097  QSettings settings(PENCIL2D, PENCIL2D);
1098  QString myPath = settings.value(LAST_PCLX_PATH, QVariant("")).toString();
1099  if (myPath.isEmpty() || !QFile::exists(myPath))
1100  {
1101  return false;
1102  }
1103  return openObject(myPath);
1104 }
1105 
1106 bool MainWindow2::tryLoadPreset()
1107 {
1108  if (!mEditor->preference()->isOn(SETTING::ASK_FOR_PRESET))
1109  {
1110  int defaultPreset = mEditor->preference()->getInt(SETTING::DEFAULT_PRESET);
1111  return newObjectFromPresets(defaultPreset);
1112  }
1113 
1114  PresetDialog* presetDialog = new PresetDialog(mEditor->preference(), this);
1115  presetDialog->setAttribute(Qt::WA_DeleteOnClose);
1116  connect(presetDialog, &PresetDialog::finished, [=](int result)
1117  {
1118  if (result != QDialog::Accepted)
1119  {
1120  return;
1121  }
1122 
1123  int presetIndex = presetDialog->getPresetIndex();
1124  if (presetDialog->shouldAlwaysUse())
1125  {
1126  mEditor->preference()->set(SETTING::ASK_FOR_PRESET, false);
1127  mEditor->preference()->set(SETTING::DEFAULT_PRESET, presetIndex);
1128  }
1129  if (!newObjectFromPresets(presetIndex))
1130  {
1131  newObject();
1132  }
1133  });
1134  presetDialog->open();
1135  return true;
1136 }
1137 
1138 void MainWindow2::closeDialogs()
1139 {
1140  for (auto dialog : findChildren<QDialog*>(QString(), Qt::FindDirectChildrenOnly)) {
1141  dialog->close();
1142  }
1143 }
1144 
1145 void MainWindow2::readSettings()
1146 {
1147  QSettings settings(PENCIL2D, PENCIL2D);
1148 
1149  QVariant winGeometry = settings.value(SETTING_WINDOW_GEOMETRY);
1150  restoreGeometry(winGeometry.toByteArray());
1151 
1152  QVariant winState = settings.value(SETTING_WINDOW_STATE);
1153  restoreState(winState.toByteArray());
1154 
1155  int opacity = mEditor->preference()->getInt(SETTING::WINDOW_OPACITY);
1156  setOpacity(100 - opacity);
1157 
1158  bool isWindowsLocked = mEditor->preference()->isOn(SETTING::LAYOUT_LOCK);
1159  lockWidgets(isWindowsLocked);
1160 }
1161 
1162 void MainWindow2::writeSettings()
1163 {
1164  QSettings settings(PENCIL2D, PENCIL2D);
1165  settings.setValue(SETTING_WINDOW_GEOMETRY, saveGeometry());
1166  settings.setValue(SETTING_WINDOW_STATE, saveState());
1167 }
1168 
1169 void MainWindow2::setupKeyboardShortcuts()
1170 {
1171  checkExistingShortcuts();
1172 
1173  auto cmdKeySeq = [](QString strCommandName) -> QKeySequence
1174  {
1175  strCommandName = QString("shortcuts/") + strCommandName;
1176  QKeySequence keySequence(pencilSettings().value(strCommandName).toString());
1177  return keySequence;
1178  };
1179 
1180  ui->actionNew->setShortcut(cmdKeySeq(CMD_NEW_FILE));
1181  ui->actionOpen->setShortcut(cmdKeySeq(CMD_OPEN_FILE));
1182  ui->actionSave->setShortcut(cmdKeySeq(CMD_SAVE_FILE));
1183  ui->actionSave_as->setShortcut(cmdKeySeq(CMD_SAVE_AS));
1184 
1185  ui->actionImport_Image->setShortcut(cmdKeySeq(CMD_IMPORT_IMAGE));
1186  ui->actionImport_ImageSeq->setShortcut(cmdKeySeq(CMD_IMPORT_IMAGE_SEQ));
1187  ui->actionImport_MovieVideo->setShortcut(cmdKeySeq(CMD_IMPORT_MOVIE_VIDEO));
1188  ui->actionImport_MovieAudio->setShortcut(cmdKeySeq(CMD_IMPORT_MOVIE_AUDIO));
1189  ui->actionImport_Append_Palette->setShortcut(cmdKeySeq(CMD_IMPORT_PALETTE));
1190  ui->actionImport_Sound->setShortcut(cmdKeySeq(CMD_IMPORT_SOUND));
1191 
1192  ui->actionExport_Image->setShortcut(cmdKeySeq(CMD_EXPORT_IMAGE));
1193  ui->actionExport_ImageSeq->setShortcut(cmdKeySeq(CMD_EXPORT_IMAGE_SEQ));
1194  ui->actionExport_Movie->setShortcut(cmdKeySeq(CMD_EXPORT_MOVIE));
1195  ui->actionExport_Palette->setShortcut(cmdKeySeq(CMD_EXPORT_PALETTE));
1196 
1197  // edit menu
1198  ui->actionUndo->setShortcut(cmdKeySeq(CMD_UNDO));
1199  ui->actionRedo->setShortcut(cmdKeySeq(CMD_REDO));
1200  ui->actionCut->setShortcut(cmdKeySeq(CMD_CUT));
1201  ui->actionCopy->setShortcut(cmdKeySeq(CMD_COPY));
1202  ui->actionPaste_Previous->setShortcut(cmdKeySeq(CMD_PASTE_FROM_PREVIOUS));
1203  ui->actionPaste->setShortcut(cmdKeySeq(CMD_PASTE));
1204  ui->actionClearFrame->setShortcut(cmdKeySeq(CMD_CLEAR_FRAME));
1205  ui->actionSelect_All->setShortcut(cmdKeySeq(CMD_SELECT_ALL));
1206  ui->actionDeselect_All->setShortcut(cmdKeySeq(CMD_DESELECT_ALL));
1207  ui->actionPreference->setShortcut(cmdKeySeq(CMD_PREFERENCE));
1208 
1209  // View menu
1210  ui->actionResetWindows->setShortcut(cmdKeySeq(CMD_RESET_WINDOWS));
1211  ui->actionReset_View->setShortcut(cmdKeySeq(CMD_RESET_ZOOM_ROTATE));
1212  ui->actionCenter_View->setShortcut(cmdKeySeq(CMD_CENTER_VIEW));
1213  ui->actionZoom_In->setShortcut(cmdKeySeq(CMD_ZOOM_IN));
1214  ui->actionZoom_Out->setShortcut(cmdKeySeq(CMD_ZOOM_OUT));
1215  ui->actionZoom400->setShortcut(cmdKeySeq(CMD_ZOOM_400));
1216  ui->actionZoom300->setShortcut(cmdKeySeq(CMD_ZOOM_300));
1217  ui->actionZoom200->setShortcut(cmdKeySeq(CMD_ZOOM_200));
1218  ui->actionZoom100->setShortcut(cmdKeySeq(CMD_ZOOM_100));
1219  ui->actionZoom50->setShortcut(cmdKeySeq(CMD_ZOOM_50));
1220  ui->actionZoom33->setShortcut(cmdKeySeq(CMD_ZOOM_33));
1221  ui->actionZoom25->setShortcut(cmdKeySeq(CMD_ZOOM_25));
1222  ui->actionRotate_Clockwise->setShortcut(cmdKeySeq(CMD_ROTATE_CLOCK));
1223  ui->actionRotate_Anticlockwise->setShortcut(cmdKeySeq(CMD_ROTATE_ANTI_CLOCK));
1224  ui->actionReset_Rotation->setShortcut(cmdKeySeq(CMD_RESET_ROTATION));
1225  ui->actionHorizontal_Flip->setShortcut(cmdKeySeq(CMD_FLIP_HORIZONTAL));
1226  ui->actionVertical_Flip->setShortcut(cmdKeySeq(CMD_FLIP_VERTICAL));
1227  ui->actionPreview->setShortcut(cmdKeySeq(CMD_PREVIEW));
1228  ui->actionGrid->setShortcut(cmdKeySeq(CMD_GRID));
1229  ui->actionOnionPrev->setShortcut(cmdKeySeq(CMD_ONIONSKIN_PREV));
1230  ui->actionOnionNext->setShortcut(cmdKeySeq(CMD_ONIONSKIN_NEXT));
1231  ui->actionStatusBar->setShortcut(cmdKeySeq(CMD_TOGGLE_STATUS_BAR));
1232 
1233  ui->actionPlay->setShortcut(cmdKeySeq(CMD_PLAY));
1234  ui->actionLoop->setShortcut(cmdKeySeq(CMD_LOOP));
1235  ui->actionPrevious_Frame->setShortcut(cmdKeySeq(CMD_GOTO_PREV_FRAME));
1236  ui->actionNext_Frame->setShortcut(cmdKeySeq(CMD_GOTO_NEXT_FRAME));
1237  ui->actionPrev_KeyFrame->setShortcut(cmdKeySeq(CMD_GOTO_PREV_KEY_FRAME));
1238  ui->actionNext_KeyFrame->setShortcut(cmdKeySeq(CMD_GOTO_NEXT_KEY_FRAME));
1239  ui->actionAdd_Frame->setShortcut(cmdKeySeq(CMD_ADD_FRAME));
1240  ui->actionDuplicate_Frame->setShortcut(cmdKeySeq(CMD_DUPLICATE_FRAME));
1241  ui->actionRemove_Frame->setShortcut(cmdKeySeq(CMD_REMOVE_FRAME));
1242  ui->actionAdd_Frame_Exposure->setShortcut(cmdKeySeq(CMD_SELECTION_ADD_FRAME_EXPOSURE));
1243  ui->actionSubtract_Frame_Exposure->setShortcut(cmdKeySeq(CMD_SELECTION_SUBTRACT_FRAME_EXPOSURE));
1244  ui->actionReverse_Frames_Order->setShortcut(cmdKeySeq(CMD_REVERSE_SELECTED_FRAMES));
1245  ui->actionRemove_Frames->setShortcut(cmdKeySeq(CMD_REMOVE_SELECTED_FRAMES));
1246  ui->actionMove_Frame_Backward->setShortcut(cmdKeySeq(CMD_MOVE_FRAME_BACKWARD));
1247  ui->actionMove_Frame_Forward->setShortcut(cmdKeySeq(CMD_MOVE_FRAME_FORWARD));
1248  ui->actionFlip_inbetween->setShortcut(cmdKeySeq(CMD_FLIP_INBETWEEN));
1249  ui->actionFlip_rolling->setShortcut(cmdKeySeq(CMD_FLIP_ROLLING));
1250 
1251  ShortcutFilter* shortcutFilter = new ShortcutFilter(ui->scribbleArea, this);
1252  ui->actionMove->setShortcut(cmdKeySeq(CMD_TOOL_MOVE));
1253  ui->actionSelect->setShortcut(cmdKeySeq(CMD_TOOL_SELECT));
1254  ui->actionBrush->setShortcut(cmdKeySeq(CMD_TOOL_BRUSH));
1255  ui->actionPolyline->setShortcut(cmdKeySeq(CMD_TOOL_POLYLINE));
1256  ui->actionSmudge->setShortcut(cmdKeySeq(CMD_TOOL_SMUDGE));
1257  ui->actionPen->setShortcut(cmdKeySeq(CMD_TOOL_PEN));
1258  ui->actionHand->setShortcut(cmdKeySeq(CMD_TOOL_HAND));
1259  ui->actionPencil->setShortcut(cmdKeySeq(CMD_TOOL_PENCIL));
1260  ui->actionBucket->setShortcut(cmdKeySeq(CMD_TOOL_BUCKET));
1261  ui->actionEyedropper->setShortcut(cmdKeySeq(CMD_TOOL_EYEDROPPER));
1262  ui->actionEraser->setShortcut(cmdKeySeq(CMD_TOOL_ERASER));
1263 
1264  ui->actionMove->installEventFilter(shortcutFilter);
1265  ui->actionMove->installEventFilter(shortcutFilter);
1266  ui->actionSelect->installEventFilter(shortcutFilter);
1267  ui->actionBrush->installEventFilter(shortcutFilter);
1268  ui->actionPolyline->installEventFilter(shortcutFilter);
1269  ui->actionSmudge->installEventFilter(shortcutFilter);
1270  ui->actionPen->installEventFilter(shortcutFilter);
1271  ui->actionHand->installEventFilter(shortcutFilter);
1272  ui->actionPencil->installEventFilter(shortcutFilter);
1273  ui->actionBucket->installEventFilter(shortcutFilter);
1274  ui->actionEyedropper->installEventFilter(shortcutFilter);
1275  ui->actionEraser->installEventFilter(shortcutFilter);
1276 
1277  ui->actionNew_Bitmap_Layer->setShortcut(cmdKeySeq(CMD_NEW_BITMAP_LAYER));
1278  ui->actionNew_Vector_Layer->setShortcut(cmdKeySeq(CMD_NEW_VECTOR_LAYER));
1279  ui->actionNew_Camera_Layer->setShortcut(cmdKeySeq(CMD_NEW_CAMERA_LAYER));
1280  ui->actionNew_Sound_Layer->setShortcut(cmdKeySeq(CMD_NEW_SOUND_LAYER));
1281  ui->actionDelete_Current_Layer->setShortcut(cmdKeySeq(CMD_DELETE_CUR_LAYER));
1282 
1283  ui->actionVisibilityCurrentLayerOnly->setShortcut(cmdKeySeq(CMD_CURRENT_LAYER_VISIBILITY));
1284  ui->actionVisibilityRelative->setShortcut(cmdKeySeq(CMD_RELATIVE_LAYER_VISIBILITY));
1285  ui->actionVisibilityAll->setShortcut(cmdKeySeq(CMD_ALL_LAYER_VISIBILITY));
1286 
1287  mToolBox->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_TOOLBOX));
1288  mToolOptions->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_TOOL_OPTIONS));
1289  mColorBox->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_COLOR_WHEEL));
1290  mColorPalette->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_COLOR_LIBRARY));
1291  mTimeLine->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_TIMELINE));
1292  mDisplayOptionWidget->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_DISPLAY_OPTIONS));
1293  mColorInspector->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_COLOR_INSPECTOR));
1294  mOnionSkinWidget->toggleViewAction()->setShortcut(cmdKeySeq(CMD_TOGGLE_ONION_SKIN));
1295 
1296  ui->actionHelp->setShortcut(cmdKeySeq(CMD_HELP));
1297  ui->actionExit->setShortcut(cmdKeySeq(CMD_EXIT));
1298 }
1299 
1300 void MainWindow2::clearKeyboardShortcuts()
1301 {
1302  QList<QAction*> actionList = findChildren<QAction*>();
1303  for (QAction* action : actionList)
1304  {
1305  action->setShortcut(QKeySequence(0));
1306  }
1307 }
1308 
1309 void MainWindow2::undoActSetText()
1310 {
1311  if (mEditor->mBackupIndex < 0)
1312  {
1313  ui->actionUndo->setText(tr("Undo", "Menu item text"));
1314  ui->actionUndo->setEnabled(false);
1315  }
1316  else
1317  {
1318  ui->actionUndo->setText(QString("%1 %2 %3").arg(tr("Undo", "Menu item text"))
1319  .arg(mEditor->mBackupIndex + 1)
1320  .arg(mEditor->mBackupList.at(mEditor->mBackupIndex)->undoText));
1321  ui->actionUndo->setEnabled(true);
1322  }
1323 
1324  if (mEditor->mBackupIndex + 2 < mEditor->mBackupList.size())
1325  {
1326  ui->actionRedo->setText(QString("%1 %2 %3").arg(tr("Redo", "Menu item text"))
1327  .arg(mEditor->mBackupIndex + 2)
1328  .arg(mEditor->mBackupList.at(mEditor->mBackupIndex + 1)->undoText));
1329  ui->actionRedo->setEnabled(true);
1330  }
1331  else
1332  {
1333  ui->actionRedo->setText(tr("Redo", "Menu item text"));
1334  ui->actionRedo->setEnabled(false);
1335  }
1336 }
1337 
1338 void MainWindow2::exportPalette()
1339 {
1340  QString filePath = FileDialog::getSaveFileName(this, FileType::PALETTE);
1341  if (!filePath.isEmpty())
1342  {
1343  mEditor->object()->exportPalette(filePath);
1344  }
1345 }
1346 
1347 void MainWindow2::importPalette()
1348 {
1349  QString filePath = FileDialog::getOpenFileName(this, FileType::PALETTE);
1350  if (!filePath.isEmpty())
1351  {
1352  mEditor->object()->importPalette(filePath);
1353  mColorPalette->refreshColorList();
1354  mColorPalette->adjustSwatches();
1355  mEditor->color()->setColorNumber(0);
1356  }
1357 }
1358 
1359 void MainWindow2::openPalette()
1360 {
1361  for (int i = 0; i < mEditor->object()->getColorCount(); i++)
1362  {
1363  if (!mEditor->object()->isColorInUse(i))
1364  {
1365  continue;
1366  }
1367 
1368  QMessageBox msgBox;
1369  msgBox.setText(tr("Opening a palette will replace the old palette.\n"
1370  "Color(s) in strokes will be altered by this action!"));
1371  msgBox.addButton(tr("Open Palette"), QMessageBox::AcceptRole);
1373 
1374  if (msgBox.exec() == QMessageBox::Cancel) {
1375  return;
1376  }
1377  break;
1378  }
1379 
1380  QString filePath = FileDialog::getOpenFileName(this, FileType::PALETTE);
1381  if (filePath.isEmpty())
1382  {
1383  return;
1384  }
1385 
1386  mEditor->object()->openPalette(filePath);
1387  mColorPalette->refreshColorList();
1388  mEditor->color()->setColorNumber(0);
1389 }
1390 
1391 void MainWindow2::makeConnections(Editor* editor)
1392 {
1393  connect(editor, &Editor::updateBackup, this, &MainWindow2::updateSaveState);
1394  connect(editor, &Editor::needDisplayInfo, this, &MainWindow2::displayMessageBox);
1395  connect(editor, &Editor::needDisplayInfoNoTitle, this, &MainWindow2::displayMessageBoxNoTitle);
1396  connect(editor->layers(), &LayerManager::currentLayerChanged, this, &MainWindow2::currentLayerChanged);
1397  connect(editor->select(), &SelectionManager::selectionChanged, this, &MainWindow2::selectionChanged);
1398  connect(editor, &Editor::canCopyChanged, this, [=](bool canCopy) {
1399  ui->actionCopy->setEnabled(canCopy);
1400  ui->actionCut->setEnabled(canCopy);
1401  });
1402  connect(editor, &Editor::canPasteChanged, ui->actionPaste, &QAction::setEnabled);
1403 
1404 }
1405 
1406 void MainWindow2::makeConnections(Editor* editor, ColorBox* colorBox)
1407 {
1408  connect(colorBox, &ColorBox::colorChanged, editor->color(), &ColorManager::setFrontColor);
1409  connect(editor->color(), &ColorManager::colorChanged, colorBox, &ColorBox::setColor);
1410 }
1411 
1412 void MainWindow2::makeConnections(Editor* editor, ColorInspector* colorInspector)
1413 {
1414  connect(colorInspector, &ColorInspector::colorChanged, editor->color(), &ColorManager::setFrontColor);
1415  connect(editor->color(), &ColorManager::colorChanged, colorInspector, &ColorInspector::setColor);
1416 }
1417 
1418 void MainWindow2::makeConnections(Editor* editor, ScribbleArea* scribbleArea)
1419 {
1420  connect(editor->tools(), &ToolManager::toolChanged, scribbleArea, &ScribbleArea::setCurrentTool);
1421  connect(editor->tools(), &ToolManager::toolChanged, mToolBox, &ToolBoxWidget::onToolSetActive);
1422  connect(editor->tools(), &ToolManager::toolPropertyChanged, scribbleArea, &ScribbleArea::updateToolCursor);
1423 
1424 
1425  connect(editor->layers(), &LayerManager::currentLayerChanged, scribbleArea, &ScribbleArea::onLayerChanged);
1426  connect(editor->layers(), &LayerManager::layerDeleted, scribbleArea, &ScribbleArea::onLayerChanged);
1427  connect(editor, &Editor::scrubbed, scribbleArea, &ScribbleArea::onScrubbed);
1430  connect(editor, &Editor::objectLoaded, scribbleArea, &ScribbleArea::onObjectLoaded);
1431  connect(editor->view(), &ViewManager::viewChanged, scribbleArea, &ScribbleArea::onViewChanged);
1432 }
1433 
1434 void MainWindow2::makeConnections(Editor* pEditor, TimeLine* pTimeline)
1435 {
1436  PlaybackManager* pPlaybackManager = pEditor->playback();
1437  connect(pTimeline, &TimeLine::duplicateLayerClick, mCommands, &ActionCommands::duplicateLayer);
1438  connect(pTimeline, &TimeLine::duplicateKeyClick, mCommands, &ActionCommands::duplicateKey);
1439 
1440  connect(pTimeline, &TimeLine::soundClick, pPlaybackManager, &PlaybackManager::enableSound);
1441  connect(pTimeline, &TimeLine::fpsChanged, pPlaybackManager, &PlaybackManager::setFps);
1442  connect(pTimeline, &TimeLine::fpsChanged, pEditor, &Editor::setFps);
1443 
1444  connect(pTimeline, &TimeLine::insertKeyClick, mCommands, &ActionCommands::insertKeyFrameAtCurrentPosition);
1445  connect(pTimeline, &TimeLine::removeKeyClick, mCommands, &ActionCommands::removeKey);
1446 
1447  connect(pTimeline, &TimeLine::newBitmapLayer, mCommands, &ActionCommands::addNewBitmapLayer);
1448  connect(pTimeline, &TimeLine::newVectorLayer, mCommands, &ActionCommands::addNewVectorLayer);
1449  connect(pTimeline, &TimeLine::newSoundLayer, mCommands, &ActionCommands::addNewSoundLayer);
1450  connect(pTimeline, &TimeLine::newCameraLayer, mCommands, &ActionCommands::addNewCameraLayer);
1451  connect(mTimeLine, &TimeLine::playButtonTriggered, mCommands, &ActionCommands::PlayStop);
1452 
1453  // Clipboard state handling
1454  connect(QApplication::clipboard(), &QClipboard::dataChanged, mEditor, &Editor::clipboardChanged);
1455  connect(ui->menuEdit, &QMenu::aboutToShow, this, &MainWindow2::updateCopyCutPasteEnabled);
1456  connect(pTimeline, &TimeLine::selectionChanged, this, &MainWindow2::updateCopyCutPasteEnabled);
1457  connect(this, &MainWindow2::windowActivated, this, &MainWindow2::updateCopyCutPasteEnabled);
1458  connect(mEditor->select(), &SelectionManager::selectionChanged, this, &MainWindow2::updateCopyCutPasteEnabled);
1459 
1460  connect(pEditor->layers(), &LayerManager::currentLayerChanged, pTimeline, &TimeLine::updateUI);
1461  connect(pEditor->layers(), &LayerManager::layerCountChanged, pTimeline, &TimeLine::updateUI);
1462  connect(pEditor->layers(), &LayerManager::animationLengthChanged, pTimeline, &TimeLine::extendLength);
1463  connect(pEditor->sound(), &SoundManager::soundClipDurationChanged, pTimeline, &TimeLine::updateUI);
1464 
1465  connect(pEditor, &Editor::objectLoaded, pTimeline, &TimeLine::onObjectLoaded);
1466  connect(pEditor, &Editor::updateTimeLine, pTimeline, &TimeLine::updateUI);
1467 
1468  connect(pEditor->layers(), &LayerManager::currentLayerChanged, mToolOptions, &ToolOptionWidget::updateUI);
1469 }
1470 
1471 void MainWindow2::makeConnections(Editor* editor, DisplayOptionWidget* displayWidget)
1472 {
1473  connect(editor->layers(), &LayerManager::currentLayerChanged, displayWidget, &DisplayOptionWidget::updateUI);
1474 }
1475 
1476 void MainWindow2::makeConnections(Editor*, OnionSkinWidget*)
1477 {
1478 }
1479 
1480 void MainWindow2::makeConnections(Editor* editor, ToolOptionWidget* toolOptions)
1481 {
1482  toolOptions->makeConnectionToEditor(editor);
1483 }
1484 
1485 void MainWindow2::makeConnections(Editor* pEditor, ColorPaletteWidget* pColorPalette)
1486 {
1487  connect(pEditor, &Editor::objectLoaded, pColorPalette, &ColorPaletteWidget::updateUI);
1488 
1489  ColorManager* pColorManager = pEditor->color();
1490  ScribbleArea* pScribbleArea = pEditor->getScribbleArea();
1491 
1492  connect(pColorPalette, &ColorPaletteWidget::colorNumberChanged, pColorManager, &ColorManager::setColorNumber);
1493  connect(pColorManager, &ColorManager::colorNumberChanged, pScribbleArea, &ScribbleArea::paletteColorChanged);
1494  connect(pColorManager, &ColorManager::colorNumberChanged, pColorPalette, &ColorPaletteWidget::selectColorNumber);
1495 }
1496 
1497 void MainWindow2::makeConnections(Editor* editor, StatusBar *statusBar)
1498 {
1499  connect(editor->tools(), &ToolManager::toolChanged, statusBar, &StatusBar::updateToolStatus);
1500  connect(editor->tools()->getTool(POLYLINE), &BaseTool::isActiveChanged, statusBar, &StatusBar::updateToolStatus);
1501 
1502  connect(editor->view(), &ViewManager::viewChanged, statusBar, &StatusBar::updateZoomStatus);
1503  connect(statusBar, &StatusBar::zoomChanged, editor->view(), &ViewManager::scale);
1504 }
1505 
1506 void MainWindow2::updateCopyCutPasteEnabled()
1507 {
1508  bool canCopy = mEditor->canCopy();
1509  bool canPaste = mEditor->canPaste();
1510 
1511  ui->actionCopy->setEnabled(canCopy);
1512  ui->actionCut->setEnabled(canCopy);
1513  ui->actionPaste->setEnabled(canPaste);
1514 }
1515 
1516 void MainWindow2::changePlayState(bool isPlaying)
1517 {
1518  if (isPlaying)
1519  {
1520  ui->actionPlay->setText(tr("Stop"));
1521  ui->actionPlay->setIcon(mStopIcon);
1522  }
1523  else
1524  {
1525  ui->actionPlay->setText(tr("Play"));
1526  ui->actionPlay->setIcon(mStartIcon);
1527  }
1528  update();
1529 }
1530 
1531 void MainWindow2::importMovieVideo()
1532 {
1533  // Flag this so we don't prompt the user about auto-save in the middle of the import.
1534  mSuppressAutoSaveDialog = true;
1535 
1536  mCommands->importMovieVideo();
1537 
1538  mSuppressAutoSaveDialog = false;
1539 }
1540 
1541 bool MainWindow2::event(QEvent* event)
1542 {
1543  if(event->type() == QEvent::WindowActivate) {
1544  emit windowActivated();
1545  return true;
1546  }
1547  return QMainWindow::event(event);
1548 }
1549 
1550 void MainWindow2::displayMessageBox(const QString& title, const QString& body)
1551 {
1552  QMessageBox::information(this, tr(qPrintable(title)), tr(qPrintable(body)), QMessageBox::Ok);
1553 }
1554 
1555 void MainWindow2::displayMessageBoxNoTitle(const QString& body)
1556 {
1557  QMessageBox::information(this, nullptr, tr(qPrintable(body)), QMessageBox::Ok);
1558 }
1559 
1560 bool MainWindow2::tryRecoverUnsavedProject()
1561 {
1562  FileManager fm;
1563  QStringList recoverables = fm.searchForUnsavedProjects();
1564 
1565  if (recoverables.empty())
1566  {
1567  return false;
1568  }
1569 
1570  QString caption = tr("Restore Project?");
1571  QString text = tr("Pencil2D didn't close correctly. Would you like to restore the project?");
1572 
1573  QString recoverPath = recoverables[0];
1574 
1575  QMessageBox* msgBox = new QMessageBox(this);
1576  msgBox->setWindowTitle(tr("Restore project"));
1579  msgBox->setIconPixmap(QPixmap(":/icons/logo.png"));
1580  msgBox->setText(QString("<h4>%1</h4>%2").arg(caption, text));
1581  msgBox->setInformativeText(QString("<b>%1</b>").arg(retrieveProjectNameFromTempPath(recoverPath)));
1583  msgBox->setProperty("RecoverPath", recoverPath);
1584  hideQuestionMark(*msgBox);
1585 
1586  connect(msgBox, &QMessageBox::finished, this, &MainWindow2::startProjectRecovery);
1587  msgBox->open();
1588  return true;
1589 }
1590 
1591 void MainWindow2::startProjectRecovery(int result)
1592 {
1593  const QMessageBox* msgBox = dynamic_cast<QMessageBox*>(QObject::sender());
1594  const QString recoverPath = msgBox->property("RecoverPath").toString();
1595 
1596  if (result == QMessageBox::Discard)
1597  {
1598  // The user presses discard
1599  QDir(recoverPath).removeRecursively();
1600  tryLoadPreset();
1601  return;
1602  }
1603  Q_ASSERT(result == QMessageBox::Open);
1604 
1605  FileManager fm;
1606  Object* o = fm.recoverUnsavedProject(recoverPath);
1607  if (!fm.error().ok())
1608  {
1609  Q_ASSERT(o == nullptr);
1610  const QString title = tr("Recovery Failed.");
1611  const QString text = tr("Sorry! Pencil2D is unable to restore your project");
1612  QMessageBox::information(this, title, QString("<h4>%1</h4>%2").arg(title, text));
1613  return;
1614  }
1615 
1616  Q_ASSERT(o);
1617  mEditor->setObject(o);
1618  updateSaveState();
1619  undoActSetText();
1620 
1621  const QString title = tr("Recovery Succeeded!");
1622  const QString text = tr("Please save your work immediately to prevent loss of data");
1623  QMessageBox::information(this, title, QString("<h4>%1</h4>%2").arg(title, text));
1624 }
1625 
1626 void MainWindow2::createToolbars()
1627 {
1628  mMainToolbar = addToolBar(tr("Main Toolbar"));
1629  mMainToolbar->setObjectName("mMainToolbar");
1630  mMainToolbar->addAction(ui->actionNew);
1631  mMainToolbar->addAction(ui->actionOpen);
1632  mMainToolbar->addAction(ui->actionSave);
1633  mMainToolbar->addSeparator();
1634  mMainToolbar->addAction(ui->actionUndo);
1635  mMainToolbar->addAction(ui->actionRedo);
1636  mMainToolbar->addSeparator();
1637  mMainToolbar->addAction(ui->actionCut);
1638  mMainToolbar->addAction(ui->actionCopy);
1639  mMainToolbar->addAction(ui->actionPaste);
1640 
1641  mViewToolbar = addToolBar(tr("View Toolbar"));
1642  mViewToolbar->setObjectName("mViewToolbar");
1643  mViewToolbar->addAction(ui->actionZoom_In);
1644  mViewToolbar->addAction(ui->actionZoom_Out);
1645  mViewToolbar->addAction(ui->actionReset_View);
1646  mViewToolbar->addAction(ui->actionHorizontal_Flip);
1647  mViewToolbar->addAction(ui->actionVertical_Flip);
1648 
1649  mOverlayToolbar = addToolBar(tr("Overlay Toolbar"));
1650  mOverlayToolbar->setObjectName("mOverlayToolbar");
1651  mOverlayToolbar->addAction(ui->actionGrid);
1652 
1653  mToolbars = { mMainToolbar, mViewToolbar, mOverlayToolbar };
1654 
1655  ui->menuWindows->addSeparator();
1656  QMenu* toolbarMenu = ui->menuWindows->addMenu(tr("Toolbars"));
1657  for (QToolBar* tb : mToolbars)
1658  {
1659  toolbarMenu->addAction(tb->toggleViewAction());
1660  }
1661 }
void addToolBar(Qt::ToolBarArea area, QToolBar *toolbar)
virtual void open()
QString toString(Qt::DateFormat format) const const
QByteArray toByteArray() const const
bool close()
void triggered(bool checked)
FindDirectChildrenOnly
void windowActivated()
Emitted when window regains focus.
QEvent::Type type() const const
void setIconPixmap(const QPixmap &pixmap)
QString writableLocation(QStandardPaths::StandardLocation type)
void onPlayStateChanged()
Playstate changed, invalidate relevant cache.
QObject * sender() const const
void setChecked(bool)
QString & prepend(QChar ch)
void setMenuRole(QAction::MenuRole menuRole)
QStatusBar * statusBar() const const
const T & at(int i) const const
AllDockWidgetAreas
void onFramesModified()
Multiple frames modified, invalidate cache for affected frames.
bool removeRecursively()
virtual void setVisible(bool visible)
void onLayerChanged()
Layer changed, invalidate relevant cache.
void setAttribute(Qt::WidgetAttribute attribute, bool on)
T value() const const
virtual int exec()
bool exists() const const
void scrubbed(int frameNumber)
This should be emitted after scrubbing.
void dataChanged()
void updateToolStatus(ToolType tool)
Updates the status bar with information about the current tool.
Definition: statusbar.cpp:89
void setDockNestingEnabled(bool enabled)
QString tr(const char *sourceText, const char *disambiguation, int n)
void toggled(bool checked)
QMessageBox::StandardButton information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QAction * addAction(const QString &text)
void update()
int size() const const
void setWindowModified(bool)
void updateZoomStatus()
Updates the zoom level displayed in the status bar.
Definition: statusbar.cpp:167
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
void finished(int result)
void setTimeSpec(Qt::TimeSpec spec)
int count(const T &value) const const
void setWindowFlag(Qt::WindowType flag, bool on)
void processEvents(QEventLoop::ProcessEventsFlags flags)
void aboutToShow()
QVariant property(const char *name) const const
bool empty() const const
void setWindowOpacity(qreal level)
bool restoreGeometry(const QByteArray &geometry)
void setWindowTitle(const QString &title)
virtual bool event(QEvent *event) override
void setObjectName(const QString &name)
void raise()
bool isEmpty() const const
void setText(const QString &text)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
void setInformativeText(const QString &text)
void setStandardButtons(QMessageBox::StandardButtons buttons)
void onScrubbed(int frameNumber)
Frame scrubbed, invalidate relevant cache.
QString toLower() const const
virtual int exec() override
void setShortcut(const QKeySequence &shortcut)
QByteArray saveState(int version) const const
static QString getOpenFileName(QWidget *parent, FileType fileType, const QString &caption=QString())
Shows a file dialog which allows the user to select a file to open.
Definition: filedialog.cpp:27
WA_DeleteOnClose
void onFrameModified(int frameNumber)
Frame modified, invalidate cache for frame if any.
QDateTime currentDateTime()
QAction * toggleViewAction() const const
char * toString(const T &value)
void activateWindow()
void onViewChanged()
View updated, invalidate relevant cache.
QByteArray saveGeometry() const const
The status bar of Pencil2D's main window.
Definition: statusbar.h:33
void onObjectLoaded()
Object updated, invalidate all cache.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void setWindowTitle(const QString &)
QWidget(QWidget *parent, Qt::WindowFlags f)
void zoomChanged(double scale)
This signal is sent when the user chooses a new zoom level through the status bar.
bool restoreState(const QByteArray &state, int version)
QAction * addSeparator()
void extendLength(int frame)
Extends the timeline frame length if necessary.
Definition: timeline.cpp:263
void addDockWidget(Qt::DockWidgetArea area, QDockWidget *dockwidget)
void keySequence(QWindow *window, const QKeySequence &keySequence)
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
Definition: object.h:41
bool setProperty(const char *name, const QVariant &value)
void addButton(QAbstractButton *button, QMessageBox::ButtonRole role)
void show()
void setWindowModality(Qt::WindowModality windowModality)
void setToolTip(const QString &)
Status insertKeyFrameAtCurrentPosition()
Will insert a keyframe at the current position and push connected frames to the right.
static QString getSaveFileName(QWidget *parent, FileType fileType, const QString &caption=QString())
Shows a file dialog which allows the user to select a file save path.
Definition: filedialog.cpp:65
Definition: editor.h:55
WindowModal
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void open(QObject *receiver, const char *member)
QAction * addAction(const QString &text)
QString toString() const const
QClipboard * clipboard()
typedef WindowFlags
void setEnabled(bool)
void framesModified()
This should be emitted after modifying multiple frames.