mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-30 22:21:12 +00:00
Closes #1423 The bug was happening because when the Undo History window was closed using the keyboard shortcut, the message kCloseMessage was never being sent to the window. This is probably not the only cause, but making UndoHistoryCommand close the window instead of just hiding it solves the problem.
260 lines
6.2 KiB
C++
260 lines
6.2 KiB
C++
// Aseprite
|
|
// Copyright (C) 2015-2016 David Capello
|
|
//
|
|
// This program is distributed under the terms of
|
|
// the End-User License Agreement for Aseprite.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "app/cmd.h"
|
|
#include "app/commands/command.h"
|
|
#include "app/console.h"
|
|
#include "app/context.h"
|
|
#include "app/document.h"
|
|
#include "app/document_access.h"
|
|
#include "app/document_undo.h"
|
|
#include "app/document_undo_observer.h"
|
|
#include "app/modules/gui.h"
|
|
#include "app/modules/palettes.h"
|
|
#include "base/bind.h"
|
|
#include "base/mem_utils.h"
|
|
#include "doc/context_observer.h"
|
|
#include "doc/documents_observer.h"
|
|
#include "doc/site.h"
|
|
#include "undo/undo_state.h"
|
|
|
|
#include "undo_history.xml.h"
|
|
|
|
namespace app {
|
|
|
|
class UndoHistoryWindow : public app::gen::UndoHistory,
|
|
public doc::ContextObserver,
|
|
public doc::DocumentsObserver,
|
|
public app::DocumentUndoObserver {
|
|
public:
|
|
class Item : public ui::ListItem {
|
|
public:
|
|
Item(const undo::UndoState* state)
|
|
: ui::ListItem(
|
|
(state ?
|
|
static_cast<Cmd*>(state->cmd())->label()
|
|
#if _DEBUG
|
|
+ std::string(" ") + base::get_pretty_memory_size(static_cast<Cmd*>(state->cmd())->memSize())
|
|
#endif
|
|
: std::string("Initial State"))),
|
|
m_state(state) {
|
|
}
|
|
const undo::UndoState* state() { return m_state; }
|
|
private:
|
|
const undo::UndoState* m_state;
|
|
};
|
|
|
|
UndoHistoryWindow(Context* ctx)
|
|
: m_ctx(ctx),
|
|
m_document(nullptr) {
|
|
actions()->Change.connect(&UndoHistoryWindow::onChangeAction, this);
|
|
}
|
|
|
|
~UndoHistoryWindow() {
|
|
}
|
|
|
|
private:
|
|
bool onProcessMessage(ui::Message* msg) override {
|
|
switch (msg->type()) {
|
|
|
|
case ui::kOpenMessage:
|
|
load_window_pos(this, "UndoHistory");
|
|
|
|
m_ctx->add_observer(this);
|
|
m_ctx->documents().add_observer(this);
|
|
if (m_ctx->activeDocument()) {
|
|
m_frame = m_ctx->activeSite().frame();
|
|
|
|
attachDocument(
|
|
static_cast<app::Document*>(m_ctx->activeDocument()));
|
|
}
|
|
break;
|
|
|
|
case ui::kCloseMessage:
|
|
save_window_pos(this, "UndoHistory");
|
|
|
|
if (m_document)
|
|
detachDocument();
|
|
m_ctx->documents().remove_observer(this);
|
|
m_ctx->remove_observer(this);
|
|
break;
|
|
}
|
|
return app::gen::UndoHistory::onProcessMessage(msg);
|
|
}
|
|
|
|
void onChangeAction() {
|
|
Item* item = static_cast<Item*>(
|
|
actions()->getSelectedChild());
|
|
|
|
if (m_document &&
|
|
m_document->undoHistory()->currentState() != item->state()) {
|
|
try {
|
|
DocumentWriter writer(m_document, 100);
|
|
m_document->undoHistory()->moveToState(item->state());
|
|
m_document->generateMaskBoundaries();
|
|
|
|
// TODO this should be an observer of the current document palette
|
|
set_current_palette(m_document->sprite()->palette(m_frame),
|
|
false);
|
|
|
|
m_document->notifyGeneralUpdate();
|
|
}
|
|
catch (const std::exception& ex) {
|
|
selectState(m_document->undoHistory()->currentState());
|
|
Console::showException(ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ContextObserver
|
|
void onActiveSiteChange(const doc::Site& site) override {
|
|
m_frame = site.frame();
|
|
|
|
if (m_document == site.document())
|
|
return;
|
|
|
|
attachDocument(
|
|
static_cast<app::Document*>(
|
|
const_cast<doc::Document*>(site.document())));
|
|
}
|
|
|
|
// DocumentsObserver
|
|
void onRemoveDocument(doc::Document* doc) override {
|
|
if (m_document && m_document == doc)
|
|
detachDocument();
|
|
}
|
|
|
|
// DocumentUndoObserver
|
|
void onAddUndoState(DocumentUndo* history) override {
|
|
ASSERT(history->currentState());
|
|
Item* item = new Item(history->currentState());
|
|
actions()->addChild(item);
|
|
actions()->layout();
|
|
view()->updateView();
|
|
actions()->selectChild(item);
|
|
}
|
|
|
|
void onAfterUndo(DocumentUndo* history) override {
|
|
selectState(history->currentState());
|
|
}
|
|
|
|
void onAfterRedo(DocumentUndo* history) override {
|
|
selectState(history->currentState());
|
|
}
|
|
|
|
void onClearRedo(DocumentUndo* history) override {
|
|
refillList(history);
|
|
}
|
|
|
|
void attachDocument(app::Document* document) {
|
|
detachDocument();
|
|
|
|
m_document = document;
|
|
if (!document)
|
|
return;
|
|
|
|
DocumentUndo* history = m_document->undoHistory();
|
|
history->add_observer(this);
|
|
|
|
refillList(history);
|
|
}
|
|
|
|
void detachDocument() {
|
|
if (!m_document)
|
|
return;
|
|
|
|
clearList();
|
|
m_document->undoHistory()->remove_observer(this);
|
|
m_document = nullptr;
|
|
}
|
|
|
|
void clearList() {
|
|
ui::Widget* child;
|
|
while ((child = actions()->firstChild()))
|
|
delete child;
|
|
|
|
actions()->layout();
|
|
view()->updateView();
|
|
}
|
|
|
|
void refillList(DocumentUndo* history) {
|
|
clearList();
|
|
|
|
// Create an item to reference the initial state (undo state == nullptr)
|
|
Item* current = new Item(nullptr);
|
|
actions()->addChild(current);
|
|
|
|
const undo::UndoState* state = history->firstState();
|
|
while (state) {
|
|
Item* item = new Item(state);
|
|
actions()->addChild(item);
|
|
if (state == history->currentState())
|
|
current = item;
|
|
|
|
state = state->next();
|
|
}
|
|
|
|
actions()->layout();
|
|
view()->updateView();
|
|
if (current)
|
|
actions()->selectChild(current);
|
|
}
|
|
|
|
void selectState(const undo::UndoState* state) {
|
|
for (auto child : actions()->children()) {
|
|
Item* item = static_cast<Item*>(child);
|
|
if (item->state() == state) {
|
|
actions()->selectChild(item);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Context* m_ctx;
|
|
app::Document* m_document;
|
|
doc::frame_t m_frame;
|
|
};
|
|
|
|
class UndoHistoryCommand : public Command {
|
|
public:
|
|
UndoHistoryCommand();
|
|
Command* clone() const override { return new UndoHistoryCommand(*this); }
|
|
|
|
protected:
|
|
void onExecute(Context* ctx) override;
|
|
};
|
|
|
|
static UndoHistoryWindow* g_window = NULL;
|
|
|
|
UndoHistoryCommand::UndoHistoryCommand()
|
|
: Command("UndoHistory",
|
|
"Undo History",
|
|
CmdUIOnlyFlag)
|
|
{
|
|
}
|
|
|
|
void UndoHistoryCommand::onExecute(Context* ctx)
|
|
{
|
|
if (!g_window)
|
|
g_window = new UndoHistoryWindow(ctx);
|
|
|
|
if (g_window->isVisible())
|
|
g_window->closeWindow(nullptr);
|
|
else
|
|
g_window->openWindow();
|
|
}
|
|
|
|
Command* CommandFactory::createUndoHistoryCommand()
|
|
{
|
|
return new UndoHistoryCommand;
|
|
}
|
|
|
|
} // namespace app
|