diff --git a/data/gui.xml b/data/gui.xml index b464d5893..59f71b2fe 100644 --- a/data/gui.xml +++ b/data/gui.xml @@ -11,6 +11,7 @@ + @@ -591,6 +592,7 @@ + diff --git a/data/strings/en.ini b/data/strings/en.ini index 879e43775..7deddd890 100644 --- a/data/strings/en.ini +++ b/data/strings/en.ini @@ -372,6 +372,7 @@ RemoveFrame = Remove Frame RemoveFrameTag = Remove Frame Tag RemoveLayer = Remove Layer RemoveSlice = Remove Slice +ReopenClosedFile = Reopen Closed File RepeatLastExport = Repeat Last Export ReplaceColor = Replace Color ReselectMask = Reselect Mask @@ -691,6 +692,7 @@ file = &File file_new = &New... file_open = &Open... file_open_recent = Open &Recent +file_reopen_closed = Reopen Close&d File file_save = &Save file_save_as = Save &As... file_export = Expor&t... diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 31a562d46..227b780b9 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -264,6 +264,7 @@ if(ENABLE_UI) commands/cmd_remove_frame.cpp commands/cmd_remove_frame_tag.cpp commands/cmd_remove_slice.cpp + commands/cmd_reopen_closed_file.cpp commands/cmd_repeat_last_export.cpp commands/cmd_reselect_mask.cpp commands/cmd_reverse_frames.cpp diff --git a/src/app/commands/cmd_reopen_closed_file.cpp b/src/app/commands/cmd_reopen_closed_file.cpp new file mode 100644 index 000000000..f90b24022 --- /dev/null +++ b/src/app/commands/cmd_reopen_closed_file.cpp @@ -0,0 +1,57 @@ +// Aseprite +// Copyright (C) 2019 Igara Studio S.A. +// +// 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/app.h" +#include "app/commands/command.h" +#include "app/commands/commands.h" +#include "app/doc.h" +#include "app/ui_context.h" + +#include + +namespace app { + +class ReopenClosedFileCommand : public Command { +public: + ReopenClosedFileCommand(); +protected: + bool onEnabled(Context* context) override; + void onExecute(Context* context) override; +}; + +ReopenClosedFileCommand::ReopenClosedFileCommand() + : Command(CommandId::ReopenClosedFile(), CmdUIOnlyFlag) +{ +} + +bool ReopenClosedFileCommand::onEnabled(Context* ctx) +{ + if (auto uiCtx = dynamic_cast(ctx)) { + const auto& docs = uiCtx->closedDocs(); + return (!docs.empty()); + } + return false; +} + +void ReopenClosedFileCommand::onExecute(Context* ctx) +{ + if (auto uiCtx = dynamic_cast(ctx)) { + const auto& docs = uiCtx->closedDocs(); + if (!docs.empty()) + uiCtx->reopenClosedDoc(docs.front()); + } +} + +Command* CommandFactory::createReopenClosedFileCommand() +{ + return new ReopenClosedFileCommand; +} + +} // namespace app diff --git a/src/app/commands/commands_list.h b/src/app/commands/commands_list.h index fa2e438c2..40e3117ff 100644 --- a/src/app/commands/commands_list.h +++ b/src/app/commands/commands_list.h @@ -116,6 +116,7 @@ FOR_EACH_COMMAND(Refresh) FOR_EACH_COMMAND(RemoveFrame) FOR_EACH_COMMAND(RemoveFrameTag) FOR_EACH_COMMAND(RemoveSlice) +FOR_EACH_COMMAND(ReopenClosedFile) FOR_EACH_COMMAND(RepeatLastExport) FOR_EACH_COMMAND(ReplaceColor) FOR_EACH_COMMAND(ReselectMask) diff --git a/src/app/context.cpp b/src/app/context.cpp index cce362fcf..322a47201 100644 --- a/src/app/context.cpp +++ b/src/app/context.cpp @@ -45,6 +45,11 @@ void Context::sendDocumentToTop(Doc* document) documents().move(document, 0); } +void Context::closeDocument(Doc* doc) +{ + onCloseDocument(doc); +} + Site Context::activeSite() const { Site site; @@ -221,4 +226,11 @@ ActiveSiteHandler* Context::activeSiteHandler() const return m_activeSiteHandler.get(); } +void Context::onCloseDocument(Doc* doc) +{ + ASSERT(doc != nullptr); + ASSERT(doc->context() == nullptr); + delete doc; +} + } // namespace app diff --git a/src/app/context.h b/src/app/context.h index a35caf929..0fe1f932a 100644 --- a/src/app/context.h +++ b/src/app/context.h @@ -77,7 +77,8 @@ namespace app { bool checkFlags(uint32_t flags) const { return m_flags.check(flags); } void updateFlags() { m_flags.update(this); } - void sendDocumentToTop(Doc* document); + void sendDocumentToTop(Doc* doc); + void closeDocument(Doc* doc); Site activeSite() const; Doc* activeDocument() const; @@ -110,6 +111,7 @@ namespace app { virtual void onSetActiveDocument(Doc* doc); virtual void onSetActiveLayer(doc::Layer* layer); virtual void onSetActiveFrame(const doc::frame_t frame); + virtual void onCloseDocument(Doc* doc); Doc* lastSelectedDoc() { return m_lastSelectedDoc; } diff --git a/src/app/doc_access.h b/src/app/doc_access.h index f361fb061..06e951616 100644 --- a/src/app/doc_access.h +++ b/src/app/doc_access.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2019 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -181,7 +182,7 @@ namespace app { } void destroyDocument() { - ASSERT(m_doc != NULL); + ASSERT(m_doc != nullptr); m_doc->close(); Doc* doc = m_doc; @@ -191,6 +192,18 @@ namespace app { m_doc = nullptr; } + void closeDocument() { + ASSERT(m_doc != nullptr); + + Context* ctx = (Context*)m_doc->context(); + m_doc->close(); + Doc* doc = m_doc; + unlock(); + + ctx->closeDocument(doc); + m_doc = nullptr; + } + }; class WeakDocReader : public DocAccess { diff --git a/src/app/ui/doc_view.cpp b/src/app/ui/doc_view.cpp index 2d305fe65..52bfc9e85 100644 --- a/src/app/ui/doc_view.cpp +++ b/src/app/ui/doc_view.cpp @@ -330,7 +330,9 @@ bool DocView::onCloseView(Workspace* workspace, bool quitting) ->setStatusText(0, "Sprite '%s' closed.", m_document->name().c_str()); - destroyer.destroyDocument(); + // Just close the document (so we can reopen it with + // ReopenClosedFile command). + destroyer.closeDocument(); // At this point the view is already destroyed return true; diff --git a/src/app/ui_context.cpp b/src/app/ui_context.cpp index 7655fc1f9..11e22468d 100644 --- a/src/app/ui_context.cpp +++ b/src/app/ui_context.cpp @@ -28,6 +28,8 @@ #include "base/mutex.h" #include "doc/sprite.h" +#include + namespace app { UIContext* UIContext::m_instance = nullptr; @@ -223,6 +225,17 @@ Editor* UIContext::activeEditor() return NULL; } +void UIContext::reopenClosedDoc(Doc* doc) +{ + auto it = std::find(m_closedDocs.begin(), m_closedDocs.end(), doc); + ASSERT(it != m_closedDocs.end()); + if (it != m_closedDocs.end()) + m_closedDocs.erase(it); + + // Put the document in the context again. + doc->setContext(this); +} + void UIContext::onAddDocument(Doc* doc) { app::Context::onAddDocument(doc); @@ -295,4 +308,11 @@ void UIContext::onGetActiveSite(Site* site) const } } +void UIContext::onCloseDocument(Doc* doc) +{ + ASSERT(doc != nullptr); + ASSERT(doc->context() == nullptr); + m_closedDocs.insert(m_closedDocs.begin(), doc); +} + } // namespace app diff --git a/src/app/ui_context.h b/src/app/ui_context.h index 99b4e1de2..e1ac0b0bc 100644 --- a/src/app/ui_context.h +++ b/src/app/ui_context.h @@ -12,6 +12,8 @@ #include "app/context.h" #include "app/docs_observer.h" +#include + namespace app { class DocView; class Editor; @@ -42,6 +44,10 @@ namespace app { // new one if it's necessary. Editor* getEditorFor(Doc* document); + // Returns the list of closed docs in this session. + const std::vector& closedDocs() const { return m_closedDocs; } + void reopenClosedDoc(Doc* doc); + protected: void onAddDocument(Doc* doc) override; void onRemoveDocument(Doc* doc) override; @@ -49,9 +55,12 @@ namespace app { void onSetActiveDocument(Doc* doc) override; void onSetActiveLayer(doc::Layer* layer) override; void onSetActiveFrame(const doc::frame_t frame) override; + void onCloseDocument(Doc* doc) override; private: DocView* m_lastSelectedView; + std::vector m_closedDocs; + static UIContext* m_instance; };