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
350void Editor::paste()
351{
352 Layer* currentLayer = layers()->currentLayer();
353
354 Q_ASSERT(currentLayer != nullptr);
355
356 if (!canPaste()) { return; }
357
358 if (clipboards()->framesIsEmpty()) {
359
360 backup(tr("Paste"));
361
362 clipboards()->setFromSystemClipboard(mScribbleArea->getCentralPoint(), currentLayer);
363
364 BitmapImage clipboardImage = clipboards()->getBitmapClipboard();
365 VectorImage clipboardVectorImage = clipboards()->getVectorClipboard();
366 if (currentLayer->type() == Layer::BITMAP && clipboardImage.isLoaded()) {
367 pasteToCanvas(&clipboardImage, mFrame);
368 } else if (currentLayer->type() == Layer::VECTOR && !clipboardVectorImage.isEmpty()) {
369 pasteToCanvas(&clipboardVectorImage, mFrame);
370 }
371 } else {
372 // TODO: implement undo/redo
373 pasteToFrames();
374 }
375
376 emit frameModified(mFrame);
377}
378
379void Editor::flipSelection(bool flipVertical)
380{
381 if (flipVertical) {
382 backup(tr("Flip selection vertically"));
383 } else {
384 backup(tr("Flip selection horizontally"));
385 }
386 mScribbleArea->flipSelection(flipVertical);
387}
388
389void Editor::repositionImage(QPoint transform, int frame)
390{
391 if (layers()->currentLayer()->type() == Layer::BITMAP)
392 {
393 scrubTo(frame);
394 LayerBitmap* layer = static_cast<LayerBitmap*>(layers()->currentLayer());
395 QRect reposRect = layer->getFrameBounds(frame);
396 select()->setSelection(reposRect);
397 QPoint point = reposRect.topLeft();
398 point += transform;
399 layer->repositionFrame(point, frame);
400 backup(layer->id(), frame, tr("Reposition frame")); // TOOD: backup multiple reposition operations.
401 }
402}
403
404void Editor::setModified(int layerNumber, int frameNumber)
405{
406 Layer* layer = object()->getLayer(layerNumber);
407 if (layer == nullptr) { return; }
408
409 layer->setModified(frameNumber, true);
410 undoRedo()->rememberLastModifiedFrame(layerNumber, frameNumber);
411
412 emit frameModified(frameNumber);
413}
414
415void Editor::clipboardChanged()
416{
417 Layer* layer = layers()->currentLayer();
418
419 clipboards()->setFromSystemClipboard(mScribbleArea->getCentralPoint(), layer);
420
421 bool canCopyState = canCopy();
422 bool canPasteState = canPaste();
423
424 emit canCopyChanged(canCopyState);
425 emit canPasteChanged(canPasteState);
426}
427
428void Editor::setLayerVisibility(LayerVisibility visibility) {
429 mScribbleArea->setLayerVisibility(visibility);
430 emit updateTimeLine();
431}
432
433LayerVisibility Editor::layerVisibility()
434{
435 return mScribbleArea->getLayerVisibility();
436}
437
438qreal Editor::viewScaleInversed()
439{
440 return view()->getScaleInversed();
441}
442
443void Editor::increaseLayerVisibilityIndex()
444{
445 mScribbleArea->increaseLayerVisibilityIndex();
446 emit updateTimeLine();
447}
448
449void Editor::decreaseLayerVisibilityIndex()
450{
451 mScribbleArea->decreaseLayerVisibilityIndex();
452 emit updateTimeLine();
453}
454
455void Editor::addTemporaryDir(QTemporaryDir* const dir)
456{
457 mTemporaryDirs.append(dir);
458}
459
460void Editor::clearTemporary()
461{
462 while(!mTemporaryDirs.isEmpty())
463 {
464 QTemporaryDir* t = mTemporaryDirs.takeLast();
465 t->remove();
466 delete t;
467 }
468}
469
470Status Editor::openObject(const QString& strFilePath, const std::function<void(int)>& progressChanged, const std::function<void(int)>& progressRangeChanged)
471{
472 // Check for potential issues with the file
473 Q_ASSERT(!strFilePath.isEmpty());
474 QFileInfo fileInfo(strFilePath);
475 DebugDetails dd;
476 dd << QString("Raw file path: %1").arg(strFilePath);
477 dd << QString("Resolved file path: %1").arg(fileInfo.absoluteFilePath());
478 if (fileInfo.isDir())
479 {
480 return Status(Status::ERROR_FILE_CANNOT_OPEN,
481 dd,
482 tr("Could not open file"),
483 tr("The file you have selected is a directory, so we are unable to open it. "
484 "If you are are trying to open a project that uses the old structure, "
485 "please open the file ending with .pcl, not the data folder."));
486 }
487 if (!fileInfo.exists())
488 {
489 return Status(Status::FILE_NOT_FOUND,
490 dd,
491 tr("Could not open file"),
492 tr("The file you have selected does not exist, so we are unable to open it. "
493 "Please make sure that you've entered the correct path and that the file is accessible and try again."));
494 }
495 if (!fileInfo.isReadable())
496 {
497 dd << QString("Permissions: 0x%1").arg(fileInfo.permissions(), 0, 16);
498 return Status(Status::ERROR_FILE_CANNOT_OPEN,
499 dd,
500 tr("Could not open file"),
501 tr("This program does not have permission to read the file you have selected. "
502 "Please check that you have read permissions for this file and try again."));
503 }
504
505 int progress = 0;
506 FileManager fm(this);
507 connect(&fm, &FileManager::progressChanged, [&progress, &progressChanged](int p)
508 {
509 progressChanged(progress = p);
510 });
511 connect(&fm, &FileManager::progressRangeChanged, [&progressRangeChanged](int max)
512 {
513 progressRangeChanged(max + 3);
514 });
515
516 QString fullPath = fileInfo.absoluteFilePath();
517
518 Object* object = fm.load(fullPath);
519
520 Status fmStatus = fm.error();
521 if (!fmStatus.ok())
522 {
523 dd.collect(fmStatus.details());
524 fmStatus.setDetails(dd);
525 return fmStatus;
526 }
527
528 if (object == nullptr)
529 {
530 return Status(Status::ERROR_FILE_CANNOT_OPEN,
531 dd,
532 tr("Could not open file"),
533 tr("An unknown error occurred while trying to load the file and we are not able to load your file."));
534 }
535
536 setObject(object);
537
538 progressChanged(progress + 1);
539
540 layers()->notifyAnimationLengthChanged();
541 setFps(playback()->fps());
542
543 return Status::OK;
544}
545
546Status Editor::setObject(Object* newObject)
547{
548 Q_ASSERT(newObject);
549
550 if (newObject == mObject.get())
551 {
552 return Status::SAFE;
553 }
554
555 mObject.reset(newObject);
556
557 updateObject();
558
559 // Make sure that object is fully loaded before calling managers.
560 for (BaseManager* m : mAllManagers)
561 {
562 m->load(mObject.get());
563 }
564 emit objectLoaded();
565
566 return Status::OK;
567}
568
569void Editor::updateObject()
570{
571 setCurrentLayerIndex(mObject->data()->getCurrentLayer());
572 scrubTo(mObject->data()->getCurrentFrame());
573
574 mAutosaveCounter = 0;
575 mAutosaveNeverAskAgain = false;
576
577 if (mPreferenceManager)
578 {
579 mObject->setActiveFramePoolSize(mPreferenceManager->getInt(SETTING::FRAME_POOL_SIZE));
580 }
581
582 emit updateLayerCount();
583}
584
585Status Editor::importBitmapImage(const QString& filePath, const QTransform& importTransform)
586{
587 QImageReader reader(filePath);
588
589 Q_ASSERT(layers()->currentLayer()->type() == Layer::BITMAP);
590 const auto layer = static_cast<LayerBitmap*>(layers()->currentLayer());
591
592 if (!layer->visible())
593 {
594 mScribbleArea->showLayerNotVisibleWarning();
595 return Status::SAFE;
596 }
597
598 Status status = Status::OK;
599 DebugDetails dd;
600 dd << QString("Raw file path: %1").arg(filePath);
601
602 QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied);
603 if (!reader.read(&img)) {
604 QString format = reader.format();
605 if (!format.isEmpty())
606 {
607 dd << QString("QImageReader format: %1").arg(format);
608 }
609 dd << QString("QImageReader ImageReaderError type: %1").arg(reader.errorString());
610
611 QString errorDesc;
612 switch(reader.error())
613 {
614 case QImageReader::ImageReaderError::FileNotFoundError:
615 errorDesc = tr("File not found at path \"%1\". Please check the image is present at the specified location and try again.").arg(filePath);
616 break;
617 case QImageReader::UnsupportedFormatError:
618 errorDesc = tr("Image format is not supported. Please convert the image file to one of the following formats and try again:\n%1")
619 .arg(QString::fromUtf8(reader.supportedImageFormats().join(", ")));
620 break;
621 default:
622 errorDesc = tr("An error has occurred while reading the image. Please check that the file is a valid image and try again.");
623 }
624
625 status = Status(Status::FAIL, dd, tr("Import failed"), errorDesc);
626 }
627
628 const QPoint pos = importTransform.map(QPoint(-img.width() / 2,
629 -img.height() / 2));
630
631 if (!layer->keyExists(mFrame))
632 {
633 const bool ok = addNewKey();
634 Q_ASSERT(ok);
635 }
636 BitmapImage* bitmapImage = layer->getBitmapImageAtFrame(mFrame);
637 BitmapImage importedBitmapImage(pos, img);
638 bitmapImage->paste(&importedBitmapImage);
639 emit frameModified(bitmapImage->pos());
640
641 scrubTo(mFrame+1);
642
643 backup(tr("Import Image"));
644
645 return status;
646}
647
648Status Editor::importVectorImage(const QString& filePath)
649{
650 Q_ASSERT(layers()->currentLayer()->type() == Layer::VECTOR);
651
652 auto layer = static_cast<LayerVector*>(layers()->currentLayer());
653
654 Status status = Status::OK;
655 DebugDetails dd;
656 dd << QString("Raw file path: %1").arg(filePath);
657
658 VectorImage* vectorImage = layer->getVectorImageAtFrame(currentFrame());
659 if (vectorImage == nullptr)
660 {
661 addNewKey();
662 vectorImage = layer->getVectorImageAtFrame(currentFrame());
663 }
664
665 VectorImage importedVectorImage;
666 bool ok = importedVectorImage.read(filePath);
667 if (ok)
668 {
669 importedVectorImage.selectAll();
670 vectorImage->paste(importedVectorImage);
671 emit frameModified(importedVectorImage.pos());
672
673 backup(tr("Import Image"));
674 }
675 else {
676 status = Status(Status::FAIL, dd, tr("Import failed"), tr("You cannot import images into a vector layer."));
677 }
678
679 return status;
680}
681
682Status Editor::importImage(const QString& filePath, const ImportImageConfig importConfig)
683{
684 Layer* layer = layers()->currentLayer();
685
686 DebugDetails dd;
687 dd << QString("Raw file path: %1").arg(filePath);
688
689 QTransform transform;
690 switch (importConfig.positionType)
691 {
692 case ImportImageConfig::CenterOfCamera: {
693 LayerCamera* layerCam = static_cast<LayerCamera*>(layers()->getCameraLayerBelow(currentLayerIndex()));
694 Q_ASSERT(layerCam);
695 transform = layerCam->getViewAtFrame(importConfig.importFrame).inverted();
696 break;
697 }
698 case ImportImageConfig::CenterOfCameraFollowed: {
699 LayerCamera* camera = static_cast<LayerCamera*>(layers()->getCameraLayerBelow(currentLayerIndex()));
700 Q_ASSERT(camera);
701 transform = camera->getViewAtFrame(currentFrame()).inverted();
702 break;
703 }
704 case ImportImageConfig::CenterOfView: {
705 QPointF centralPoint = mScribbleArea->getCentralPoint();
706 transform = QTransform::fromTranslate(centralPoint.x(), centralPoint.y());
707 break;
708 }
709 case ImportImageConfig::CenterOfCanvas:
710 case ImportImageConfig::None: {
711 transform = QTransform();
712 break;
713 }
714 }
715
716 switch (layer->type())
717 {
718 case Layer::BITMAP:
719 return importBitmapImage(filePath, transform);
720
721 case Layer::VECTOR:
722 return importVectorImage(filePath);
723
724 default:
725 dd << QString("Current layer: %1").arg(layer->type());
726 return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("You can only import images to a bitmap layer."));
727 }
728}
729
730Status Editor::importAnimatedImage(const QString& filePath, int frameSpacing, const std::function<void(int)>& progressChanged, const std::function<bool()>& wasCanceled)
731{
732 frameSpacing = qMax(1, frameSpacing);
733
734 DebugDetails dd;
735 dd << QString("Raw file path: %1").arg(filePath);
736
737 Layer* layer = layers()->currentLayer();
738 if (layer->type() != Layer::BITMAP)
739 {
740 dd << QString("Current layer: %1").arg(layer->type());
741 return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("You can only import images to a bitmap layer."));
742 }
743 LayerBitmap* bitmapLayer = static_cast<LayerBitmap*>(layers()->currentLayer());
744
745 QImageReader reader(filePath);
746 dd << QString("QImageReader format: %1").arg(QString(reader.format()));
747 if (!reader.supportsAnimation()) {
748 return Status(Status::ERROR_INVALID_LAYER_TYPE, dd, tr("Import failed"), tr("The selected image has a format that does not support animation."));
749 }
750
751 QImage img(reader.size(), QImage::Format_ARGB32_Premultiplied);
752 const QPoint pos(view()->getView().dx() - (img.width() / 2),
753 view()->getView().dy() - (img.height() / 2));
754 int totalFrames = reader.imageCount();
755 while (reader.read(&img))
756 {
757 if (reader.error())
758 {
759 dd << QString("QImageReader ImageReaderError type: %1").arg(reader.errorString());
760
761 QString errorDesc;
762 switch(reader.error())
763 {
764 case QImageReader::ImageReaderError::FileNotFoundError:
765 errorDesc = tr("File not found at path \"%1\". Please check the image is present at the specified location and try again.").arg(filePath);
766 break;
767 case QImageReader::UnsupportedFormatError:
768 errorDesc = tr("Image format is not supported. Please convert the image file to one of the following formats and try again:\n%1")
769 .arg((QString)reader.supportedImageFormats().join(", "));
770 break;
771 default:
772 errorDesc = tr("An error has occurred while reading the image. Please check that the file is a valid image and try again.");
773 }
774
775 return Status(Status::FAIL, dd, tr("Import failed"), errorDesc);
776 }
777
778 if (!bitmapLayer->keyExists(mFrame))
779 {
780 addNewKey();
781 }
782 BitmapImage* bitmapImage = bitmapLayer->getBitmapImageAtFrame(mFrame);
783 BitmapImage importedBitmapImage(pos, img);
784 bitmapImage->paste(&importedBitmapImage);
785 emit frameModified(bitmapImage->pos());
786
787 if (wasCanceled())
788 {
789 break;
790 }
791
792 scrubTo(mFrame + frameSpacing);
793
794 backup(tr("Import Image"));
795
796 progressChanged(qFloor(qMin(static_cast<double>(reader.currentImageNumber()) / totalFrames, 1.0) * 100));
797 }
798
799 return Status::OK;
800}
801
802void Editor::selectAll() const
803{
804 Layer* layer = layers()->currentLayer();
805
806 QRectF rect;
807 if (layer->type() == Layer::BITMAP)
808 {
809 // Selects the drawn area (bigger or smaller than the screen). It may be more accurate to select all this way
810 // as the drawing area is not limited
811 BitmapImage *bitmapImage = static_cast<BitmapImage*>(layer->getLastKeyFrameAtPosition(mFrame));
812 if (bitmapImage == nullptr) { return; }
813
814 rect = bitmapImage->bounds();
815 }
816 else if (layer->type() == Layer::VECTOR)
817 {
818 VectorImage *vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mFrame));
819 if (vectorImage != nullptr)
820 {
821 vectorImage->selectAll();
822 rect = vectorImage->getSelectionRect();
823 }
824 }
825 select()->setSelection(rect, false);
826}
827
828void Editor::deselectAll() const
829{
830 select()->resetSelectionProperties();
831
832 Layer* layer = layers()->currentLayer();
833 if (layer == nullptr) { return; }
834
835 if (layer->type() == Layer::VECTOR)
836 {
837 VectorImage *vectorImage = static_cast<VectorImage*>(layer->getLastKeyFrameAtPosition(mFrame));
838 if (vectorImage != nullptr)
839 {
840 vectorImage->deselectAll();
841 }
842 }
843
844 if (layer->hasAnySelectedFrames()) {
845 layer->deselectAll();
846 emit updateTimeLine();
847 }
848}
849
850void Editor::updateFrame()
851{
852 mScribbleArea->updateFrame();
853}
854
855void Editor::setCurrentLayerIndex(int i)
856{
857 mCurrentLayerIndex = i;
858
859 Layer* layer = mObject->getLayer(i);
860 for (auto mgr : mAllManagers)
861 {
862 mgr->workingLayerChanged(layer);
863 }
864}
865
866void Editor::scrubTo(int frame)
867{
868 if (frame < 1) { frame = 1; }
869 mFrame = frame;
870
871 // FIXME: should not emit Timeline update here.
872 // Editor must be an individual class.
873 // Will remove all Timeline related code in Editor class.
874 if (mPlaybackManager && !mPlaybackManager->isPlaying())
875 {
876 emit updateTimeLineCached(); // needs to update the timeline to update onion skin positions
877 }
878 mObject->updateActiveFrames(frame);
879 emit scrubbed(frame);
880}
881
882void Editor::scrubForward()
883{
884 int nextFrame = mFrame + 1;
885 if (!playback()->isPlaying()) {
886 playback()->playScrub(nextFrame);
887 }
888 scrubTo(nextFrame);
889}
890
891void Editor::scrubBackward()
892{
893 if (currentFrame() > 1)
894 {
895 int previousFrame = mFrame - 1;
896 if (!playback()->isPlaying()) {
897 playback()->playScrub(previousFrame);
898 }
899 scrubTo(previousFrame);
900 }
901}
902
903KeyFrame* Editor::addNewKey()
904{
905 return addKeyFrame(layers()->currentLayerIndex(), currentFrame());
906}
907
908KeyFrame* Editor::addKeyFrame(const int layerNumber, int frameIndex)
909{
910 Layer* layer = mObject->getLayer(layerNumber);
911 Q_ASSERT(layer);
912
913 if (!layer->visible())
914 {
915 mScribbleArea->showLayerNotVisibleWarning();
916 return nullptr;
917 }
918
919 // Find next available space for a keyframe (where either no key exists or there is an empty sound key)
920 while (layer->keyExists(frameIndex))
921 {
922 if (layer->type() == Layer::SOUND
923 && layer->getKeyFrameAt(frameIndex)->fileName().isEmpty()
924 && layer->removeKeyFrame(frameIndex))
925 {
926 break;
927 }
928 else
929 {
930 frameIndex += 1;
931 }
932 }
933
934 const bool ok = layer->addNewKeyFrameAt(frameIndex);
935 Q_ASSERT(ok); // We already ensured that there is no keyframe at frameIndex, so this should always succeed
936 scrubTo(frameIndex); // currentFrameChanged() emit inside.
937 emit frameModified(frameIndex);
938 layers()->notifyAnimationLengthChanged();
939 return layer->getKeyFrameAt(frameIndex);
940}
941
942void Editor::removeKey()
943{
944 Layer* layer = layers()->currentLayer();
945 Q_ASSERT(layer != nullptr);
946
947 if (!layer->visible())
948 {
949 mScribbleArea->showLayerNotVisibleWarning();
950 return;
951 }
952
953 if (!layer->keyExistsWhichCovers(currentFrame()))
954 {
955 scrubBackward();
956 return;
957 }
958
959 backup(tr("Remove frame"));
960
961 deselectAll();
962 layer->removeKeyFrame(currentFrame());
963 layers()->notifyAnimationLengthChanged();
964 emit layers()->currentLayerChanged(layers()->currentLayerIndex()); // trigger timeline repaint.
965}
966
967void Editor::scrubNextKeyFrame()
968{
969 Layer* currentLayer = layers()->currentLayer();
970 Q_ASSERT(currentLayer);
971
972 int nextPosition = currentLayer->getNextKeyFramePosition(currentFrame());
973 if (currentFrame() >= currentLayer->getMaxKeyFramePosition()) nextPosition = currentFrame() + 1;
974 scrubTo(nextPosition);
975}
976
977void Editor::scrubPreviousKeyFrame()
978{
979 Layer* layer = mObject->getLayer(layers()->currentLayerIndex());
980 Q_ASSERT(layer);
981
982 int prevPosition = layer->getPreviousKeyFramePosition(currentFrame());
983 scrubTo(prevPosition);
984}
985
986void Editor::switchVisibilityOfLayer(int layerNumber)
987{
988 Layer* layer = mObject->getLayer(layerNumber);
989 if (layer != nullptr) layer->switchVisibility();
990 mScribbleArea->onLayerChanged();
991
992 emit updateTimeLine();
993}
994
995void Editor::swapLayers(int i, int j)
996{
997 bool didSwapLayer = mObject->swapLayers(i, j);
998 if (!didSwapLayer) { return; }
999
1000 if (j < i)
1001 {
1002 layers()->setCurrentLayer(j + 1);
1003 }
1004 else
1005 {
1006 layers()->setCurrentLayer(j - 1);
1007 }
1008 emit updateTimeLine();
1009 mScribbleArea->onLayerChanged();
1010}
1011
1012bool Editor::canSwapLayers(int layerIndexLeft, int layerIndexRight) const
1013{
1014 return mObject->canSwapLayers(layerIndexLeft, layerIndexRight);
1015}
1016
1017void Editor::prepareSave()
1018{
1019 for (auto mgr : mAllManagers)
1020 {
1021 mgr->save(mObject.get());
1022 }
1023}
1024
1025void Editor::clearCurrentFrame()
1026{
1027 mScribbleArea->clearImage();
1028}
1029
1030bool Editor::canCopy() const
1031{
1032 Layer* layer = layers()->currentLayer();
1033 KeyFrame* keyframe = layer->getLastKeyFrameAtPosition(mFrame);
1034
1035 switch (layer->type())
1036 {
1037 case Layer::SOUND:
1038 case Layer::CAMERA:
1039 return canCopyFrames(layer);
1040 case Layer::BITMAP:
1041 return canCopyBitmapImage(static_cast<BitmapImage*>(keyframe)) || canCopyFrames(layer);
1042 case Layer::VECTOR:
1043 return canCopyVectorImage(static_cast<VectorImage*>(keyframe)) || canCopyFrames(layer);
1044 default:
1045 Q_UNREACHABLE();
1046 }
1047}
1048
1049bool Editor::canPaste() const
1050{
1051 Layer* layer = layers()->currentLayer();
1052 auto clipboardMan = clipboards();
1053 auto layerType = layer->type();
1054
1055 return (layerType == clipboardMan->framesLayerType() && !clipboardMan->framesIsEmpty()) ||
1056 (layerType == Layer::BITMAP && clipboardMan->getBitmapClipboard().isLoaded()) ||
1057 (layerType == Layer::VECTOR && !clipboardMan->getVectorClipboard().isEmpty());
1058}
1059
1060bool Editor::canCopyFrames(const Layer* layer) const
1061{
1062 Q_ASSERT(layer != nullptr);
1063 return layer->hasAnySelectedFrames();
1064}
1065
1066bool Editor::canCopyBitmapImage(BitmapImage* bitmapImage) const
1067{
1068 return bitmapImage != nullptr && bitmapImage->isLoaded() && !bitmapImage->bounds().isEmpty();
1069}
1070
1071bool Editor::canCopyVectorImage(const VectorImage* vectorImage) const
1072{
1073 return vectorImage != nullptr && !vectorImage->isEmpty();
1074}
1075
1076void Editor::backup(const QString &undoText)
1077{
1078 undoRedo()->legacyBackup(undoText);
1079 updateAutoSaveCounter();
1080}
1081
1082bool Editor::backup(int layerNumber, int frameNumber, const QString &undoText)
1083{
1084 bool didBackup = undoRedo()->legacyBackup(layerNumber, frameNumber, undoText);
1085
1086 updateAutoSaveCounter();
1087 return didBackup;
1088}
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:903
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:850
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:428
Editor::addKeyFrame
KeyFrame * addKeyFrame(int layerNumber, int frameIndex)
Attempts to create a new keyframe at the given position and layer.
Definition: editor.cpp:908
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:458
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:403
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 Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39