Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • interface
editor.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#include "editor.h"
19
20#include <QTimer>
21#include <QImageReader>
22#include <QDropEvent>
23#include <QTemporaryDir>
24
25#include "object.h"
26#include "vectorimage.h"
27#include "bitmapimage.h"
28#include "soundclip.h"
29#include "camera.h"
30#include "layerbitmap.h"
31#include "layervector.h"
32#include "layercamera.h"
33#include "undoredocommand.h"
34
35#include "colormanager.h"
36#include "filemanager.h"
37#include "toolmanager.h"
38#include "layermanager.h"
39#include "playbackmanager.h"
40#include "viewmanager.h"
41#include "preferencemanager.h"
42#include "soundmanager.h"
43#include "selectionmanager.h"
44#include "overlaymanager.h"
45#include "clipboardmanager.h"
46#include "undoredomanager.h"
47
48#include "scribblearea.h"
49
50Editor::Editor(QObject* parent) : QObject(parent)
51{
52}
53
54Editor::~Editor()
55{
56 // a lot more probably needs to be cleaned here...
57 clearTemporary();
58}
59
60bool Editor::init()
61{
62 // Initialize managers
63 mColorManager = new ColorManager(this);
64 mLayerManager = new LayerManager(this);
65 mToolManager = new ToolManager(this);
66 mPlaybackManager = new PlaybackManager(this);
67 mViewManager = new ViewManager(this);
68 mPreferenceManager = new PreferenceManager(this);
69 mSoundManager = new SoundManager(this);
70 mSelectionManager = new SelectionManager(this);
71 mOverlayManager = new OverlayManager(this);
72 mClipboardManager = new ClipboardManager(this);
73 mUndoRedoManager = new UndoRedoManager(this);
74
75 mAllManagers =
76 {
77 mColorManager,
78 mToolManager,
79 mLayerManager,
80 mPlaybackManager,
81 mViewManager,
82 mPreferenceManager,
83 mSoundManager,
84 mSelectionManager,
85 mOverlayManager,
86 mClipboardManager,
87 mUndoRedoManager
88 };
89
90 for (BaseManager* pManager : mAllManagers)
91 {
92 pManager->init();
93 }
94
95 makeConnections();
96
97 mIsAutosave = mPreferenceManager->isOn(SETTING::AUTO_SAVE);
98 mAutosaveNumber = mPreferenceManager->getInt(SETTING::AUTO_SAVE_NUMBER);
99
100 return true;
101}
102
103int Editor::currentFrame() const
104{
105 return mFrame;
106}
107
108int Editor::fps()
109{
110 return mPlaybackManager->fps();
111}
112
113void Editor::setFps(int fps)
114{
115 mPreferenceManager->set(SETTING::FPS, fps);
116 emit fpsChanged(fps);
117}
118
119void Editor::makeConnections()
120{
121 connect(mPreferenceManager, &PreferenceManager::optionChanged, this, &Editor::settingUpdated);
122 connect(mUndoRedoManager, &UndoRedoManager::didUpdateUndoStack, this, &Editor::updateAutoSaveCounter);
123 connect(mPreferenceManager, &PreferenceManager::optionChanged, mUndoRedoManager, &UndoRedoManager::onSettingChanged);
124
125 // XXX: This is a hack to prevent crashes until #864 is done (see #1412)
126 connect(mLayerManager, &LayerManager::layerDeleted, mUndoRedoManager, &UndoRedoManager::sanitizeLegacyBackupElementsAfterLayerDeletion);
127 connect(mLayerManager, &LayerManager::currentLayerWillChange, this, &Editor::onCurrentLayerWillChange);
128}
129
130void Editor::settingUpdated(SETTING setting)
131{
132 switch (setting)
133 {
134 case SETTING::AUTO_SAVE:
135 mIsAutosave = mPreferenceManager->isOn(SETTING::AUTO_SAVE);
136 break;
137 case SETTING::AUTO_SAVE_NUMBER:
138 mAutosaveNumber = mPreferenceManager->getInt(SETTING::AUTO_SAVE_NUMBER);
139 break;
140 case SETTING::ONION_TYPE:
141 mScribbleArea->onOnionSkinTypeChanged();
142 emit updateTimeLineCached();
143 break;
144 case SETTING::FRAME_POOL_SIZE:
145 mObject->setActiveFramePoolSize(mPreferenceManager->getInt(SETTING::FRAME_POOL_SIZE));
146 break;
147 case SETTING::LAYER_VISIBILITY:
148 mScribbleArea->setLayerVisibility(static_cast<LayerVisibility>(mPreferenceManager->getInt(SETTING::LAYER_VISIBILITY)));
149 emit updateTimeLine();
150 break;
151 default:
152 break;
153 }
154}
155
156void Editor::onCurrentLayerWillChange(int index)
157{
158 Layer* newLayer = layers()->getLayer(index);
159 Layer* currentLayer = layers()->currentLayer();
160 Q_ASSERT(newLayer && currentLayer);
161 if (currentLayer->type() != newLayer->type()) {
162 // We apply transform changes upon leaving a layer and deselect all
163 mScribbleArea->applyTransformedSelection();
164
165 if (currentLayer->type() == Layer::VECTOR) {
166 auto keyFrame = static_cast<VectorImage*>(currentLayer->getLastKeyFrameAtPosition(mFrame));
167 if (keyFrame)
168 {
169 keyFrame->deselectAll();
170 }
171 }
172
173 select()->resetSelectionProperties();
174 }
175}
176
177void Editor::updateAutoSaveCounter()
178{
179 if (mIsAutosave == false)
180 return;
181
182 mAutosaveCounter++;
183 if (mAutosaveCounter >= mAutosaveNumber)
184 {
185 resetAutoSaveCounter();
186 emit needSave();
187 }
188}
189
190void Editor::resetAutoSaveCounter()
191{
192 mAutosaveCounter = 0;
193}
194
195void Editor::copy()
196{
197 Layer* currentLayer = layers()->currentLayer();
198
199 Q_ASSERT(currentLayer != nullptr);
200
201 if (!canCopy()) { return; }
202
203 backup(tr("Copy"));
204
205 if (currentLayer->hasAnySelectedFrames() && !select()->somethingSelected()) {
206 clipboards()->copySelectedFrames(currentLayer);
207 } else if (currentLayer->type() == Layer::BITMAP) {
208 BitmapImage* bitmapImage = static_cast<BitmapImage*>(currentLayer->getLastKeyFrameAtPosition(currentFrame()));
209 clipboards()->copyBitmapImage(bitmapImage, select()->mySelectionRect());
210 } else if (currentLayer->type() == Layer::VECTOR) {
211 VectorImage* vectorImage = static_cast<VectorImage*>(currentLayer->getLastKeyFrameAtPosition(currentFrame()));
212 clipboards()->copyVectorImage(vectorImage);
213 }
214}
215
216void Editor::copyAndCut()
217{
218 copy();
219
220 Layer* currentLayer = layers()->currentLayer();
221
222 if (currentLayer->hasAnySelectedFrames() && !select()->somethingSelected()) {
223 for (int pos : currentLayer->selectedKeyFramesPositions()) {
224 currentLayer->removeKeyFrame(pos);
225 }
226 emit layers()->currentLayerChanged(currentLayerIndex());
227 emit updateTimeLine();
228 return;
229 }
230
231 if (currentLayer->type() == Layer::BITMAP || currentLayer->type() == Layer::VECTOR) {
232 mScribbleArea->deleteSelection();
233 deselectAll();
234 }
235}
236
237void Editor::pasteFromPreviousFrame()
238{
239 Layer* currentLayer = layers()->currentLayer();
240 int prevFrame = currentLayer->getPreviousKeyFramePosition(mFrame);
241 if (!currentLayer->keyExists(mFrame) || prevFrame == mFrame)
242 {
243 return;
244 }
245
246 if (currentLayer->type() == Layer::BITMAP)
247 {
248 backup(tr("Paste from Previous Keyframe"));
249 BitmapImage* bitmapImage = static_cast<BitmapImage*>(currentLayer->getKeyFrameAt(prevFrame));
250 if (select()->somethingSelected())
251 {
252 BitmapImage copy = bitmapImage->copy(select()->mySelectionRect().toRect());
253 pasteToCanvas(&copy, mFrame);
254 }
255 else
256 {
257 pasteToCanvas(bitmapImage, mFrame);
258 }
259 }
260 else if (currentLayer->type() == Layer::VECTOR)
261 {
262 backup(tr("Paste from Previous Keyframe"));
263 VectorImage* vectorImage = static_cast<VectorImage*>(currentLayer->getKeyFrameAt(prevFrame));
264 pasteToCanvas(vectorImage, mFrame);
265 }
266}
267
268void Editor::pasteToCanvas(BitmapImage* bitmapImage, int frameNumber)
269{
270 Layer* currentLayer = layers()->currentLayer();
271
272 Q_ASSERT(currentLayer->type() == Layer::BITMAP);
273
274 if (select()->somethingSelected())
275 {
276 QRectF selection = select()->mySelectionRect();
277 if (bitmapImage->width() <= selection.width() && bitmapImage->height() <= selection.height())
278 {
279 bitmapImage->moveTopLeft(selection.topLeft());
280 }
281 else
282 {
283 bitmapImage->transform(selection, true);
284 }
285 }
286 mScribbleArea->handleDrawingOnEmptyFrame();
287 BitmapImage *canvasImage = static_cast<BitmapImage*>(currentLayer->getLastKeyFrameAtPosition(frameNumber));
288
289 // Paste clipboard onto current shown image
290 canvasImage->paste(bitmapImage);
291
292 // TODO: currently we don't support placing an image without already pasting it on an already existing
293 // image, this should be reworked such that a hovering selection could be shown, before applying it...
294 select()->setSelection(bitmapImage->bounds());
295 emit frameModified(frameNumber);
296}
297
298void Editor::pasteToCanvas(VectorImage* vectorImage, int frameNumber)
299{
300 Layer* currentLayer = layers()->currentLayer();
301
302 Q_ASSERT(currentLayer->type() == Layer::VECTOR);
303
304 deselectAll();
305 mScribbleArea->handleDrawingOnEmptyFrame();
306 VectorImage* canvasImage = static_cast<VectorImage*>(currentLayer->getLastKeyFrameAtPosition(frameNumber));
307 canvasImage->paste(*vectorImage);
308 select()->setSelection(vectorImage->getSelectionRect());
309 emit frameModified(frameNumber);
310}
311
312void Editor::pasteToFrames()
313{
314 auto clipboardFrames = clipboards()->getClipboardFrames();
315 Q_ASSERT(!clipboardFrames.empty());
316 Layer* currentLayer = layers()->currentLayer();
317
318 currentLayer->deselectAll();
319
320 int newPositionOffset = mFrame - clipboardFrames.cbegin()->first;
321 for (auto it = clipboardFrames.cbegin(); it != clipboardFrames.cend(); ++it)
322 {
323 int newPosition = it->first + newPositionOffset;
324
325 KeyFrame* keyFrameNewPos = currentLayer->getKeyFrameWhichCovers(newPosition);
326
327 if (keyFrameNewPos != nullptr) {
328
329 // Select and move any frames that may come into contact with the new position
330 currentLayer->newSelectionOfConnectedFrames(newPosition);
331 currentLayer->moveSelectedFrames(1);
332 }
333
334 KeyFrame* key = it->second;
335 // It's a bug if the keyframe is nullptr at this point...
336 Q_ASSERT(key != nullptr);
337
338 // TODO: undo/redo implementation
339 currentLayer->addKeyFrame(newPosition, key);
340 if (currentLayer->type() == Layer::SOUND)
341 {
342 auto soundClip = static_cast<SoundClip*>(key);
343 sound()->loadSound(soundClip, soundClip->fileName());
344 }
345
346 currentLayer->setFrameSelected(key->pos(), true);
347 }
348
349 layers()->notifyAnimationLengthChanged();
350}
351
352void Editor::paste()
353{
354 Layer* currentLayer = layers()->currentLayer();
355
356 Q_ASSERT(currentLayer != nullptr);
357
358 if (!canPaste()) { return; }
359
360 if (clipboards()->framesIsEmpty()) {
361
362 backup(tr("Paste"));
363
364 clipboards()->setFromSystemClipboard(mScribbleArea->getCentralPoint(), currentLayer);
365
366 BitmapImage clipboardImage = clipboards()->getBitmapClipboard();
367 VectorImage clipboardVectorImage = clipboards()->getVectorClipboard();
368 if (currentLayer->type() == Layer::BITMAP && clipboardImage.isLoaded()) {
369 pasteToCanvas(&clipboardImage, mFrame);
370 } else if (currentLayer->type() == Layer::VECTOR && !clipboardVectorImage.isEmpty()) {
371 pasteToCanvas(&clipboardVectorImage, mFrame);
372 }
373 } else {
374 // TODO: implement undo/redo
375 pasteToFrames();
376 }
377
378 emit frameModified(mFrame);
379}
380
381void Editor::flipSelection(bool flipVertical)
382{
383 if (flipVertical) {
384 backup(tr("Flip selection vertically"));
385 } else {
386 backup(tr("Flip selection horizontally"));
387 }
388 mScribbleArea->flipSelection(flipVertical);
389}
390
391void Editor::repositionImage(QPoint transform, int frame)
392{
393 if (layers()->currentLayer()->type() == Layer::BITMAP)
394 {
395 scrubTo(frame);
396 LayerBitmap* layer = static_cast<LayerBitmap*>(layers()->currentLayer());
397 QRect reposRect = layer->getFrameBounds(frame);
398 select()->setSelection(reposRect);
399 QPoint point = reposRect.topLeft();
400 point += transform;
401 layer->repositionFrame(point, frame);
402 backup(layer->id(), frame, tr("Reposition frame")); // TOOD: backup multiple reposition operations.
403 }
404}
405
406void Editor::setModified(int layerNumber, int frameNumber)
407{
408 Layer* layer = object()->getLayer(layerNumber);
409 if (layer == nullptr) { return; }
410
411 layer->setModified(frameNumber, true);
412 undoRedo()->rememberLastModifiedFrame(layerNumber, frameNumber);
413
414 emit frameModified(frameNumber);
415}
416
417void Editor::clipboardChanged()
418{
419 Layer* layer = layers()->currentLayer();
420
421 clipboards()->setFromSystemClipboard(mScribbleArea->getCentralPoint(), layer);
422
423 bool canCopyState = canCopy();
424 bool canPasteState = canPaste();
425
426 emit canCopyChanged(canCopyState);
427 emit canPasteChanged(canPasteState);
428}
429
430void Editor::setLayerVisibility(LayerVisibility visibility) {
431 mScribbleArea->setLayerVisibility(visibility);
432 emit updateTimeLine();
433}
434
435LayerVisibility Editor::layerVisibility()
436{
437 return mScribbleArea->getLayerVisibility();
438}
439
440qreal Editor::viewScaleInversed()
441{
442 return view()->getScaleInversed();
443}
444
445void Editor::increaseLayerVisibilityIndex()
446{
447 mScribbleArea->increaseLayerVisibilityIndex();
448 emit updateTimeLine();
449}
450
451void Editor::decreaseLayerVisibilityIndex()
452{
453 mScribbleArea->decreaseLayerVisibilityIndex();
454 emit updateTimeLine();
455}
456
457void Editor::addTemporaryDir(QTemporaryDir* const dir)
458{
459 mTemporaryDirs.append(dir);
460}
461
462void Editor::clearTemporary()
463{
464 while(!mTemporaryDirs.isEmpty())
465 {
466 QTemporaryDir* t = mTemporaryDirs.takeLast();
467 t->remove();
468 delete t;
469 }
470}
471
472Status Editor::openObject(const QString& strFilePath, const std::function<void(int)>& progressChanged, const std::function<void(int)>& progressRangeChanged)
473{
474 // Check for potential issues with the file
475 Q_ASSERT(!strFilePath.isEmpty());
476 QFileInfo fileInfo(strFilePath);
477 DebugDetails dd;
478 dd << QString("Raw file path: %1").arg(strFilePath);
479 dd << QString("Resolved file path: %1").arg(fileInfo.absoluteFilePath());
480 if (fileInfo.isDir())
481 {
482 return Status(Status::ERROR_FILE_CANNOT_OPEN,
483 dd,
484 tr("Could not open file"),
485 tr("The file you have selected is a directory, so we are unable to open it. "
486 "If you are are trying to open a project that uses the old structure, "
487 "please open the file ending with .pcl, not the data folder."));
488 }
489 if (!fileInfo.exists())
490 {
491 return Status(Status::FILE_NOT_FOUND,
492 dd,
493 tr("Could not open file"),
494 tr("The file you have selected does not exist, so we are unable to open it. "
495 "Please make sure that you've entered the correct path and that the file is accessible and try again."));
496 }
497 if (!fileInfo.isReadable())
498 {
499 dd << QString("Permissions: 0x%1").arg(static_cast<int>(fileInfo.permissions()), 0, 16);
500 return Status(Status::ERROR_FILE_CANNOT_OPEN,
501 dd,
502 tr("Could not open file"),
503 tr("This program does not have permission to read the file you have selected. "
504 "Please check that you have read permissions for this file and try again."));
505 }
506
507 int progress = 0;
508 FileManager fm(this);
509 connect(&fm, &FileManager::progressChanged, [&progress, &progressChanged](int p)
510 {
511 progressChanged(progress = p);
512 });
513 connect(&fm, &FileManager::progressRangeChanged, [&progressRangeChanged](int max)
514 {
515 progressRangeChanged(max + 3);
516 });
517
518 QString fullPath = fileInfo.absoluteFilePath();
519
520 Object* object = fm.load(fullPath);
521
522 Status fmStatus = fm.error();
523 if (!fmStatus.ok())
524 {
525 dd.collect(fmStatus.details());
526 fmStatus.setDetails(dd);
527 return fmStatus;
528 }
529
530 if (object == nullptr)
531 {
532 return Status(Status::ERROR_FILE_CANNOT_OPEN,
533 dd,
534 tr("Could not open file"),
535 tr("An unknown error occurred while trying to load the file and we are not able to load your file."));
536 }
537
538 setObject(object);
539
540 progressChanged(progress + 1);
541
542 layers()->notifyAnimationLengthChanged();
543 setFps(playback()->fps());
544
545 return Status::OK;
546}
547
548Status Editor::setObject(Object* newObject)
549{
550 Q_ASSERT(newObject);
551
552 if (newObject == mObject.get())
553 {
554 return Status::SAFE;
555 }
556
557 mObject.reset(newObject);
558
559 updateObject();
560
561 // Make sure that object is fully loaded before calling managers.
562 for (BaseManager* m : mAllManagers)
563 {
564 m->load(mObject.get());
565 }
566 emit objectLoaded();
567
568 return Status::OK;
569}
570
571void Editor::updateObject()
572{
573 setCurrentLayerIndex(mObject->data()->getCurrentLayer());
574 scrubTo(mObject->data()->getCurrentFrame());
575
576 mAutosaveCounter = 0;
577 mAutosaveNeverAskAgain = false;
578
579 if (mPreferenceManager)
580 {
581 mObject->setActiveFramePoolSize(mPreferenceManager->getInt(SETTING::FRAME_POOL_SIZE));
582 }
583
584 emit updateLayerCount();
585}
586
587Status Editor::importBitmapImage(const QString& filePath, const QTransform& importTransform)
588{
589 QImageReader reader(filePath);
590
591 Q_ASSERT(layers()->currentLayer()->type() == Layer::BITMAP);
592 const auto layer = static_cast<LayerBitmap*>(layers()->currentLayer());
593
594 if (!layer->visible())
595 {
596 mScribbleArea->showLayerNotVisibleWarning();
597 return Status::SAFE;
598 }
599
600 Status status = Status::OK;
601 DebugDetails dd;
602 dd << QString("Raw file path: %1").arg(filePath);
603
604 QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied);
605 if (!reader.read(&img)) {
606 QString format = reader.format();
607 if (!format.isEmpty())
608 {
609 dd << QString("QImageReader format: %1").arg(format);
610 }
611 dd << QString("QImageReader ImageReaderError type: %1").arg(reader.errorString());
612
613 QString errorDesc;
614 switch(reader.error())
615 {
616 case QImageReader::ImageReaderError::FileNotFoundError:
617 errorDesc = tr("File not found at path \"%1\". Please check the image is present at the specified location and try again.").arg(filePath);
618 break;
619 case QImageReader::UnsupportedFormatError:
620 errorDesc = tr("Image format is not supported. Please convert the image file to one of the following formats and try again:\n%1")
621 .arg(QString::fromUtf8(reader.supportedImageFormats().join(", ")));
622 break;
623 default:
624 errorDesc = tr("An error has occurred while reading the image. Please check that the file is a valid image and try again.");
625 }
626
627 status = Status(Status::FAIL, dd, tr("Import failed"), errorDesc);
628 }
629
630 const QPoint pos = importTransform.map(QPoint(-img.width() / 2,
631 -img.height() / 2));
632
633 if (!layer->keyExists(mFrame))
634 {
635 const bool ok = addNewKey();
636 Q_ASSERT(ok);
637 }
638 BitmapImage* bitmapImage = layer->getBitmapImageAtFrame(mFrame);
639 BitmapImage importedBitmapImage(pos, img);
640 bitmapImage->paste(&importedBitmapImage);
641 emit frameModified(bitmapImage->pos());
642
643 scrubTo(mFrame+1);
644
645 backup(tr("Import Image"));
646
647 return status;
648}
649
650Status Editor::importVectorImage(const QString& filePath)
651{
652 Q_ASSERT(layers()->currentLayer()->type() == Layer::VECTOR);
653
654 auto layer = static_cast<LayerVector*>(layers()->currentLayer());
655
656 Status status = Status::OK;
657 DebugDetails dd;
658 dd << QString("Raw file path: %1").arg(filePath);
659
660 VectorImage* vectorImage = layer->getVectorImageAtFrame(currentFrame());
661 if (vectorImage == nullptr)
662 {
663 addNewKey();
664 vectorImage = layer->getVectorImageAtFrame(currentFrame());
665 }
666
667 VectorImage importedVectorImage;
668 bool ok = importedVectorImage.read(filePath);
669 if (ok)
670 {
671 importedVectorImage.selectAll();
672 vectorImage->paste(importedVectorImage);
673 emit frameModified(importedVectorImage.pos());
674
675 backup(tr("Import Image"));
676 }
677 else {
678 status = Status(Status::FAIL, dd, tr("Import failed"), tr("You cannot import images into a vector layer."));
679 }
680
681 return status;
682}
683
684Status Editor::importImage(const QString& filePath, const ImportImageConfig importConfig)
685{
686 Layer* layer = layers()->currentLayer();
687
688 DebugDetails dd;
689 dd << QString("Raw file path: %1").arg(filePath);
690
691 QTransform transform;
692 switch (importConfig.positionType)
693 {
694 case ImportImageConfig::CenterOfCamera: {
695 LayerCamera* layerCam = static_cast<LayerCamera*>(layers()->getCameraLayerBelow(currentLayerIndex()));
696 Q_ASSERT(layerCam);
697 transform = layerCam->getViewAtFrame(importConfig.importFrame).inverted();
698 break;
699 }
700 case ImportImageConfig::CenterOfCameraFollowed: {
701 LayerCamera* camera = static_cast<LayerCamera*>(layers()->getCameraLayerBelow(currentLayerIndex()));
702 Q_ASSERT(camera);
703 transform = camera->getViewAtFrame(currentFrame()).inverted();
704 break;
705 }
706 case ImportImageConfig::CenterOfView: {
707 QPointF centralPoint = mScribbleArea->getCentralPoint();
708 transform = QTransform::fromTranslate(centralPoint.x(), centralPoint.y());
709 break;
710 }
711 case ImportImageConfig::CenterOfCanvas:
712 case ImportImageConfig::None: {
713 transform = QTransform();
714 break;
715 }
716 }
717
718 switch (layer->type())
719 {
720 case Layer::BITMAP:
721 return importBitmapImage(filePath, transform);
722
723 case Layer::VECTOR:
724 return importVectorImage(filePath);
725
726 default:
727 dd << QString("Current layer: %1").arg(layer->type());
728 return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("You can only import images to a bitmap layer."));
729 }
730}
731
732Status Editor::importAnimatedImage(const QString& filePath, int frameSpacing, const std::function<void(int)>& progressChanged, const std::function<bool()>& wasCanceled)
733{
734 frameSpacing = qMax(1, frameSpacing);
735
736 DebugDetails dd;
737 dd << QString("Raw file path: %1").arg(filePath);
738
739 Layer* layer = layers()->currentLayer();
740 if (layer->type() != Layer::BITMAP)
741 {
742 dd << QString("Current layer: %1").arg(layer->type());
743 return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("You can only import images to a bitmap layer."));
744 }
745 LayerBitmap* bitmapLayer = static_cast<LayerBitmap*>(layers()->currentLayer());
746
747 QImageReader reader(filePath);
748 dd << QString("QImageReader format: %1").arg(QString(reader.format()));
749 if (!reader.supportsAnimation()) {
750 return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("The selected image has a format that does not support animation."));
751 }
752
753 QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied);
754 const QPoint pos(view()->getView().dx() - (img.width() / 2),
755 view()->getView().dy() - (img.height() / 2));
756 int totalFrames = reader.imageCount();
757 while (reader.read(&img))
758 {
759 if (reader.error())
760 {
761 dd << QString("QImageReader ImageReaderError type: %1").arg(reader.errorString());
762
763 QString errorDesc;
764 switch(reader.error())
765 {
766 case QImageReader::ImageReaderError::FileNotFoundError:
767 errorDesc = tr("File not found at path \"%1\". Please check the image is present at the specified location and try again.").arg(filePath);
768 break;
769 case QImageReader::UnsupportedFormatError:
770 errorDesc = tr("Image format is not supported. Please convert the image file to one of the following formats and try again:\n%1")
771 .arg((QString)reader.supportedImageFormats().join(", "));
772 break;
773 default:
774 errorDesc = tr("An error has occurred while reading the image. Please check that the file is a valid image and try again.");
775 }
776
777 return Status(Status::FAIL, dd, tr("Import failed"), errorDesc);
778 }
779
780 if (!bitmapLayer->keyExists(mFrame))
781 {
782 addNewKey();
783 }
784 BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(mFrame);
785 BitmapImage importedBitmapImage(pos, img);
786 bitmapImage->paste(&importedBitmapImage);
787 emit frameModified(bitmapImage->pos());
788
789 if (wasCanceled())
790 {
791 break;
792 }
793
794 scrubTo(mFrame + frameSpacing);
795
796 backup(tr("Import Image"));
797
798 progressChanged(qFloor(qMin(static_cast<double>(reader.currentImageNumber()) / totalFrames, 1.0) * 100));
799 }
800
801 return Status::OK;
802}
803
804void Editor::selectAll() const
805{
806 Layer* layer = layers()->currentLayer();
807
808 QRectF rect;
809 if (layer->type() == Layer::BITMAP)
810 {
811 // Selects the drawn area (bigger or smaller than the screen). It may be more accurate to select all this way
812 // as the drawing area is not limited
813 BitmapImage *bitmapImage = static_cast<BitmapImage*>(layer->getLastKeyFrameAtPosition(mFrame));
814 if (bitmapImage == nullptr) { return; }
815
816 rect = bitmapImage->bounds();
817 }
818 else if (layer->type() == Layer::VECTOR)
819 {
820 VectorImage *vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mFrame));
821 if (vectorImage != nullptr)
822 {
823 vectorImage->selectAll();
824 rect = vectorImage->getSelectionRect();
825 }
826 }
827 select()->setSelection(rect, false);
828}
829
830void Editor::deselectAll() const
831{
832 select()->resetSelectionProperties();
833
834 Layer* layer = layers()->currentLayer();
835 if (layer == nullptr) { return; }
836
837 if (layer->type() == Layer::VECTOR)
838 {
839 VectorImage *vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mFrame));
840 if (vectorImage != nullptr)
841 {
842 vectorImage->deselectAll();
843 }
844 }
845
846 if (layer->hasAnySelectedFrames()) {
847 layer->deselectAll();
848 emit updateTimeLine();
849 }
850}
851
852void Editor::updateFrame()
853{
854 mScribbleArea->updateFrame();
855}
856
857void Editor::setCurrentLayerIndex(int i)
858{
859 mCurrentLayerIndex = i;
860
861 Layer* layer = mObject->getLayer(i);
862 for (auto mgr : mAllManagers)
863 {
864 mgr->workingLayerChanged(layer);
865 }
866}
867
868void Editor::scrubTo(int frame)
869{
870 if (frame < 1) { frame = 1; }
871 mFrame = frame;
872
873 // FIXME: should not emit Timeline update here.
874 // Editor must be an individual class.
875 // Will remove all Timeline related code in Editor class.
876 if (mPlaybackManager && !mPlaybackManager->isPlaying())
877 {
878 emit updateTimeLineCached(); // needs to update the timeline to update onion skin positions
879 }
880 mObject->updateActiveFrames(frame);
881 emit scrubbed(frame);
882}
883
884void Editor::scrubForward()
885{
886 int nextFrame = mFrame + 1;
887 if (!playback()->isPlaying()) {
888 playback()->playScrub(nextFrame);
889 }
890 scrubTo(nextFrame);
891}
892
893void Editor::scrubBackward()
894{
895 if (currentFrame() > 1)
896 {
897 int previousFrame = mFrame - 1;
898 if (!playback()->isPlaying()) {
899 playback()->playScrub(previousFrame);
900 }
901 scrubTo(previousFrame);
902 }
903}
904
905KeyFrame* Editor::addNewKey()
906{
907 return addKeyFrame(layers()->currentLayerIndex(), currentFrame());
908}
909
910KeyFrame* Editor::addKeyFrame(const int layerNumber, int frameIndex)
911{
912 Layer* layer = mObject->getLayer(layerNumber);
913 Q_ASSERT(layer);
914
915 if (!layer->visible())
916 {
917 mScribbleArea->showLayerNotVisibleWarning();
918 return nullptr;
919 }
920
921 // Find next available space for a keyframe (where either no key exists or there is an empty sound key)
922 while (layer->keyExists(frameIndex))
923 {
924 if (layer->type() == Layer::SOUND
925 && layer->getKeyFrameAt(frameIndex)->fileName().isEmpty()
926 && layer->removeKeyFrame(frameIndex))
927 {
928 break;
929 }
930 else
931 {
932 frameIndex += 1;
933 }
934 }
935
936 const bool ok = layer->addNewKeyFrameAt(frameIndex);
937 Q_ASSERT(ok); // We already ensured that there is no keyframe at frameIndex, so this should always succeed
938 scrubTo(frameIndex); // currentFrameChanged() emit inside.
939 emit frameModified(frameIndex);
940 layers()->notifyAnimationLengthChanged();
941 return layer->getKeyFrameAt(frameIndex);
942}
943
944void Editor::removeKey()
945{
946 Layer* layer = layers()->currentLayer();
947 Q_ASSERT(layer != nullptr);
948
949 if (!layer->visible())
950 {
951 mScribbleArea->showLayerNotVisibleWarning();
952 return;
953 }
954
955 if (!layer->keyExistsWhichCovers(currentFrame()))
956 {
957 scrubBackward();
958 return;
959 }
960
961 backup(tr("Remove frame"));
962
963 deselectAll();
964 layer->removeKeyFrame(currentFrame());
965 layers()->notifyAnimationLengthChanged();
966 emit layers()->currentLayerChanged(layers()->currentLayerIndex()); // trigger timeline repaint.
967}
968
969void Editor::scrubNextKeyFrame()
970{
971 Layer* currentLayer = layers()->currentLayer();
972 Q_ASSERT(currentLayer);
973
974 int nextPosition = currentLayer->getNextKeyFramePosition(currentFrame());
975 if (currentFrame() >= currentLayer->getMaxKeyFramePosition()) nextPosition = currentFrame() + 1;
976 scrubTo(nextPosition);
977}
978
979void Editor::scrubPreviousKeyFrame()
980{
981 Layer* layer = mObject->getLayer(layers()->currentLayerIndex());
982 Q_ASSERT(layer);
983
984 int prevPosition = layer->getPreviousKeyFramePosition(currentFrame());
985 scrubTo(prevPosition);
986}
987
988void Editor::switchVisibilityOfLayer(int layerNumber)
989{
990 Layer* layer = mObject->getLayer(layerNumber);
991 if (layer != nullptr) layer->switchVisibility();
992 mScribbleArea->onLayerChanged();
993
994 emit updateTimeLine();
995}
996
997void Editor::swapLayers(int i, int j)
998{
999 bool didSwapLayer = mObject->swapLayers(i, j);
1000 if (!didSwapLayer) { return; }
1001
1002 if (j < i)
1003 {
1004 layers()->setCurrentLayer(j + 1);
1005 }
1006 else
1007 {
1008 layers()->setCurrentLayer(j - 1);
1009 }
1010 emit updateTimeLine();
1011 mScribbleArea->onLayerChanged();
1012}
1013
1014bool Editor::canSwapLayers(int layerIndexLeft, int layerIndexRight) const
1015{
1016 return mObject->canSwapLayers(layerIndexLeft, layerIndexRight);
1017}
1018
1019void Editor::prepareSave()
1020{
1021 for (auto mgr : mAllManagers)
1022 {
1023 mgr->save(mObject.get());
1024 }
1025}
1026
1027void Editor::clearCurrentFrame()
1028{
1029 mScribbleArea->clearImage();
1030}
1031
1032bool Editor::canCopy() const
1033{
1034 Layer* layer = layers()->currentLayer();
1035 KeyFrame* keyframe = layer->getLastKeyFrameAtPosition(mFrame);
1036
1037 switch (layer->type())
1038 {
1039 case Layer::SOUND:
1040 case Layer::CAMERA:
1041 return canCopyFrames(layer);
1042 case Layer::BITMAP:
1043 return canCopyBitmapImage(static_cast<BitmapImage*>(keyframe)) || canCopyFrames(layer);
1044 case Layer::VECTOR:
1045 return canCopyVectorImage(static_cast<VectorImage*>(keyframe)) || canCopyFrames(layer);
1046 default:
1047 Q_UNREACHABLE();
1048 }
1049}
1050
1051bool Editor::canPaste() const
1052{
1053 Layer* layer = layers()->currentLayer();
1054 auto clipboardMan = clipboards();
1055 auto layerType = layer->type();
1056
1057 return (layerType == clipboardMan->framesLayerType() && !clipboardMan->framesIsEmpty()) ||
1058 (layerType == Layer::BITMAP && clipboardMan->getBitmapClipboard().isLoaded()) ||
1059 (layerType == Layer::VECTOR && !clipboardMan->getVectorClipboard().isEmpty());
1060}
1061
1062bool Editor::canCopyFrames(const Layer* layer) const
1063{
1064 Q_ASSERT(layer != nullptr);
1065 return layer->hasAnySelectedFrames();
1066}
1067
1068bool Editor::canCopyBitmapImage(BitmapImage* bitmapImage) const
1069{
1070 return bitmapImage != nullptr && bitmapImage->isLoaded() && !bitmapImage->bounds().isEmpty();
1071}
1072
1073bool Editor::canCopyVectorImage(const VectorImage* vectorImage) const
1074{
1075 return vectorImage != nullptr && !vectorImage->isEmpty();
1076}
1077
1078void Editor::backup(const QString &undoText)
1079{
1080 undoRedo()->legacyBackup(undoText);
1081 updateAutoSaveCounter();
1082}
1083
1084bool Editor::backup(int layerNumber, int frameNumber, const QString &undoText)
1085{
1086 bool didBackup = undoRedo()->legacyBackup(layerNumber, frameNumber, undoText);
1087
1088 updateAutoSaveCounter();
1089 return didBackup;
1090}
BaseManager
Definition: basemanager.h:29
BitmapImage
Definition: bitmapimage.h:28
ClipboardManager
Definition: clipboardmanager.h:31
ClipboardManager::getClipboardFrames
std::map< int, KeyFrame * > getClipboardFrames()
Return a copy of all clipboard frames keyed by their position.
Definition: clipboardmanager.cpp:100
ClipboardManager::copySelectedFrames
void copySelectedFrames(const Layer *currentLayer)
Copy selected keyframes of any given layer and remember its type.
Definition: clipboardmanager.cpp:82
ClipboardManager::copyVectorImage
void copyVectorImage(const VectorImage *vectorImage)
Copy the entire vector image to clipboard, this operation does not yet support partial selections.
Definition: clipboardmanager.cpp:73
ClipboardManager::copyBitmapImage
void copyBitmapImage(BitmapImage *image, QRectF selectionRect)
Copy bitmap image to clipboard and save its latest position Additionally only a part of the image wil...
Definition: clipboardmanager.cpp:56
ColorManager
Definition: colormanager.h:27
DebugDetails
Definition: pencilerror.h:25
Editor::addNewKey
KeyFrame * addNewKey()
Attempts to create a new keyframe at the current frame and layer.
Definition: editor.cpp:905
Editor::frameModified
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
Editor::updateFrame
void updateFrame()
Will call update() and update the canvas Only call this directly If you need the cache to be intact a...
Definition: editor.cpp:852
Editor::scrubbed
void scrubbed(int frameNumber)
This should be emitted after scrubbing.
Editor::setLayerVisibility
void setLayerVisibility(LayerVisibility visibility)
The visibility value should match any of the VISIBILITY enum values.
Definition: editor.cpp:430
Editor::addKeyFrame
KeyFrame * addKeyFrame(int layerNumber, int frameIndex)
Attempts to create a new keyframe at the given position and layer.
Definition: editor.cpp:910
FileManager
Definition: filemanager.h:37
KeyFrame
Definition: keyframe.h:30
LayerBitmap
Definition: layerbitmap.h:26
LayerCamera
Definition: layercamera.h:30
Layer
Definition: layer.h:33
Layer::newSelectionOfConnectedFrames
bool newSelectionOfConnectedFrames(int position)
Make a selection from specified position until a blank spot appears The search is only looking forwar...
Definition: layer.cpp:453
Layer::addNewKeyFrameAt
bool addNewKeyFrameAt(int position)
Creates a new keyframe at the given position, unless one already exists.
Definition: layer.cpp:170
Layer::selectedKeyFramesPositions
QList< int > selectedKeyFramesPositions() const
Get selected keyframe positions sorted by position.
Definition: layer.h:62
Layer::addKeyFrame
virtual bool addKeyFrame(int position, KeyFrame *pKeyFrame)
Adds a keyframe at the given position, unless one already exists.
Definition: layer.cpp:191
LayerManager
Definition: layermanager.h:31
LayerManager::notifyAnimationLengthChanged
void notifyAnimationLengthChanged()
This should be emitted whenever the animation length frames, eg.
Definition: layermanager.cpp:405
LayerVector
Definition: layervector.h:26
Object
Definition: object.h:42
OverlayManager
Definition: overlaymanager.h:32
PlaybackManager
Definition: playbackmanager.h:30
PreferenceManager
Definition: preferencemanager.h:28
ScribbleArea::updateFrame
void updateFrame()
Update frame.
Definition: scribblearea.cpp:199
ScribbleArea::handleDrawingOnEmptyFrame
void handleDrawingOnEmptyFrame()
Call this when starting to use a paint tool.
Definition: scribblearea.cpp:827
ScribbleArea::onOnionSkinTypeChanged
void onOnionSkinTypeChanged()
Onion skin type changed, all frames will be affected.
Definition: scribblearea.cpp:369
ScribbleArea::onLayerChanged
void onLayerChanged()
Layer changed, invalidate relevant cache.
Definition: scribblearea.cpp:357
SelectionManager
Definition: selectionmanager.h:33
SoundClip
Definition: soundclip.h:27
SoundManager
Definition: soundmanager.h:30
Status
Definition: pencilerror.h:40
ToolManager
Definition: toolmanager.h:30
UndoRedoManager
Definition: undoredomanager.h:91
UndoRedoManager::sanitizeLegacyBackupElementsAfterLayerDeletion
void sanitizeLegacyBackupElementsAfterLayerDeletion(int layerIndex)
Restores integrity of the backup elements after a layer has been deleted.
Definition: undoredomanager.cpp:509
VectorImage
Definition: vectorimage.h:32
VectorImage::deselectAll
void deselectAll()
VectorImage::deselectAll.
Definition: vectorimage.cpp:839
VectorImage::paste
void paste(VectorImage &)
VectorImage::paste.
Definition: vectorimage.cpp:1049
VectorImage::read
bool read(QString filePath)
VectorImage::read.
Definition: vectorimage.cpp:73
VectorImage::selectAll
void selectAll()
VectorImage::selectAll.
Definition: vectorimage.cpp:813
ViewManager
Definition: viewmanager.h:26
ViewManager::getScaleInversed
qreal getScaleInversed() const
Definition: viewmanager.cpp:111
QFileInfo
QImage
QImage::Format_ARGB32_Premultiplied
Format_ARGB32_Premultiplied
QImageReader
QImageReader::UnsupportedFormatError
UnsupportedFormatError
QList::append
void append(const T &value)
QList::isEmpty
bool isEmpty() const const
QList::takeLast
T takeLast()
QObject
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QPoint
QPointF
QPointF::x
qreal x() const const
QPointF::y
qreal y() const const
QRect
QRect::isEmpty
bool isEmpty() const const
QRect::topLeft
QPoint topLeft() const const
QRectF
QRectF::height
qreal height() const const
QRectF::toRect
QRect toRect() const const
QRectF::topLeft
QPointF topLeft() const const
QRectF::width
qreal width() const const
QString
QString::arg
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString::fromUtf8
QString fromUtf8(const char *str, int size)
QString::isEmpty
bool isEmpty() const const
QTemporaryDir
QTemporaryDir::remove
bool remove()
QTransform
QTransform::fromTranslate
QTransform fromTranslate(qreal dx, qreal dy)
QTransform::inverted
QTransform inverted(bool *invertible) const const
QTransform::map
QPoint map(const QPoint &point) const const
ImportImageConfig
Definition: importimageconfig.h:22
Generated on Tue Dec 9 2025 11:50:20 for Pencil2D by doxygen 1.9.6 based on revision 97e4764d4c0161ef1d2d375f7b616b3eb8bf2487