WingGifEditor/Dialog/mainwindow.cpp

1336 lines
44 KiB
C++

#include "Dialog/mainwindow.h"
#include "Dialog/aboutsoftwaredialog.h"
#include "Dialog/createreversedialog.h"
#include "Dialog/exportdialog.h"
#include "Dialog/newdialog.h"
#include "Dialog/reduceframedialog.h"
#include "Dialog/scalegifdialog.h"
#include "Dialog/sponsordialog.h"
#include "UndoCommand/cropimagecommand.h"
#include "UndoCommand/delayframecommand.h"
#include "UndoCommand/delframedircommand.h"
#include "UndoCommand/flipframecommand.h"
#include "UndoCommand/insertframecommand.h"
#include "UndoCommand/moveframecommand.h"
#include "UndoCommand/reduceframecommand.h"
#include "UndoCommand/removeframecommand.h"
#include "UndoCommand/replaceframecommand.h"
#include "UndoCommand/reverseframecommand.h"
#include "UndoCommand/rotateframecommand.h"
#include "UndoCommand/scaleframecommand.h"
#include <DInputDialog>
#include <DMenu>
#include <DMessageManager>
#include <DTitlebar>
#include <DToolBar>
#include <DToolButton>
#include <QApplication>
#include <QClipboard>
#include <QCloseEvent>
#include <QDesktopServices>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QFileDialog>
#include <QListWidgetItem>
#include <QMessageBox>
#include <QMimeData>
#include <QRubberBand>
#include <QSettings>
#include <QShortcut>
#include <QVBoxLayout>
#include <QWidget>
#define ICONRES(name) QIcon(":/images/" name ".png")
#define CheckEnabled \
if (curfilename.isEmpty()) \
return;
MainWindow::MainWindow(DMainWindow *parent) : DMainWindow(parent) {
setMinimumSize(800, 600);
setWindowTitle(tr("WingGifEditor"));
setWindowIcon(ICONRES("icon"));
auto w = new QWidget(this);
setCentralWidget(w);
auto vlayout = new QVBoxLayout(w);
editor = new GifEditor(QPixmap(":/images/icon.png"), w);
editor->setInteractive(true);
vlayout->addWidget(editor);
imglist = new QListWidget(w);
imglist->setFlow(QListWidget::TopToBottom);
imglist->setViewMode(QListWidget::IconMode);
imglist->setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
imglist->setSelectionMode(
QAbstractItemView::SelectionMode::ExtendedSelection);
imglist->setIconSize(QSize(100, 100));
imglist->setFixedHeight(140);
imglist->setLayoutDirection(Qt::LayoutDirection::LeftToRight);
imglist->setResizeMode(QListView::Adjust);
imglist->setDragDropMode(QListWidget::DragDropMode::NoDragDrop);
imglist->setDefaultDropAction(Qt::DropAction::TargetMoveAction);
vlayout->addWidget(imglist);
connect(imglist, &QListWidget::itemSelectionChanged, this, [=] {
editor->setBackgroudPix(gif.frameimg(imglist->currentRow()));
this->showGifMessage();
});
auto title = titlebar();
title->setIcon(ICONRES("icon"));
title->setTitle(tr("WingGifEditor"));
auto menu = new DMenu(this);
QAction *a;
auto keyselrev =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_I);
auto keygoto =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_G);
auto keybeginframe = QKeySequence(Qt::Key_Home);
auto keylastframe = QKeySequence(Qt::Key_Left);
auto keynextframe = QKeySequence(Qt::Key_Right);
auto keyendframe = QKeySequence(Qt::Key_End);
auto keyplay = QKeySequence(Qt::Key_F5);
auto keypause = QKeySequence(Qt::Key_F10);
auto keyreduceframe =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_D);
auto keyrmleft =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_J);
auto keyrmright =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_K);
auto keyrev = QKeySequence(Qt::KeyboardModifier::ControlModifier |
Qt::KeyboardModifier::ShiftModifier | Qt::Key_I);
auto keymvl =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_BracketLeft);
auto keymvr = QKeySequence(Qt::KeyboardModifier::ControlModifier |
Qt::Key_BracketRight);
auto keysetdelay =
QKeySequence(Qt::KeyboardModifier::ControlModifier | Qt::Key_T);
auto keyscaledelay =
QKeySequence(Qt::KeyboardModifier::ControlModifier |
Qt::KeyboardModifier::ShiftModifier | Qt::Key_T);
auto keyscalepic =
QKeySequence(Qt::KeyboardModifier::ShiftModifier | Qt::Key_T);
auto keycutpic =
QKeySequence(Qt::KeyboardModifier::ControlModifier |
Qt::KeyboardModifier::ShiftModifier | Qt::Key_X);
#define AddMenuIconAction(Icon, Title, Slot, Owner) \
a = new QAction(Owner); \
a->setText(Title); \
a->setIcon(ICONRES(Icon)); \
connect(a, &QAction::triggered, this, &Slot); \
Owner->addAction(a);
#define AddMenuAction(Title, Slot, Owner) \
a = new QAction(Owner); \
a->setText(Title); \
connect(a, &QAction::triggered, this, &Slot); \
Owner->addAction(a);
#define AddMenuShortcutAction(Icon, Title, Slot, Owner, ShortCut) \
AddMenuIconAction(Icon, Title, Slot, Owner); \
a->setShortcut(ShortCut); \
a->setShortcutVisibleInContextMenu(true);
#define AddToolSubMenuShortcutAction(Icon, Title, Slot, ShortCut) \
AddMenuShortcutAction(Icon, Title, Slot, tm, ShortCut)
#define AddMenuDB() \
a->setDisabled(true); \
dismenu.append(a)
auto tm = new QMenu(this);
tm->setIcon(ICONRES("file"));
tm->setTitle(tr("File"));
auto m = new QMenu(this);
m->setIcon(ICONRES("new"));
m->setTitle(tr("New"));
AddMenuIconAction("pics", tr("NewFromPics"), MainWindow::on_new_frompics, m);
AddMenuIconAction("gifs", tr("NewFromGifs"), MainWindow::on_new_fromgifs, m);
tm->addMenu(m);
AddMenuShortcutAction("open", tr("Open"), MainWindow::on_open, tm,
QKeySequence::Open);
AddMenuShortcutAction("save", tr("Save"), MainWindow::on_save, tm,
QKeySequence::Save);
AddMenuDB();
AddMenuShortcutAction("saveas", tr("SaveAs"), MainWindow::on_saveas, tm,
QKeySequence::SaveAs);
AddMenuDB();
AddMenuIconAction("export", tr("Export"), MainWindow::on_export, tm);
tm->addSeparator();
AddMenuDB();
AddMenuShortcutAction("close", tr("Close"), MainWindow::on_exit, tm,
QKeySequence::Close);
menu->addMenu(tm);
tm = new QMenu(this);
tm->setTitle(tr("Edit"));
tm->setIcon(ICONRES("edit"));
AddMenuShortcutAction("undo", tr("Undo"), MainWindow::on_undo, tm,
QKeySequence::Undo);
a->setDisabled(true);
undomenu = a;
AddMenuShortcutAction("redo", tr("Redo"), MainWindow::on_redo, tm,
QKeySequence::Redo);
a->setDisabled(true);
redomenu = a;
tm->addSeparator();
AddMenuShortcutAction("cut", tr("Cut"), MainWindow::on_cut, tm,
QKeySequence::Cut);
AddMenuDB();
AddMenuShortcutAction("copy", tr("Copy"), MainWindow::on_copy, tm,
QKeySequence::Copy);
AddMenuDB();
AddMenuShortcutAction("paste", tr("Paste"), MainWindow::on_paste, tm,
QKeySequence::Paste);
AddMenuDB();
AddMenuShortcutAction("del", tr("Delete"), MainWindow::on_del, tm,
QKeySequence::Delete);
AddMenuDB();
tm->addSeparator();
AddMenuShortcutAction("selall", tr("SelectAll"), MainWindow::on_selall, tm,
QKeySequence::SelectAll);
AddMenuDB();
AddMenuShortcutAction("desel", tr("Deselect"), MainWindow::on_desel, tm,
QKeySequence::Deselect);
AddMenuDB();
AddMenuShortcutAction("selrev", tr("ReverseSelection"),
MainWindow::on_selreverse, tm, keyselrev);
AddMenuDB();
tm->addSeparator();
AddMenuShortcutAction("jmp", tr("Goto"), MainWindow::on_goto, tm, keygoto);
AddMenuDB();
menu->addMenu(tm);
tm = new QMenu(this);
tm->setIcon(ICONRES("play"));
tm->setTitle(tr("Play"));
AddMenuShortcutAction("first", tr("FirstFrame"), MainWindow::on_beginframe,
tm, keybeginframe);
AddMenuDB();
AddMenuShortcutAction("back", tr("LastFrame"), MainWindow::on_last, tm,
keylastframe);
AddMenuDB();
AddMenuShortcutAction("gifplay", tr("Play"), MainWindow::on_play, tm,
keyplay);
AddMenuDB();
AddMenuShortcutAction("pause", tr("Stop"), MainWindow::on_stop, tm, keypause);
AddMenuDB();
AddMenuShortcutAction("foreword", tr("NextFrame"), MainWindow::on_next, tm,
keynextframe);
AddMenuDB();
AddMenuShortcutAction("last", tr("EndFrame"), MainWindow::on_endframe, tm,
keylastframe);
AddMenuDB();
title->setMenu(menu);
tm = new QMenu(this);
tm->setIcon(ICONRES("pic"));
tm->setTitle(tr("Picture"));
AddMenuShortcutAction("rmframe", tr("ReduceFrame"),
MainWindow::on_decreaseframe, tm, keyreduceframe);
AddMenuDB();
AddMenuShortcutAction("rml", tr("DeleteBefore"), MainWindow::on_delbefore, tm,
keyrmleft);
AddMenuDB();
AddMenuShortcutAction("rmr", tr("DeleteAfter"), MainWindow::on_delafter, tm,
keyrmright);
AddMenuDB();
tm->addSeparator();
AddMenuShortcutAction("mvf", tr("MoveLeft"), MainWindow::on_moveleft, tm,
keymvl);
AddMenuDB();
AddMenuShortcutAction("mvb", tr("MoveRight"), MainWindow::on_moveright, tm,
keymvr);
AddMenuDB();
AddMenuShortcutAction("reverse", tr("Reverse"), MainWindow::on_reverse, tm,
keyrev);
AddMenuDB();
tm->addSeparator();
AddMenuShortcutAction("setdelay", tr("SetDelay"), MainWindow::on_setdelay, tm,
keysetdelay);
AddMenuDB();
AddMenuIconAction("pics", tr("InsertPics"), MainWindow::on_insertpic, tm);
AddMenuDB();
AddMenuIconAction("gifs", tr("MergeGIfs"), MainWindow::on_merge, tm);
AddMenuDB();
AddMenuShortcutAction("scale", tr("ScaleGif"), MainWindow::on_scalepic, tm,
keyscalepic);
AddMenuDB();
AddMenuShortcutAction("cutpic", tr("CutGif"), MainWindow::on_cutpic, tm,
keycutpic);
AddMenuDB();
tm->addSeparator();
AddMenuIconAction("fliph", tr("FilpH"), MainWindow::on_fliph, tm);
AddMenuDB();
AddMenuIconAction("flipv", tr("FlipV"), MainWindow::on_flipv, tm);
AddMenuDB();
AddMenuIconAction("rotatel", tr("RotateLeft"), MainWindow::on_clockwise, tm);
AddMenuDB();
AddMenuIconAction("rotater", tr("RotateR"), MainWindow::on_anticlockwise, tm);
AddMenuDB();
menu->addMenu(tm);
tm = new DMenu(this);
tm->setTitle(tr("Effect"));
tm->setIcon(ICONRES("effect"));
AddMenuIconAction("blank", tr("ExportBlank"), MainWindow::on_exportapply, tm);
AddMenuDB();
AddMenuIconAction("model", tr("ApplyModel"), MainWindow::on_applypic, tm);
AddMenuDB();
tm->addSeparator();
AddMenuIconAction("reverseplus", tr("CreateReverse"),
MainWindow::on_createreverse, tm);
AddMenuDB();
AddMenuShortcutAction("scaledelay", tr("ScaleDelay"),
MainWindow::on_scaledelay, tm, keyscaledelay);
AddMenuDB();
AddMenuIconAction("onion", tr("OnionMask"), MainWindow::on_onion, tm);
AddMenuDB();
menu->addMenu(tm);
tm = new DMenu(this);
tm->setTitle(tr("Author"));
tm->setIcon(ICONRES("author"));
AddMenuIconAction("soft", tr("About"), MainWindow::on_about, tm);
AddMenuIconAction("sponsor", tr("Sponsor"), MainWindow::on_sponsor, tm);
AddMenuIconAction("wiki", tr("Wiki"), MainWindow::on_wiki, tm);
a = new QAction(ICONRES("qt"), tr("AboutQT"), tm);
connect(a, &QAction::triggered, this, [=] { QMessageBox::aboutQt(this); });
tm->addAction(a);
menu->addMenu(tm);
auto toolbar = new DToolBar(this);
#define AddToolBarAction(Icon, Owner, Slot, ToolTip) \
a = new QAction(Owner); \
a->setIcon(ICONRES(Icon)); \
connect(a, &QAction::triggered, this, &Slot); \
a->setToolTip(ToolTip); \
Owner->addAction(a);
#define AddToolBarTool(Icon, Slot, ToolTip) \
AddToolBarAction(Icon, toolbar, Slot, ToolTip)
DToolButton *tbtn;
DMenu *tmenu;
#define AddToolBtnBegin(DIcon) \
tbtn = new DToolButton(this); \
tbtn->setIcon(ICONRES(DIcon)); \
tmenu = new DMenu(this);
#define AddToolBtnBtn(Icon, Title, Slot) \
a = new QAction(ICONRES(Icon), Title, this); \
connect(a, &QAction::triggered, this, &Slot); \
tmenu->addAction(a);
#define AddToolBtnEnd() \
tbtn->setMenu(tmenu); \
tbtn->setPopupMode(DToolButton::ToolButtonPopupMode::InstantPopup); \
toolbar->addWidget(tbtn);
AddToolBtnBegin("new") {
AddToolBtnBtn("pics", tr("NewFromPics"), MainWindow::on_new_frompics);
AddToolBtnBtn("gifs", tr("NewFromGifs"), MainWindow::on_new_fromgifs);
}
AddToolBtnEnd();
#define AddToolDB() \
a->setEnabled(false); \
distool.append(a);
AddToolBarTool("open", MainWindow::on_open, tr("Open"));
AddToolBarTool("save", MainWindow::on_save, tr("Save"));
AddToolDB();
AddToolBarTool("saveas", MainWindow::on_saveas, tr("SaveAs"));
AddToolDB();
AddToolBarTool("export", MainWindow::on_export, tr("Export"));
AddToolDB();
toolbar->addSeparator();
AddToolBarTool("undo", MainWindow::on_undo, tr("Undo"));
a->setEnabled(false);
undotool = a;
AddToolBarTool("redo", MainWindow::on_redo, tr("Redo"));
a->setEnabled(false);
redotool = a;
AddToolBarTool("cut", MainWindow::on_cut, tr("Cut"));
AddToolDB();
AddToolBarTool("copy", MainWindow::on_copy, tr("Copy"));
AddToolDB();
AddToolBarTool("paste", MainWindow::on_paste, tr("Paste"));
AddToolDB();
AddToolBarTool("del", MainWindow::on_del, tr("Delete"));
AddToolDB();
AddToolBarTool("selall", MainWindow::on_selall, tr("SelectAll"));
AddToolDB();
AddToolBarTool("desel", MainWindow::on_desel, tr("Deselect"));
AddToolDB();
AddToolBarTool("selrev", MainWindow::on_selreverse, tr("ReverseSelection"));
AddToolDB();
AddToolBarTool("jmp", MainWindow::on_goto, tr("Goto"));
AddToolDB();
toolbar->addSeparator();
AddToolBarTool("soft", MainWindow::on_about, tr("About"));
AddToolBarTool("sponsor", MainWindow::on_sponsor, tr("Sponsor"));
AddToolBarTool("wiki", MainWindow::on_wiki, tr("Wiki"));
toolbar->setObjectName("main");
addToolBar(toolbar);
addToolBarBreak();
toolbar = new DToolBar(this);
AddToolBarTool("rmframe", MainWindow::on_decreaseframe, tr("ReduceFrame"));
AddToolDB();
AddToolBarTool("rml", MainWindow::on_delbefore, tr("DeleteBefore"));
AddToolDB();
AddToolBarTool("rmr", MainWindow::on_delafter, tr("DeleteAfter"));
AddToolDB();
AddToolBarTool("mvf", MainWindow::on_moveleft, tr("MoveLeft"));
AddToolDB();
AddToolBarTool("mvb", MainWindow::on_moveright, tr("MoveRight"));
AddToolDB();
AddToolBarTool("reverse", MainWindow::on_reverse, tr("Reverse"));
AddToolDB();
toolbar->addSeparator();
AddToolBarTool("setdelay", MainWindow::on_setdelay, tr("SetDelay"));
AddToolDB();
AddToolBarTool("pics", MainWindow::on_insertpic, tr("InsertPics"));
AddToolDB();
AddToolBarTool("gifs", MainWindow::on_merge, tr("MergeGIfs"));
AddToolDB();
AddToolBarTool("scale", MainWindow::on_scalepic, tr("ScaleGif"));
AddToolDB();
AddToolBarTool("cutpic", MainWindow::on_cutpic, tr("CutGif"));
AddToolDB();
toolbar->addSeparator();
AddToolBarTool("fliph", MainWindow::on_fliph, tr("FilpH"));
AddToolDB();
AddToolBarTool("flipv", MainWindow::on_flipv, tr("FlipV"));
AddToolDB();
AddToolBarTool("rotatel", MainWindow::on_clockwise, tr("RotateLeft"));
AddToolDB();
AddToolBarTool("rotater", MainWindow::on_anticlockwise, tr("RotateR"));
AddToolDB();
toolbar->setObjectName("pic");
addToolBar(toolbar);
toolbar = new DToolBar(this);
AddToolBarTool("first", MainWindow::on_beginframe, tr("FirstFrame"));
AddToolDB();
AddToolBarTool("back", MainWindow::on_last, tr("LastFrame"));
AddToolDB();
AddToolBarTool("gifplay", MainWindow::on_play, tr("Play"));
AddToolDB();
AddToolBarTool("pause", MainWindow::on_stop, tr("Stop"));
AddToolDB();
AddToolBarTool("foreword", MainWindow::on_next, tr("NextFrame"));
AddToolDB();
AddToolBarTool("last", MainWindow::on_endframe, tr("EndFrame"));
AddToolDB();
toolbar->setObjectName("play");
addToolBar(toolbar);
toolbar = new DToolBar(this);
AddToolBarTool("blank", MainWindow::on_exportapply, tr("ExportBlank"));
AddToolDB();
AddToolBarTool("model", MainWindow::on_applypic, tr("ApplyModel"));
AddToolDB();
AddToolBarTool("reverseplus", MainWindow::on_createreverse,
tr("CreateReverse"));
AddToolDB();
AddToolBarTool("scaledelay", MainWindow::on_scaledelay, tr("ScaleDelay"));
AddToolDB();
toolbar->setObjectName("effect");
addToolBar(toolbar);
status = new DStatusBar(this);
DLabel *l;
#define AddNamedStatusLabel(Var, Content) \
Var = new DLabel(this); \
Var->setText(Content); \
status->addPermanentWidget(Var);
#define AddStatusLabel(Content) AddNamedStatusLabel(l, Content)
#define LoadPixMap(Var, Icon) Var.load(":/images/" Icon ".png");
#define AddStausILable(PixMap, Icon, Label, OPixMap, OIcon, GPixMap, GIcon) \
LoadPixMap(PixMap, Icon); \
LoadPixMap(OPixMap, OIcon); \
LoadPixMap(GPixMap, GIcon) Label = new DLabel(this); \
Label->setPixmap(GPixMap); \
Label->setScaledContents(true); \
Label->setFixedSize(20, 20); \
Label->setAlignment(Qt::AlignCenter); \
status->addPermanentWidget(Label); \
AddStatusLabel(QString(' '));
#define AddFunctionIconButton(Var, Icon) \
Var = new DIconButton(this); \
Var->setIcon(ICONRES(Icon)); \
Var->setIconSize(QSize(20, 20)); \
status->addPermanentWidget(Var);
AddStausILable(infoSaved, "saved", iSaved, infoUnsaved, "unsaved", infoSaveg,
"saveg");
iSaved->setToolTip(tr("InfoSave"));
AddStausILable(infoWriteable, "writable", iReadWrite, infoReadonly,
"readonly", inforwg, "rwg");
iReadWrite->setToolTip(tr("InfoReadWrite"));
AddFunctionIconButton(iSetfitInView, "fitinview");
iSetfitInView->setToolTip(tr("FitInView"));
iSetfitInView->setEnabled(false);
AddStatusLabel(QString(5, ' '));
connect(iSetfitInView, &DIconButton::clicked, this,
[=] { editor->fitPicEditor(); });
setStatusBar(status);
auto pgif = &gif;
connect(pgif, &GifDecoder::frameRemoved, this, [=](int index) {
auto item = imglist->item(index);
imglist->removeItemWidget(item);
delete item;
});
connect(pgif, &GifDecoder::frameMoved, this, [=](int from, int to) {
auto max = qMax(from, to);
auto min = qMin(from, to);
for (auto i = min; i <= max; i++) {
auto p = imglist->item(i);
p->setIcon(gif.thumbnail(i));
p->setText(QString("%1 %2 ms").arg(i).arg(gif.frameDelay(i)));
}
});
connect(pgif, &GifDecoder::frameRefreshAll, this,
&MainWindow::refreshImglist);
connect(pgif, &GifDecoder::frameDelaySet, this, [=](int index, int delay) {
imglist->item(index)->setText(QString("%1 %2 ms").arg(index).arg(delay));
});
connect(pgif, &GifDecoder::frameRefreshImg, this, [=](int index) {
imglist->item(index)->setIcon(gif.thumbnail(index));
imglist->itemSelectionChanged();
});
connect(pgif, &GifDecoder::frameRefreshLabel, this,
&MainWindow::refreshListLabel);
connect(pgif, &GifDecoder::frameMerge, this, [=](int start, int count) {
auto end = start + count - 1;
for (int i = end; i >= start; i--) {
auto p = new QListWidgetItem(
gif.thumbnail(i),
QString("%1 %2 ms").arg(i).arg(gif.frameDelay(i)));
p->setSizeHint(QSize(120, 120));
imglist->insertItem(start, p);
}
});
connect(pgif, &GifDecoder::frameInsert, this, [=](int index) {
auto p = new QListWidgetItem(
gif.thumbnail(index),
QString("%1 %2 ms").arg(index).arg(gif.frameDelay(index)));
p->setSizeHint(QSize(120, 120));
imglist->insertItem(index, p);
});
connect(pgif, &GifDecoder::frameImageChanged, this, [=] {
auto len = imglist->count();
for (auto i = 0; i < len; i++) {
imglist->item(i)->setIcon(gif.thumbnail(i));
}
editor->setBackgroudPix(gif.frameimg(imglist->currentRow()));
});
player = new PlayGifManager(this);
connect(player, &PlayGifManager::tick, this,
[=](int index) { imglist->setCurrentRow(index); });
connect(&undo, &QUndoStack::canUndoChanged, this, [=](bool b) {
CheckEnabled;
undomenu->setEnabled(b);
undotool->setEnabled(b);
});
connect(&undo, &QUndoStack::canRedoChanged, this, [=](bool b) {
CheckEnabled;
redomenu->setEnabled(b);
redotool->setEnabled(b);
});
connect(&undo, &QUndoStack::cleanChanged, this, [=](bool clean) {
CheckEnabled;
this->setSaved(clean && curfilename != ":");
});
cuttingdlg = new CropGifDialog(this);
connect(cuttingdlg, &CropGifDialog::selRectChanged, editor,
&GifEditor::setSelRect);
connect(cuttingdlg, &CropGifDialog::crop, this,
[=](int x, int y, int w, int h) {
CheckEnabled;
editor->endCrop();
this->setEditMode(true);
undo.push(new CropImageCommand(&gif, x, y, w, h));
editor->fitPicEditor();
});
connect(cuttingdlg, &CropGifDialog::pressCancel, this, [=] {
CheckEnabled;
editor->endCrop();
this->setEditMode(true);
});
connect(editor, &GifEditor::selRectChanged, cuttingdlg,
&CropGifDialog::setSelRect);
#define ConnectShortCut(ShortCut, Slot) \
s = new QShortcut(ShortCut, this); \
connect(s, &QShortcut::activated, this, &Slot);
QShortcut *s;
ConnectShortCut(QKeySequence::Open, MainWindow::on_open);
ConnectShortCut(QKeySequence::Save, MainWindow::on_save);
ConnectShortCut(QKeySequence::SaveAs, MainWindow::on_saveas);
ConnectShortCut(QKeySequence::Close, MainWindow::on_exit);
ConnectShortCut(QKeySequence::Undo, MainWindow::on_undo);
ConnectShortCut(QKeySequence::Redo, MainWindow::on_redo);
ConnectShortCut(QKeySequence::Cut, MainWindow::on_cut);
ConnectShortCut(QKeySequence::Copy, MainWindow::on_copy);
ConnectShortCut(QKeySequence::Paste, MainWindow::on_paste);
ConnectShortCut(QKeySequence::Delete, MainWindow::on_del);
ConnectShortCut(QKeySequence::SelectAll, MainWindow::on_selall);
ConnectShortCut(QKeySequence::Deselect, MainWindow::on_desel);
ConnectShortCut(keyselrev, MainWindow::on_selreverse);
ConnectShortCut(keygoto, MainWindow::on_goto);
ConnectShortCut(keybeginframe, MainWindow::on_beginframe);
ConnectShortCut(keylastframe, MainWindow::on_last);
ConnectShortCut(keyplay, MainWindow::on_play);
ConnectShortCut(keypause, MainWindow::on_stop);
ConnectShortCut(keynextframe, MainWindow::on_next);
ConnectShortCut(keylastframe, MainWindow::on_endframe);
ConnectShortCut(keyreduceframe, MainWindow::on_decreaseframe);
ConnectShortCut(keyrmleft, MainWindow::on_delbefore);
ConnectShortCut(keyrmright, MainWindow::on_delafter);
ConnectShortCut(keymvl, MainWindow::on_moveleft);
ConnectShortCut(keymvr, MainWindow::on_moveright);
ConnectShortCut(keyrev, MainWindow::on_reverse);
ConnectShortCut(keysetdelay, MainWindow::on_setdelay);
ConnectShortCut(keyscalepic, MainWindow::on_scalepic);
ConnectShortCut(keycutpic, MainWindow::on_cutpic);
ConnectShortCut(keyscaledelay, MainWindow::on_scaledelay);
loadWindowStatus();
}
void MainWindow::openGif(QString filename) {
if (!gif.load(filename)) {
setEditMode(false);
DMessageManager::instance()->sendMessage(this, ICONRES("icon"),
"OpenError");
QMessageBox::critical(this, tr("OpenError"),
QString(GifErrorString(gif.getLastError())));
return;
}
showWaitNotify();
curfilename = filename;
imglist->setCurrentRow(0);
editor->fitPicEditor();
setEditMode(true);
setSaved(true);
setWritable(QFileInfo(filename).permission(QFile::WriteUser));
showGifMessage();
showProcessSuccess();
}
bool MainWindow::checkIsGif(QString filename) {
QFile f(filename);
if (f.open(QFile::ReadOnly)) {
char *bu = new char[GIF_STAMP_LEN];
f.read(bu, GIF_STAMP_LEN);
return !memcmp(GIF_STAMP, bu, GIF_STAMP_LEN) ||
!memcmp(GIF87_STAMP, bu, GIF_STAMP_LEN) ||
!memcmp(GIF89_STAMP, bu, GIF_STAMP_LEN);
}
return false;
}
void MainWindow::refreshImglist() {
auto pos = imglist->currentRow();
imglist->clear();
auto len = gif.frameCount();
for (int i = 0; i < len; i++) {
auto p = new QListWidgetItem(
gif.thumbnail(i), QString("%1 %2 ms").arg(i).arg(gif.frameDelay(i)),
imglist);
p->setSizeHint(QSize(120, 120));
}
imglist->setCurrentRow(pos);
}
void MainWindow::refreshListLabel(int start) {
auto len = gif.frameCount();
for (int i = start; i < len; i++) {
imglist->item(i)->setText(
QString("%1 %2 ms").arg(i).arg(gif.frameDelay(i)));
}
}
void MainWindow::setEditMode(bool b) {
for (auto item : distool)
item->setEnabled(b);
for (auto item : dismenu)
item->setEnabled(item);
iSetfitInView->setEnabled(b);
}
bool MainWindow::ensureSafeClose() {
if (!undo.isClean()) {
if (QMessageBox::question(
this, tr("ConfirmClose"),
tr("ConfirmSave") + "\n" +
(curfilename == ":" ? tr("Untitle") : curfilename)) ==
QMessageBox::No)
return false;
}
return true;
}
void MainWindow::setSaved(bool b) {
iSaved->setPixmap(b ? infoSaved : infoUnsaved);
}
void MainWindow::setWritable(bool b) {
iReadWrite->setPixmap(b ? infoWriteable : infoReadonly);
}
void MainWindow::showGifMessage(QString message) {
status->showMessage(QString(5, ' ') + message +
QString(tr("%1 frame | %2 total")
.arg(imglist->currentRow())
.arg(gif.frameCount())));
}
void MainWindow::saveWindowStatus() {
QSettings settings(QApplication::organizationName(),
QApplication::applicationName());
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
}
void MainWindow::loadWindowStatus() {
QSettings settings(QApplication::organizationName(),
QApplication::applicationName());
settings.setValue("geometry", saveGeometry());
settings.setValue("windowState", saveState());
}
void MainWindow::showWaitNotify() {
DMessageManager::instance()->sendMessage(this, ICONRES("icon"),
tr("PleaseWait!"));
}
void MainWindow::showProcessSuccess() {
DMessageManager::instance()->sendMessage(this, ICONRES("icon"),
tr("ProcessSuccess"));
}
bool MainWindow::saveGif(QString filename) {
GifEncoder gifsaver;
auto size = gif.size();
if (gifsaver.init(filename.toStdString().c_str(), uint16_t(size.width()),
uint16_t(size.height()), 0)) {
std::vector<std::vector<uint32_t>> images;
std::vector<uint32_t> delay;
std::vector<QRect> rects;
int i = 0;
QImage lastimg;
for (auto &frame : gif.frames()) {
auto &img = frame.image;
QImage timg = img;
// TODO : 通过只保留不同区域图片实现,但效果不好,故注释,以备用
// if (i) {
// auto bpl = lastimg.bytesPerLine();
// auto ls = lastimg.height();
// int x, y, x0, y0;
// for (y = 0; y < ls; y++) {
// auto o = lastimg.constScanLine(y);
// auto d = img.constScanLine(y);
// if (memcmp(o, d, size_t(bpl))) {
// break;
// }
// }
// for (y0 = ls - 1; y0 > y; y0--) {
// auto o = lastimg.constScanLine(y0);
// auto d = img.constScanLine(y0);
// if (memcmp(o, d, size_t(bpl))) {
// break;
// }
// }
// //比较列
// QTransform trans;
// trans.rotate(-90);
// timg = img.copy().transformed(trans);
// lastimg = lastimg.transformed(trans);
// bpl = lastimg.bytesPerLine();
// ls = lastimg.height();
// for (x = 0; x < ls; x++) {
// auto o = lastimg.constScanLine(x);
// auto d = timg.constScanLine(x);
// if (memcmp(o, d, size_t(bpl))) {
// break;
// }
// }
// for (x0 = ls - 1; x0 > x; x0--) {
// auto o = lastimg.constScanLine(x0);
// auto d = timg.constScanLine(x0);
// if (memcmp(o, d, size_t(bpl))) {
// break;
// }
// }
// timg = img.copy(x, y, x0 - x + 1, y0 - y + 1);
// rects.push_back(QRect(QPoint(x, y), timg.size()));
// } else {
rects.push_back(QRect(QPoint(), img.size()));
// }
std::vector<uint32_t> buffer(ulong(timg.sizeInBytes()));
memcpy(buffer.data(), timg.constBits(), buffer.size());
images.push_back(buffer);
delay.push_back(uint32_t(frame.delayTime));
lastimg = img;
i++;
}
gifsaver.addImages(images, delay, rects);
gifsaver.finishEncoding();
return true;
}
return false;
}
void MainWindow::on_new_frompics() {
if (ensureSafeClose()) {
NewDialog d(NewType::FromPics, this);
if (d.exec()) {
showWaitNotify();
gif.loadfromImages(d.getResult());
showProcessSuccess();
curfilename = ":"; //表示新建
setSaved(false);
setWritable(true);
setEditMode(true);
imglist->setCurrentRow(0);
showGifMessage();
showProcessSuccess();
}
}
}
void MainWindow::on_new_fromgifs() {
if (ensureSafeClose()) {
NewDialog d(NewType::FromGifs, this);
if (d.exec()) {
showWaitNotify();
gif.loadfromGifs(d.getResult());
curfilename = ":"; //表示新建
setSaved(false);
setWritable(true);
setEditMode(true);
imglist->setCurrentRow(0);
showGifMessage();
showProcessSuccess();
}
}
}
void MainWindow::on_open() {
if (ensureSafeClose()) {
auto filename = QFileDialog::getOpenFileName(this, tr("ChooseFile"),
lastusedpath, "gif (*.gif)");
if (filename.isEmpty())
return;
lastusedpath = QFileInfo(filename).absoluteDir().absolutePath();
if (checkIsGif(filename))
openGif(filename);
}
}
void MainWindow::on_del() {
CheckEnabled;
QVector<int> indices;
for (auto item : imglist->selectionModel()->selectedIndexes()) {
indices.append(item.row());
}
undo.push(new RemoveFrameCommand(&gif, indices));
}
void MainWindow::on_selall() {
CheckEnabled;
imglist->selectAll();
}
void MainWindow::on_selreverse() {
CheckEnabled;
auto len = imglist->count();
for (auto i = 0; i < len; i++) {
auto item = imglist->item(i);
item->setSelected(!item->isSelected());
}
}
void MainWindow::on_desel() {
CheckEnabled;
auto cur = imglist->currentRow();
imglist->setCurrentRow(cur);
}
void MainWindow::on_goto() {
CheckEnabled;
bool ok;
auto index =
DInputDialog::getInt(this, tr("Goto"), tr("PleaseInputIndex"),
imglist->currentRow(), 0, imglist->count(), 1, &ok);
if (ok) {
imglist->setCurrentRow(index);
}
}
void MainWindow::on_beginframe() {
CheckEnabled;
imglist->setCurrentRow(0);
}
void MainWindow::on_last() {
CheckEnabled;
auto pos = imglist->currentRow();
if (pos > 0) {
imglist->setCurrentRow(pos - 1);
}
}
void MainWindow::on_play() {
CheckEnabled;
QList<int> ints;
auto len = gif.frameCount();
for (auto i = 0; i < len; i++) {
ints.append(gif.frameDelay(i));
}
player->setTickIntervals(ints);
player->play(imglist->currentRow());
}
void MainWindow::on_stop() {
CheckEnabled;
player->stop();
}
void MainWindow::on_next() {
CheckEnabled;
auto pos = imglist->currentRow();
auto max = imglist->count();
if (pos < max - 1) {
imglist->setCurrentRow(pos + 1);
}
}
void MainWindow::on_endframe() {
CheckEnabled;
imglist->setCurrentRow(imglist->count() - 1);
}
void MainWindow::on_decreaseframe() {
CheckEnabled;
ReduceFrameDialog d(imglist->count(), this);
if (d.exec()) {
auto res = d.getResult();
auto from = res.start;
auto step = res.end;
auto to = res.end;
QVector<int> delindices, modinter;
auto ii = from + 1;
auto q = step + 1;
for (auto i = ii; i <= to; i++) {
if (i == ii + step) {
ii += step;
delindices.append(i);
} else {
modinter.append(gif.frameDelay(i) * (q + 1) / q);
}
}
undo.push(new ReduceFrameCommand(&gif, delindices, modinter));
}
}
void MainWindow::on_delbefore() {
CheckEnabled;
auto pos = imglist->currentRow();
undo.push(new DelFrameDirCommand(&gif, pos, DelDirection::Before, imglist));
}
void MainWindow::on_delafter() {
CheckEnabled;
auto pos = imglist->currentRow();
undo.push(new DelFrameDirCommand(&gif, pos, DelDirection::After, imglist));
}
void MainWindow::on_saveas() {
CheckEnabled;
auto filename = QFileDialog::getSaveFileName(this, tr("ChooseSaveFile"),
lastusedpath, "gif (*.gif)");
if (filename.isEmpty())
return;
lastusedpath = QFileInfo(filename).absoluteDir().absolutePath();
if (saveGif(filename)) {
DMessageManager::instance()->sendMessage(this, ICONRES("saveas"),
tr("SaveAsSuccess"));
curfilename = filename;
} else {
DMessageManager::instance()->sendMessage(this, ICONRES("saveas"),
"SaveAsFail");
}
}
void MainWindow::on_export() {
CheckEnabled;
ExportDialog d;
if (d.exec()) {
auto res = d.getResult();
QString ext;
switch (res.type) {
case ExportImageType::PNG: {
ext = "png";
break;
}
case ExportImageType::JPG: {
ext = "jpg";
break;
}
case ExportImageType::TIFF: {
ext = "tiff";
break;
}
case ExportImageType::WEBP: {
ext = "webp";
break;
}
}
if (gif.exportImages(res.path, ext)) {
DMessageManager::instance()->sendMessage(this, ICONRES("saveas"),
tr("ExportSuccess"));
} else {
DMessageManager::instance()->sendMessage(this, ICONRES("saveas"),
tr("ExportFail"));
}
}
}
void MainWindow::on_exit() {
if (ensureSafeClose()) {
gif.close();
editor->setBackgroudPix(QPixmap(":/images/icon.png"));
editor->scale(1, 1);
iSaved->setPixmap(infoSaveg);
iReadWrite->setPixmap(inforwg);
status->showMessage("");
curfilename.clear();
undo.clear();
}
}
void MainWindow::on_undo() {
CheckEnabled;
undo.undo();
}
void MainWindow::on_redo() {
CheckEnabled;
undo.redo();
}
void MainWindow::on_copy() {
CheckEnabled;
auto sels = imglist->selectionModel()->selectedRows();
QList<QGifFrameInfoData> sel;
auto frames = gif.frames();
for (auto &i : sels) {
sel.append(frames[i.row()]);
}
ClipBoardHelper::setImageFrames(sel);
}
void MainWindow::on_cut() {
CheckEnabled;
auto sels = imglist->selectionModel()->selectedRows();
QList<QGifFrameInfoData> sel;
QVector<int> indices;
auto frames = gif.frames();
for (auto &i : sels) {
indices.append(i.row());
sel.append(frames[i.row()]);
}
ClipBoardHelper::setImageFrames(sel);
undo.push(new RemoveFrameCommand(&gif, indices));
}
void MainWindow::on_paste() {
CheckEnabled;
auto pos = imglist->currentRow() + 1;
QList<QGifFrameInfoData> imgs;
ClipBoardHelper::getImageFrames(imgs);
if (imgs.count()) {
undo.push(new InsertFrameCommand(&gif, imglist, pos, imgs));
}
}
void MainWindow::on_save() {
CheckEnabled;
if (curfilename == ":") {
on_saveas();
return;
}
if (saveGif(curfilename)) {
undo.setClean();
DMessageManager::instance()->sendMessage(this, ICONRES("save"),
tr("SaveSuccess"));
} else {
DMessageManager::instance()->sendMessage(this, ICONRES("save"), "SaveFail");
}
}
void MainWindow::on_reverse() {
CheckEnabled;
undo.push(new ReverseFrameCommand(&gif));
refreshImglist();
}
void MainWindow::on_moveleft() {
CheckEnabled;
auto pos = imglist->currentRow();
undo.push(new MoveFrameCommand(&gif, imglist, MoveFrameDirection::Left, pos));
}
void MainWindow::on_moveright() {
CheckEnabled;
auto pos = imglist->currentRow();
undo.push(
new MoveFrameCommand(&gif, imglist, MoveFrameDirection::Right, pos));
}
void MainWindow::on_createreverse() {
CheckEnabled;
CreateReverseDialog d(imglist->count(), this);
if (d.exec()) {
auto res = d.getResult();
auto frames = gif.frames();
auto bu = frames.mid(res.start, res.end - res.start + 1);
std::reverse(bu.begin(), bu.end());
undo.push(new InsertFrameCommand(&gif, imglist, imglist->currentRow(), bu));
}
}
void MainWindow::on_setdelay() {
CheckEnabled;
auto indices = imglist->selectionModel()->selectedRows();
bool ok;
auto time = DInputDialog::getInt(this, tr("DelayTime"), tr("Inputms"), 2, 1,
INT_MAX, 1, &ok);
if (ok) {
QVector<int> is;
for (auto i : indices) {
is.append(i.row());
}
undo.push(new DelayFrameCommand(&gif, is, time));
}
}
void MainWindow::on_scaledelay() {
CheckEnabled;
auto indices = imglist->selectionModel()->selectedRows();
bool ok;
auto scale = DInputDialog::getInt(this, tr("ScaleDelayTime"),
tr("InputPercent"), 100, 1, 100, 1, &ok);
if (ok) {
QVector<int> is;
for (auto i : indices) {
is.append(i.row());
}
undo.push(new DelayScaleCommand(&gif, is, scale));
}
}
void MainWindow::on_insertpic() {
CheckEnabled;
auto filenames = QFileDialog::getOpenFileNames(
this, tr("ChooseFile"), lastusedpath, tr("Images (*.jpg *.tiff *.png)"));
if (filenames.isEmpty())
return;
lastusedpath = QFileInfo(filenames.first()).absoluteDir().absolutePath();
auto pos = imglist->currentRow() + 1;
GifDecoder buffer;
buffer.loadfromImages(filenames);
undo.push(new InsertFrameCommand(&gif, imglist, pos, buffer.frames()));
buffer.close();
}
void MainWindow::on_merge() {
CheckEnabled;
auto filenames = QFileDialog::getOpenFileNames(this, tr("ChooseFile"),
lastusedpath, "gif (*.gif)");
if (filenames.isEmpty())
return;
lastusedpath = QFileInfo(filenames.first()).absoluteDir().absolutePath();
auto pos = imglist->currentRow() + 1;
GifDecoder buffer;
buffer.loadfromGifs(filenames);
undo.push(new InsertFrameCommand(&gif, imglist, pos, buffer.frames()));
buffer.close();
}
void MainWindow::on_scalepic() {
CheckEnabled;
ScaleGIFDialog d(gif.size(), this);
if (d.exec()) {
auto res = d.getResult();
undo.push(new ScaleFrameCommand(&gif, res.width, res.height));
}
}
void MainWindow::on_cutpic() {
CheckEnabled;
setEditMode(false);
QRectF rect;
editor->initCrop(rect);
cuttingdlg->setMaxSize(rect.size().toSize());
cuttingdlg->setSelRect(rect);
cuttingdlg->show();
}
void MainWindow::on_fliph() {
CheckEnabled;
undo.push(new FlipFrameCommand(&gif, FlipDirection::Horizontal));
}
void MainWindow::on_flipv() {
CheckEnabled;
undo.push(new FlipFrameCommand(&gif, FlipDirection::Vertical));
}
void MainWindow::on_clockwise() {
CheckEnabled;
undo.push(new RotateFrameCommand(&gif, true));
}
void MainWindow::on_anticlockwise() {
CheckEnabled;
undo.push(new RotateFrameCommand(&gif, false));
}
void MainWindow::on_exportapply() {
CheckEnabled;
auto filename = QFileDialog::getSaveFileName(this, tr("ChooseSaveFile"),
lastusedpath, "png (*.png)");
if (filename.isEmpty())
return;
lastusedpath = QFileInfo(filename).absoluteDir().absolutePath();
QImage img(gif.size(), QImage::Format_RGBA8888);
img.fill(Qt::transparent);
img.save(filename);
DMessageManager::instance()->sendMessage(this, ICONRES("blank"),
tr("ExportSuccess"));
}
void MainWindow::on_applypic() {
CheckEnabled;
auto indices = imglist->selectionModel()->selectedRows();
if (!indices.count()) {
DMessageManager::instance()->sendMessage(this, ICONRES("model"),
tr("NoSelection"));
return;
}
auto filename = QFileDialog::getOpenFileName(this, tr("ChooseFile"),
lastusedpath, "png (*.png)");
if (filename.isEmpty())
return;
lastusedpath = QFileInfo(filename).absoluteDir().absolutePath();
QVector<int> rows;
QVector<QImage> imgs;
QImage img;
if (img.load(filename)) {
if (img.size() == gif.size()) {
auto frames = gif.frames();
for (auto i : indices) {
rows.append(i.row());
QImage bimg = frames[i.row()].image.copy();
QPainter painter(&bimg);
painter.drawImage(QPoint(), img, img.rect());
imgs.append(bimg);
}
undo.push(new ReplaceFrameCommand(&gif, rows, imgs));
return;
}
}
DMessageManager::instance()->sendMessage(this, ICONRES("model"),
tr("InvalidModel"));
}
void MainWindow::on_onion() {
CheckEnabled;
bool ok;
auto index =
DInputDialog::getInt(this, tr("OnionMask"), tr("PleaseInputIndex"),
imglist->currentRow(), 0, imglist->count(), 1, &ok);
if (ok) {
gif.setOnionIndex(index);
}
}
void MainWindow::on_about() {
AboutSoftwareDialog d;
d.exec();
}
void MainWindow::on_sponsor() {
SponsorDialog d;
d.exec();
}
void MainWindow::on_wiki() {
QDesktopServices::openUrl(QUrl("https://code.gitlink.org.cn/wingsummer/"
"WingGifEditor/wiki/%E4%BB%8B%E7%BB%8D"));
}
void MainWindow::closeEvent(QCloseEvent *event) {
player->stop();
if (ensureSafeClose()) {
saveWindowStatus();
event->accept();
} else
event->ignore();
}
void MainWindow::resizeEvent(QResizeEvent *event) { Q_UNUSED(event); }
void MainWindow::dragEnterEvent(QDragEnterEvent *event) {
if (event->mimeData()->hasUrls())
event->acceptProposedAction();
else
event->ignore();
}
void MainWindow::dropEvent(QDropEvent *event) {
const QMimeData *mimeData = event->mimeData();
if (mimeData->hasUrls()) {
auto filename = mimeData->urls().first().toLocalFile();
openGif(filename);
}
}