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