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