17 #include "actioncommands.h"
19 #include <QInputDialog>
20 #include <QMessageBox>
21 #include <QProgressDialog>
22 #include <QApplication>
23 #include <QDesktopServices>
24 #include <QStandardPaths>
25 #include <QFileDialog>
27 #include "pencildef.h"
30 #include "viewmanager.h"
31 #include "layermanager.h"
32 #include "scribblearea.h"
33 #include "soundmanager.h"
34 #include "playbackmanager.h"
35 #include "colormanager.h"
36 #include "preferencemanager.h"
37 #include "selectionmanager.h"
41 #include "layercamera.h"
42 #include "layersound.h"
43 #include "layerbitmap.h"
44 #include "layervector.h"
45 #include "bitmapimage.h"
46 #include "vectorimage.h"
47 #include "soundclip.h"
50 #include "movieimporter.h"
51 #include "movieexporter.h"
52 #include "filedialog.h"
53 #include "exportmoviedialog.h"
54 #include "exportimagedialog.h"
55 #include "aboutdialog.h"
56 #include "doubleprogressdialog.h"
57 #include "checkupdatesdialog.h"
58 #include "errordialog.h"
66 ActionCommands::~ActionCommands() {}
68 Status ActionCommands::importMovieVideo()
78 hideQuestionMark(progressDialog);
80 progressDialog.setMinimumWidth(250);
81 progressDialog.show();
85 information.setText(
tr(
"You are importing a lot of frames, beware this could take some time. Are you sure you want to proceed?"));
90 importer.setCore(mEditor);
94 Status st = importer.run(filePath, mEditor->playback()->fps(), FileType::MOVIE, [&progressDialog](
int prog) {
95 progressDialog.setValue(prog);
97 }, [&progressDialog](
QString progMessage) {
98 progressDialog.setLabelText(progMessage);
101 int ret = information.exec();
105 if (!st.ok() && st != Status::CANCELED)
107 ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent);
111 mEditor->layers()->notifyAnimationLengthChanged();
113 progressDialog.setValue(100);
114 progressDialog.close();
119 Status ActionCommands::importMovieAudio()
128 QProgressDialog progressDialog(
tr(
"Importing movie audio..."),
tr(
"Abort"), 0, 100, mParent);
129 hideQuestionMark(progressDialog);
131 progressDialog.show();
134 importer.setCore(mEditor);
138 Status st = importer.run(filePath, mEditor->playback()->fps(), FileType::SOUND, [&progressDialog](
int prog) {
139 progressDialog.setValue(prog);
141 }, [](
QString progressMessage) {
142 Q_UNUSED(progressMessage)
148 if (!st.ok() && st != Status::CANCELED)
150 ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent);
154 mEditor->layers()->notifyAnimationLengthChanged();
156 progressDialog.setValue(100);
157 progressDialog.close();
162 Status ActionCommands::importSound()
164 Layer* layer = mEditor->layers()->currentLayer();
165 if (layer ==
nullptr)
171 if (layer->type() != Layer::SOUND)
174 msg.
setText(
tr(
"No sound layer exists as a destination for your import. Create a new sound layer?"));
178 int buttonClicked = msg.
exec();
188 mEditor->layers()->nameSuggestLayer(
tr(
"Sound Layer",
"Default name on creating a sound layer")), &ok);
189 if (ok && !strLayerName.
isEmpty())
191 Layer* newLayer = mEditor->layers()->createSoundLayer(strLayerName);
192 mEditor->layers()->setCurrentLayer(newLayer);
200 layer = mEditor->layers()->currentLayer();
201 Q_ASSERT(layer->type() == Layer::SOUND);
204 int currentFrame = mEditor->currentFrame();
226 st = mEditor->sound()->loadSound(key, strSoundFile);
230 st = convertSoundToWav(strSoundFile);
235 layer->removeKeyFrame(currentFrame);
241 Status ActionCommands::convertSoundToWav(
const QString& filePath)
244 hideQuestionMark(progressDialog);
246 progressDialog.show();
249 importer.setCore(mEditor);
251 Status st = importer.run(filePath, mEditor->playback()->fps(), FileType::SOUND, [&progressDialog](
int prog) {
252 progressDialog.setValue(prog);
254 }, [](
QString progressMessage) {
255 Q_UNUSED(progressMessage)
263 if (!st.ok() && st != Status::CANCELED)
265 ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent);
271 Status ActionCommands::exportGif()
274 return exportMovie(
true);
277 Status ActionCommands::exportMovie(
bool isGif)
281 dialog =
new ExportMovieDialog(mParent, ImportExportDialog::Export, FileType::GIF);
289 std::vector< std::pair<QString, QSize> > camerasInfo;
290 auto cameraLayers = mEditor->object()->getLayersByType<
LayerCamera >();
293 camerasInfo.push_back(std::make_pair(i->name(), i->getViewSize()));
296 auto currLayer = mEditor->layers()->currentLayer();
297 if (currLayer->type() == Layer::CAMERA)
299 QString strName = currLayer->name();
300 auto it = std::find_if(camerasInfo.begin(), camerasInfo.end(),
301 [strName](std::pair<QString, QSize> p)
303 return p.first == strName;
306 Q_ASSERT(it != camerasInfo.end());
308 std::swap(camerasInfo[0], *it);
311 dialog->setCamerasInfo(camerasInfo);
316 dialog->setDefaultRange(1, length, lengthWithSounds);
323 QString strMoviePath = dialog->getFilePath();
326 desc.strFileName = strMoviePath;
327 desc.startFrame = dialog->getStartFrame();
328 desc.endFrame = dialog->getEndFrame();
329 desc.fps = mEditor->playback()->fps();
330 desc.exportSize = dialog->getExportSize();
331 desc.strCameraName = dialog->getSelectedCameraName();
332 desc.loop = dialog->getLoop();
333 desc.alpha = dialog->getTransparency();
344 connect(&progressDlg, &DoubleProgressDialog::canceled, [&ex]
350 float minorStart, minorLength;
352 Status st = ex.
run(mEditor->object(), desc,
353 [&progressDlg, &minorStart, &minorLength](
float f,
float final)
355 progressDlg.major->setValue(f);
358 minorLength = qMax(0.f,
final - minorStart);
362 [&progressDlg, &minorStart, &minorLength](
float f) {
363 progressDlg.minor->setValue(f);
365 progressDlg.major->setValue(minorStart + f * minorLength);
370 progressDlg.setStatus(s);
381 tr(
"Finished. Open file location?"));
385 QString path = dialog->getAbsolutePath();
391 tr(
"Finished. Open movie now?",
"When movie export done."));
399 ErrorDialog errorDialog(
tr(
"Unknown export error"),
tr(
"The export did not produce any errors, however we can't find the output file. Your export may not have completed successfully."),
QString(), mParent);
403 else if(st != Status::CANCELED)
405 ErrorDialog errorDialog(st.title(), st.description(), st.details().html(), mParent);
412 Status ActionCommands::exportImageSequence()
419 std::vector< std::pair<QString, QSize> > camerasInfo;
420 auto cameraLayers = mEditor->object()->getLayersByType<
LayerCamera >();
423 camerasInfo.push_back(std::make_pair(i->name(), i->getViewSize()));
426 auto currLayer = mEditor->layers()->currentLayer();
427 if (currLayer->type() == Layer::CAMERA)
429 QString strName = currLayer->name();
430 auto it = std::find_if(camerasInfo.begin(), camerasInfo.end(),
431 [strName](std::pair<QString, QSize> p)
433 return p.first == strName;
436 Q_ASSERT(it != camerasInfo.end());
437 std::swap(camerasInfo[0], *it);
439 dialog->setCamerasInfo(camerasInfo);
444 dialog->setDefaultRange(1, length, lengthWithSounds);
453 QString strFilePath = dialog->getFilePath();
454 QSize exportSize = dialog->getExportSize();
455 QString exportFormat = dialog->getExportFormat();
456 bool exportKeyframesOnly = dialog->getExportKeyframesOnly();
457 bool useTranparency = dialog->getTransparency();
458 int startFrame = dialog->getStartFrame();
459 int endFrame = dialog->getEndFrame();
461 QString sCameraLayerName = dialog->getCameraLayerName();
462 LayerCamera* cameraLayer =
static_cast<LayerCamera*
>(mEditor->layers()->findLayerByName(sCameraLayerName, Layer::CAMERA));
466 hideQuestionMark(progress);
470 mEditor->object()->exportFrames(startFrame, endFrame,
477 mEditor->layers()->currentLayer()->name(),
487 Status ActionCommands::exportImage()
495 std::vector< std::pair<QString, QSize> > camerasInfo;
496 auto cameraLayers = mEditor->object()->getLayersByType<
LayerCamera >();
499 camerasInfo.push_back(std::make_pair(i->name(), i->getViewSize()));
502 auto currLayer = mEditor->layers()->currentLayer();
503 if (currLayer->type() == Layer::CAMERA)
505 QString strName = currLayer->name();
506 auto it = std::find_if(camerasInfo.begin(), camerasInfo.end(),
507 [strName](std::pair<QString, QSize> p)
509 return p.first == strName;
512 Q_ASSERT(it != camerasInfo.end());
513 std::swap(camerasInfo[0], *it);
515 dialog->setCamerasInfo(camerasInfo);
524 QString filePath = dialog->getFilePath();
525 QSize exportSize = dialog->getExportSize();
526 QString exportFormat = dialog->getExportFormat();
527 bool useTranparency = dialog->getTransparency();
530 QString sCameraLayerName = dialog->getCameraLayerName();
531 LayerCamera* cameraLayer =
static_cast<LayerCamera*
>(mEditor->layers()->findLayerByName(sCameraLayerName, Layer::CAMERA));
533 QTransform view = cameraLayer->getViewAtFrame(mEditor->currentFrame());
535 bool bOK = mEditor->object()->exportIm(mEditor->currentFrame(),
537 cameraLayer->getViewSize(),
548 tr(
"Unable to export image."),
555 void ActionCommands::flipSelectionX()
557 bool flipVertical =
false;
558 mEditor->flipSelection(flipVertical);
561 void ActionCommands::flipSelectionY()
563 bool flipVertical =
true;
564 mEditor->flipSelection(flipVertical);
567 void ActionCommands::selectAll()
569 mEditor->selectAll();
572 void ActionCommands::deselectAll()
574 mEditor->deselectAll();
577 void ActionCommands::ZoomIn()
579 mEditor->view()->scaleUp();
582 void ActionCommands::ZoomOut()
584 mEditor->view()->scaleDown();
587 void ActionCommands::rotateClockwise()
589 float currentRotation = mEditor->view()->rotation();
590 mEditor->view()->rotate(currentRotation + 15.f);
593 void ActionCommands::rotateCounterClockwise()
595 float currentRotation = mEditor->view()->rotation();
596 mEditor->view()->rotate(currentRotation - 15.f);
599 void ActionCommands::PlayStop()
602 if (playback->isPlaying())
612 void ActionCommands::GotoNextFrame()
614 mEditor->scrubForward();
617 void ActionCommands::GotoPrevFrame()
619 mEditor->scrubBackward();
622 void ActionCommands::GotoNextKeyFrame()
624 mEditor->scrubNextKeyFrame();
627 void ActionCommands::GotoPrevKeyFrame()
629 mEditor->scrubPreviousKeyFrame();
632 Status ActionCommands::addNewKey()
634 KeyFrame* key = mEditor->addNewKey();
643 mEditor->layers()->currentLayer()->removeKeyFrame(clip->pos());
646 Status st = mEditor->sound()->loadSound(clip, strSoundFile);
649 mEditor->layers()->currentLayer()->removeKeyFrame(clip->pos());
650 return Status::ERROR_LOAD_SOUND_FILE;
657 mEditor->view()->updateViewTransforms();
663 void ActionCommands::removeKey()
665 mEditor->removeKey();
667 Layer* layer = mEditor->layers()->currentLayer();
668 if (layer->keyFrameCount() == 0)
670 layer->addNewKeyFrameAt(1);
674 void ActionCommands::duplicateKey()
676 Layer* layer = mEditor->layers()->currentLayer();
677 if (layer ==
nullptr)
return;
678 if (!layer->visible())
680 mEditor->getScribbleArea()->showLayerNotVisibleWarning();
684 KeyFrame* key = layer->getKeyFrameAt(mEditor->currentFrame());
685 if (key ==
nullptr)
return;
689 int nextEmptyFrame = mEditor->currentFrame() + 1;
690 while (layer->keyExistsWhichCovers(nextEmptyFrame))
695 layer->addKeyFrame(nextEmptyFrame, dupKey);
696 mEditor->scrubTo(nextEmptyFrame);
698 if (layer->type() == Layer::SOUND)
700 mEditor->sound()->processSound(dynamic_cast<SoundClip*>(dupKey));
704 dupKey->setFileName(
"");
705 dupKey->modification();
708 mEditor->layers()->notifyAnimationLengthChanged();
711 void ActionCommands::moveFrameForward()
713 Layer* layer = mEditor->layers()->currentLayer();
716 if (layer->moveKeyFrameForward(mEditor->currentFrame()))
718 mEditor->scrubForward();
722 mEditor->layers()->notifyAnimationLengthChanged();
725 void ActionCommands::moveFrameBackward()
727 Layer* layer = mEditor->layers()->currentLayer();
730 if (layer->moveKeyFrameBackward(mEditor->currentFrame()))
732 mEditor->scrubBackward();
737 Status ActionCommands::addNewBitmapLayer()
742 mEditor->layers()->nameSuggestLayer(
tr(
"Bitmap Layer")), &ok);
745 mEditor->layers()->createBitmapLayer(text);
750 Status ActionCommands::addNewVectorLayer()
755 mEditor->layers()->nameSuggestLayer(
tr(
"Vector Layer")), &ok);
758 mEditor->layers()->createVectorLayer(text);
763 Status ActionCommands::addNewCameraLayer()
768 mEditor->layers()->nameSuggestLayer(
tr(
"Camera Layer")), &ok);
771 mEditor->layers()->createCameraLayer(text);
776 Status ActionCommands::addNewSoundLayer()
781 mEditor->layers()->nameSuggestLayer(
tr(
"Sound Layer")), &ok);
782 if (ok && !strLayerName.
isEmpty())
784 Layer* layer = mEditor->layers()->createSoundLayer(strLayerName);
785 mEditor->layers()->setCurrentLayer(layer);
790 Status ActionCommands::deleteCurrentLayer()
793 QString strLayerName = layerMgr->currentLayer()->name();
796 tr(
"Delete Layer",
"Windows title of Delete current layer pop-up."),
797 tr(
"Are you sure you want to delete layer: %1? This cannot be undone.").arg(strLayerName),
802 Status st = layerMgr->deleteLayer(mEditor->currentLayerIndex());
803 if (st == Status::ERROR_NEED_AT_LEAST_ONE_CAMERA_LAYER)
806 tr(
"Please keep at least one camera layer in project",
"text when failed to delete camera layer"));
812 void ActionCommands::setLayerVisibilityIndex(
int index)
817 void ActionCommands::changeKeyframeLineColor()
819 if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP &&
820 mEditor->layers()->currentLayer()->keyExists(mEditor->currentFrame()))
822 QRgb color = mEditor->color()->frontColor().
rgb();
824 layer->getBitmapImageAtFrame(mEditor->currentFrame())->fillNonAlphaPixels(color);
825 mEditor->updateFrame(mEditor->currentFrame());
829 void ActionCommands::changeallKeyframeLineColor()
831 if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP)
833 QRgb color = mEditor->color()->frontColor().
rgb();
835 for (
int i = layer->firstKeyFramePosition(); i <= layer->getMaxKeyFramePosition(); i++)
837 if (layer->keyExists(i))
838 layer->getBitmapImageAtFrame(i)->fillNonAlphaPixels(color);
840 mEditor->updateFrame(mEditor->currentFrame());
844 void ActionCommands::help()
846 QString url =
"http://www.pencil2d.org/doc/";
850 void ActionCommands::quickGuide()
855 QFile quickGuideFile(
":/app/pencil2d_quick_guide.pdf");
856 quickGuideFile.copy(sCopyDest);
861 void ActionCommands::website()
863 QString url =
"https://www.pencil2d.org/";
867 void ActionCommands::forum()
869 QString url =
"https://discuss.pencil2d.org/";
873 void ActionCommands::discord()
875 QString url =
"https://discord.gg/8FxdV2g";
879 void ActionCommands::reportbug()
881 QString url =
"https://github.com/pencil2d/pencil/issues";
885 void ActionCommands::checkForUpdates()
888 dialog.startChecking();
893 void ActionCommands::openTemporaryDirectory()
902 void ActionCommands::about()
QString writableLocation(QStandardPaths::StandardLocation type)
void setLayerVisibility(LayerVisibility visibility)
The visiblity value should match any of the VISIBILITY enum values.
QString filePath(const QString &fileName) const const
bool exists() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
void processEvents(QEventLoop::ProcessEventsFlags flags)
QMessageBox::StandardButton question(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
bool isEmpty() const const
void setText(const QString &text)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
Status run(const Object *obj, const ExportMovieDesc &desc, std::function< void(float, float)> majorProgress, std::function< void(float)> minorProgress, std::function< void(QString)> progressMessage)
Begin exporting the movie described by exportDesc.
virtual int exec() override
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.
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
bool openUrl(const QUrl &url)
int animationLength(bool includeSounds=true)
Get the length of current project.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
QUrl fromLocalFile(const QString &localFile)