New option to configure how much time we keep a closed sprite on RAM

This commit is contained in:
David Capello 2019-06-07 13:17:21 -03:00
parent a7a9d05de3
commit c7d1e4a472
17 changed files with 382 additions and 69 deletions

View File

@ -133,7 +133,9 @@
<option id="data_recovery" type="bool" default="true" />
<option id="data_recovery_period" type="double" default="2.0" />
<option id="keep_edited_sprite_data" type="bool" default="true" />
<option id="keep_edited_sprite_data_lifespan" type="int" default="7" />
<option id="keep_edited_sprite_data_for" type="int" default="7" />
<option id="keep_closed_sprite_on_memory" type="bool" default="true" />
<option id="keep_closed_sprite_on_memory_for" type="double" default="15.0" />
<option id="show_full_path" type="bool" default="true" />
<option id="timeline_position" type="TimelinePosition" default="TimelinePosition::BOTTOM" />
<option id="timeline_layer_panel_width" type="int" default="100" />

View File

@ -137,6 +137,7 @@ Aseprite
END
restart_by_preferences_save_recovery_data_period = Automatically save recovery data every X minutes
restart_by_preferences_keep_edited_sprite_data_lifespan = Keep edited sprite data for X days
restart_by_preferences_keep_closed_sprite_on_memory_for = Keep closed sprite on memory for X minutes
restore_all_shortcuts = <<<END
Warning
<<Do you want to restore all keyboard shortcuts
@ -937,6 +938,7 @@ Check this option to get
this old menus behavior.
END
color_bar_entries_separator = Draw a separation between each palette entry
recover_files = Recover Files
auto_save_recovery_data = Automatically save recovery data every
auto_save_recovery_data_tooltip = <<<END
With this option you can recover your documents
@ -950,6 +952,9 @@ END
10_minutes = 10 Minutes
15_minutes = 15 Minutes
30_minutes = 30 Minutes
1_hour = 1 Hour
4_hours = 4 Hours
8_hours = 8 Hours
keep_edited_sprite_data = Keep edited sprite data for
keep_edited_sprite_data_tooltip = <<<END
With this option you can re-open edited documents
@ -967,6 +972,12 @@ show_full_path_tooltip = <<<END
Uncheck this option if you would prefer to hide
full path on UI (e.g. useful for live streaming)
END
keep_closed_sprite_on_memory = Keep closed sprite on memory for
keep_closed_sprite_on_memory_tooltip = <<<END
When you close a sprite, it will be kept on memory just in case if you
have closed the sprite by mistake, so you can "undo" the close action
using "File > Reopen Closed File" menu option.
END
default_extension_for = Default extension for:
save_default_extension = File > Save:
export_image_default_extension = File > Export (one image):

View File

@ -67,32 +67,7 @@
text="@.color_bar_entries_separator"
tooltip="@.color_bar_entries_separator"
pref="color_bar.entries_separator" />
<grid columns="2">
<check id="enable_data_recovery"
text="@.auto_save_recovery_data"
tooltip="@.auto_save_recovery_data_tooltip" />
<combobox id="data_recovery_period">
<listitem text="@.10_seconds" value="0.1667" />
<listitem text="@.30_seconds" value="0.5" />
<listitem text="@.1_minute" value="1" />
<listitem text="@.2_minutes" value="2" />
<listitem text="@.5_minutes" value="5" />
<listitem text="@.10_minutes" value="10" />
<listitem text="@.15_minutes" value="15" />
<listitem text="@.30_minutes" value="30" />
</combobox>
<check id="keep_edited_sprite_data"
text="@.keep_edited_sprite_data"
tooltip="@.keep_edited_sprite_data_tooltip" />
<combobox id="keep_edited_sprite_data_lifespan">
<listitem text="@.1_day" value="1" />
<listitem text="@.2_days" value="2" />
<listitem text="@.3_days" value="3" />
<listitem text="@.1_week" value="7" />
<listitem text="@.2_weeks" value="14" />
<listitem text="@.1_month" value="30" />
</combobox>
</grid>
<separator horizontal="true" />
<link id="locate_file" text="@.locate_file" />
<link id="locate_crash_folder" text="@.locate_crash_folder" />
@ -101,7 +76,6 @@
<!-- Files -->
<vbox id="section_files">
<separator text="@.section_files" horizontal="true" />
<label text="@.default_extension_for" />
<grid columns="2">
<label text="@.save_default_extension" />
@ -129,6 +103,51 @@
text="@.show_full_path"
tooltip="@.show_full_path_tooltip" />
</grid>
<separator text="@.recover_files" horizontal="true" />
<grid columns="2">
<check id="enable_data_recovery"
text="@.auto_save_recovery_data"
tooltip="@.auto_save_recovery_data_tooltip" />
<combobox id="data_recovery_period">
<listitem text="@.10_seconds" value="0.1667" />
<listitem text="@.30_seconds" value="0.5" />
<listitem text="@.1_minute" value="1" />
<listitem text="@.2_minutes" value="2" />
<listitem text="@.5_minutes" value="5" />
<listitem text="@.10_minutes" value="10" />
<listitem text="@.15_minutes" value="15" />
<listitem text="@.30_minutes" value="30" />
</combobox>
<check id="keep_edited_sprite_data"
text="@.keep_edited_sprite_data"
tooltip="@.keep_edited_sprite_data_tooltip" />
<combobox id="keep_edited_sprite_data_for">
<listitem text="@.1_day" value="1" />
<listitem text="@.2_days" value="2" />
<listitem text="@.3_days" value="3" />
<listitem text="@.1_week" value="7" />
<listitem text="@.2_weeks" value="14" />
<listitem text="@.1_month" value="30" />
</combobox>
<check id="keep_closed_sprite_on_memory"
text="@.keep_closed_sprite_on_memory"
tooltip="@.keep_closed_sprite_on_memory_tooltip" />
<combobox id="keep_closed_sprite_on_memory_for">
<listitem text="@.10_seconds" value="0.1667" />
<listitem text="@.30_seconds" value="0.5" />
<listitem text="@.1_minute" value="1" />
<listitem text="@.2_minutes" value="2" />
<listitem text="@.5_minutes" value="5" />
<listitem text="@.10_minutes" value="10" />
<listitem text="@.15_minutes" value="15" />
<listitem text="@.30_minutes" value="30" />
<listitem text="@.1_hour" value="60" />
<listitem text="@.4_hours" value="240" />
<listitem text="@.8_hours" value="480" />
</combobox>
</grid>
</vbox>
<!-- Color -->

View File

@ -195,6 +195,7 @@ if(ENABLE_UI)
set(ui_app_files
app_brushes.cpp
app_menus.cpp
closed_docs.cpp
commands/cmd_about.cpp
commands/cmd_add_color.cpp
commands/cmd_advanced_mode.cpp

View File

@ -420,7 +420,7 @@ void App::run()
// Destroy all documents from the UIContext.
std::vector<Doc*> docs;
#ifdef ENABLE_UI
for (Doc* doc : m_modules->m_context.closedDocs())
for (Doc* doc : m_modules->m_context.getAndRemoveAllClosedDocs())
docs.push_back(doc);
#endif
for (Doc* doc : m_modules->m_context.documents())

166
src/app/closed_docs.cpp Normal file
View File

@ -0,0 +1,166 @@
// 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/closed_docs.h"
#include "app/doc.h"
#include "app/pref/preferences.h"
#include <algorithm>
#include <limits>
#define CLOSEDOC_TRACE(...)
namespace app {
ClosedDocs::ClosedDocs()
: m_done(false)
{
CLOSEDOC_TRACE("CLOSEDOC: Init");
const auto& pref = Preferences::instance();
if (pref.general.dataRecovery())
m_dataRecoveryPeriodMSecs = int(1000.0*60.0*pref.general.dataRecoveryPeriod());
else
m_dataRecoveryPeriodMSecs = 0;
if (pref.general.keepClosedSpriteOnMemory())
m_keepClosedDocAliveForMSecs = int(1000.0*60.0*pref.general.keepClosedSpriteOnMemoryFor());
else
m_keepClosedDocAliveForMSecs = 0;
}
ClosedDocs::~ClosedDocs()
{
CLOSEDOC_TRACE("CLOSEDOC: Exit");
if (m_thread.joinable()) {
CLOSEDOC_TRACE("CLOSEDOC: Join thread");
m_done = true;
m_cv.notify_one();
m_thread.join();
CLOSEDOC_TRACE("CLOSEDOC: Join done");
}
ASSERT(m_docs.empty());
}
bool ClosedDocs::hasClosedDocs()
{
bool result;
{
std::unique_lock<std::mutex> lock(m_mutex);
result = !m_docs.empty();
}
CLOSEDOC_TRACE("CLOSEDOC: Has closed docs?",
(result ? "true": "false"));
return result;
}
void ClosedDocs::addClosedDoc(Doc* doc)
{
CLOSEDOC_TRACE("CLOSEDOC: Add closed doc", doc);
ASSERT(doc != nullptr);
ASSERT(doc->context() == nullptr);
ClosedDoc closedDoc = { doc, base::current_tick() };
std::unique_lock<std::mutex> lock(m_mutex);
m_docs.insert(m_docs.begin(), std::move(closedDoc));
if (!m_thread.joinable())
m_thread = std::thread([this]{ backgroundThread(); });
else
m_cv.notify_one();
}
Doc* ClosedDocs::reopenLastClosedDoc()
{
Doc* doc = nullptr;
{
std::unique_lock<std::mutex> lock(m_mutex);
if (!m_docs.empty()) {
doc = m_docs.front().doc;
m_docs.erase(m_docs.begin());
}
CLOSEDOC_TRACE(" -> ", doc);
}
CLOSEDOC_TRACE("CLOSEDOC: Reopen last closed doc", doc);
return doc;
}
std::vector<Doc*> ClosedDocs::getAndRemoveAllClosedDocs()
{
std::vector<Doc*> docs;
{
std::unique_lock<std::mutex> lock(m_mutex);
CLOSEDOC_TRACE("CLOSEDOC: Get and remove all closed", m_docs.size(), "docs");
for (const ClosedDoc& closedDoc : m_docs)
docs.push_back(closedDoc.doc);
m_docs.clear();
m_done = true;
m_cv.notify_one();
}
return docs;
}
void ClosedDocs::backgroundThread()
{
CLOSEDOC_TRACE("CLOSEDOC: [BG] Background thread start");
std::unique_lock<std::mutex> lock(m_mutex);
while (!m_done) {
base::tick_t now = base::current_tick();
base::tick_t waitForMSecs = std::numeric_limits<base::tick_t>::max();
for (auto it=m_docs.begin(); it != m_docs.end(); ) {
const ClosedDoc& closedDoc = *it;
base::tick_t diff = now - closedDoc.timestamp;
if (diff >= m_keepClosedDocAliveForMSecs) {
if (m_dataRecoveryPeriodMSecs == 0 ||
closedDoc.doc->isFullyBackedUp()) {
// Finally delete the document (this is the place where we
// delete all documents created/loaded by the user)
CLOSEDOC_TRACE("CLOSEDOC: [BG] Delete doc", closedDoc.doc);
delete closedDoc.doc;
it = m_docs.erase(it);
}
else {
waitForMSecs = std::min(waitForMSecs, m_dataRecoveryPeriodMSecs);
++it;
}
}
else {
waitForMSecs = std::min(waitForMSecs, m_keepClosedDocAliveForMSecs-diff);
++it;
}
}
if (waitForMSecs < std::numeric_limits<base::tick_t>::max()) {
CLOSEDOC_TRACE("CLOSEDOC: [BG] Wait for", waitForMSecs, "milliseconds");
ASSERT(!m_docs.empty());
m_cv.wait_for(lock, std::chrono::milliseconds(waitForMSecs));
}
else {
CLOSEDOC_TRACE("CLOSEDOC: [BG] Wait for condition variable");
ASSERT(m_docs.empty());
m_cv.wait(lock);
}
}
CLOSEDOC_TRACE("CLOSEDOC: [BG] Background thread end");
}
} // namespace app

62
src/app/closed_docs.h Normal file
View File

@ -0,0 +1,62 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CLOSED_DOCS_H_INCLUDED
#define APP_CLOSED_DOCS_H_INCLUDED
#pragma once
#include "base/time.h"
#include <condition_variable>
#include <mutex>
#include <thread>
#include <vector>
namespace app {
class Doc;
// Handle the list of closed docs:
// * When a document is closed, we keep it for some time so the user
// can undo the close command without losing the undo history.
// * For the first closed document, a thread is launched to wait
// until we can definitely delete the doc after X minutes (like a
// garbage collector).
// * If the document was not restore, we delete it from memory, if
// the document was restore, we remove it from the m_docs.
class ClosedDocs {
public:
ClosedDocs();
~ClosedDocs();
bool hasClosedDocs();
void addClosedDoc(Doc* doc);
Doc* reopenLastClosedDoc();
// Called at the very end to get all closed docs, remove them from
// the list of closed docs, and stop the thread.
std::vector<Doc*> getAndRemoveAllClosedDocs();
private:
void backgroundThread();
struct ClosedDoc {
Doc* doc;
base::tick_t timestamp;
};
bool m_done;
base::tick_t m_dataRecoveryPeriodMSecs;
base::tick_t m_keepClosedDocAliveForMSecs;
std::vector<ClosedDoc> m_docs;
std::mutex m_mutex;
std::condition_variable m_cv;
std::thread m_thread;
};
} // namespace app
#endif

View File

@ -245,6 +245,9 @@ public:
if (m_pref.general.keepEditedSpriteData())
keepEditedSpriteData()->setSelected(true);
if (m_pref.general.keepClosedSpriteOnMemory())
keepClosedSpriteOnMemory()->setSelected(true);
if (m_pref.general.showFullPath())
showFullPath()->setSelected(true);
@ -252,9 +255,13 @@ public:
dataRecoveryPeriod()->findItemIndexByValue(
base::convert_to<std::string>(m_pref.general.dataRecoveryPeriod())));
keepEditedSpriteDataLifespan()->setSelectedItemIndex(
keepEditedSpriteDataLifespan()->findItemIndexByValue(
base::convert_to<std::string>(m_pref.general.keepEditedSpriteDataLifespan())));
keepEditedSpriteDataFor()->setSelectedItemIndex(
keepEditedSpriteDataFor()->findItemIndexByValue(
base::convert_to<std::string>(m_pref.general.keepEditedSpriteDataFor())));
keepClosedSpriteOnMemoryFor()->setSelectedItemIndex(
keepClosedSpriteOnMemoryFor()->findItemIndexByValue(
base::convert_to<std::string>(m_pref.general.keepClosedSpriteOnMemoryFor())));
if (m_pref.editor.zoomFromCenterWithWheel())
zoomFromCenterWithWheel()->setSelected(true);
@ -486,15 +493,24 @@ public:
warnings += "<<- " + Strings::alerts_restart_by_preferences_save_recovery_data_period();
}
int newLifespan = base::convert_to<int>(keepEditedSpriteDataLifespan()->getValue());
int newLifespan = base::convert_to<int>(keepEditedSpriteDataFor()->getValue());
if (keepEditedSpriteData()->isSelected() != m_pref.general.keepEditedSpriteData() ||
newLifespan != m_pref.general.keepEditedSpriteDataLifespan()) {
newLifespan != m_pref.general.keepEditedSpriteDataFor()) {
m_pref.general.keepEditedSpriteData(keepEditedSpriteData()->isSelected());
m_pref.general.keepEditedSpriteDataLifespan(newLifespan);
m_pref.general.keepEditedSpriteDataFor(newLifespan);
warnings += "<<- " + Strings::alerts_restart_by_preferences_keep_edited_sprite_data_lifespan();
}
newLifespan = base::convert_to<int>(keepClosedSpriteOnMemoryFor()->getValue());
if (keepClosedSpriteOnMemory()->isSelected() != m_pref.general.keepClosedSpriteOnMemory() ||
newLifespan != m_pref.general.keepClosedSpriteOnMemoryFor()) {
m_pref.general.keepClosedSpriteOnMemory(keepClosedSpriteOnMemory()->isSelected());
m_pref.general.keepClosedSpriteOnMemoryFor(newLifespan);
warnings += "<<- " + Strings::alerts_restart_by_preferences_keep_closed_sprite_on_memory_for();
}
m_pref.editor.zoomFromCenterWithWheel(zoomFromCenterWithWheel()->isSelected());
m_pref.editor.zoomFromCenterWithKeys(zoomFromCenterWithKeys()->isSelected());
m_pref.editor.showScrollbars(showScrollbars()->isSelected());

View File

@ -34,19 +34,15 @@ ReopenClosedFileCommand::ReopenClosedFileCommand()
bool ReopenClosedFileCommand::onEnabled(Context* ctx)
{
if (auto uiCtx = dynamic_cast<UIContext*>(ctx)) {
const auto& docs = uiCtx->closedDocs();
return (!docs.empty());
return uiCtx->hasClosedDocs();
}
return false;
}
void ReopenClosedFileCommand::onExecute(Context* ctx)
{
if (auto uiCtx = dynamic_cast<UIContext*>(ctx)) {
const auto& docs = uiCtx->closedDocs();
if (!docs.empty())
uiCtx->reopenClosedDoc(docs.front());
}
if (auto uiCtx = dynamic_cast<UIContext*>(ctx))
uiCtx->reopenLastClosedDoc();
}
Command* CommandFactory::createReopenClosedFileCommand()

View File

@ -97,7 +97,7 @@ void BackupObserver::onRemoveDocument(Doc* doc)
std::unique_lock<std::mutex> lock(m_mutex);
base::remove_from_container(m_documents, doc);
}
if (m_config->keepEditedSpriteData)
if (m_config->keepEditedSpriteDataFor > 0)
m_closedDocs.push_back(doc);
else
m_session->removeDocument(doc);
@ -119,7 +119,8 @@ void BackupObserver::backgroundThread()
while (!m_done) {
m_wakeup.wait_for(lock, std::chrono::seconds(waitFor));
TRACE("RECO: Start backup process for %d documents\n", m_documents.size());
TRACE("RECO: Start backup process for %d documents\n",
m_documents.size() + m_closedDocs.size());
SwitchBackupIcon icon;
base::Chrono chrono;
@ -133,8 +134,15 @@ void BackupObserver::backgroundThread()
if (!m_closedDocs.empty()) {
for (auto it=m_closedDocs.begin(); it != m_closedDocs.end(); ) {
Doc* doc = *it;
if (saveDocData(doc))
TRACE("RECO: Save backup data for %p...\n", doc);
if (saveDocData(doc)) {
TRACE("RECO: Doc %p is fully backed up\n", doc);
it = m_closedDocs.erase(it);
doc->markAsBackedUp();
}
else {
somethingLocked = true;
++it;
@ -161,8 +169,8 @@ bool BackupObserver::saveDocData(Doc* doc)
else if (!m_session->saveDocumentChanges(doc)) {
TRACE("RECO: Document '%d' backup was canceled by UI\n", doc->id());
}
#ifdef TEST_BACKUP_INTEGRITY
else {
#ifdef TEST_BACKUP_INTEGRITY
DocReader reader(doc, 500);
std::unique_ptr<Doc> copy(
m_session->restoreBackupDocById(doc->id(), nullptr));
@ -188,9 +196,9 @@ bool BackupObserver::saveDocData(Doc* doc)
else {
TRACE("RECO: No differences\n");
}
#endif
return true;
}
#endif
}
catch (const std::exception&) {
TRACE("RECO: Document '%d' is locked\n", doc->id());

View File

@ -37,8 +37,10 @@ DataRecovery::DataRecovery(Context* ctx)
{
auto& pref = Preferences::instance();
m_config.dataRecoveryPeriod = pref.general.dataRecoveryPeriod();
m_config.keepEditedSpriteData = pref.general.keepEditedSpriteData();
m_config.keepEditedSpriteDataLifespan = pref.general.keepEditedSpriteDataLifespan();
if (pref.general.keepEditedSpriteData())
m_config.keepEditedSpriteDataFor = pref.general.keepEditedSpriteDataFor();
else
m_config.keepEditedSpriteDataFor = 0;
ResourceFinder rf;
rf.includeUserDir(base::join_path("sessions", ".").c_str());

View File

@ -15,8 +15,7 @@ namespace crash {
// avoid accessing to Preferences from a non-UI thread.
struct RecoveryConfig {
double dataRecoveryPeriod;
bool keepEditedSpriteData;
int keepEditedSpriteDataLifespan;
int keepEditedSpriteDataFor;
};
} // namespace crash

View File

@ -140,17 +140,17 @@ bool Session::isCrashedSession()
bool Session::isOldSession()
{
if (!m_config->keepEditedSpriteData)
if (m_config->keepEditedSpriteDataFor <= 0)
return true;
std::string verfile = verFilename();
if (!base::is_file(verfile))
return true;
int lifespan = m_config->keepEditedSpriteDataLifespan;
int lifespanDays = m_config->keepEditedSpriteDataFor;
base::Time sessionTime = base::get_modification_time(verfile);
return (sessionTime.addDays(lifespan) < base::current_time());
return (sessionTime.addDays(lifespanDays) < base::current_time());
}
bool Session::isEmpty()
@ -183,7 +183,7 @@ void Session::close()
// If we don't have to keep the sprite data, just remove it from
// the disk.
if (!m_config->keepEditedSpriteData)
if (m_config->keepEditedSpriteDataFor == 0)
removeFromDisk();
}
catch (const std::exception&) {

View File

@ -75,8 +75,15 @@ void Doc::setContext(Context* ctx)
removeFromContext();
m_ctx = ctx;
if (ctx)
if (ctx) {
// Remove the flag that indicates that this doc is fully backed
// up, because now we are inside a context, so the user can change
// it again and the backup will be outdated.
if (m_flags & kFullyBackedUp)
m_flags ^= kFullyBackedUp;
ctx->documents().add(this);
}
onContextChanged();
}
@ -227,6 +234,16 @@ void Doc::setInhibitBackup(const bool inhibitBackup)
m_flags &= ~kInhibitBackup;
}
void Doc::markAsBackedUp()
{
m_flags |= kFullyBackedUp;
}
bool Doc::isFullyBackedUp() const
{
return (m_flags & kFullyBackedUp ? true: false);
}
//////////////////////////////////////////////////////////////////////
// Loaded options from file

View File

@ -63,6 +63,7 @@ namespace app {
kAssociatedToFile = 1, // This sprite is associated to a file in the file-system
kMaskVisible = 2, // The mask wasn't hidden by the user
kInhibitBackup = 4, // Inhibit the backup process
kFullyBackedUp = 8, // Full backup was done
};
public:
Doc(Sprite* sprite);
@ -122,6 +123,9 @@ namespace app {
bool inhibitBackup() const;
void setInhibitBackup(const bool inhibitBackup);
void markAsBackedUp();
bool isFullyBackedUp() const;
//////////////////////////////////////////////////////////////////////
// Loaded options from file

View File

@ -9,6 +9,8 @@
#include "config.h"
#endif
#include "app/ui_context.h"
#include "app/app.h"
#include "app/doc.h"
#include "app/modules/editors.h"
@ -24,7 +26,6 @@
#include "app/ui/timeline/timeline.h"
#include "app/ui/workspace.h"
#include "app/ui/workspace_tabs.h"
#include "app/ui_context.h"
#include "base/mutex.h"
#include "doc/sprite.h"
@ -225,16 +226,23 @@ Editor* UIContext::activeEditor()
return NULL;
}
void UIContext::reopenClosedDoc(Doc* doc)
bool UIContext::hasClosedDocs()
{
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);
return m_closedDocs.hasClosedDocs();
}
void UIContext::reopenLastClosedDoc()
{
if (Doc* doc = m_closedDocs.reopenLastClosedDoc()) {
// Put the document in the context again.
doc->setContext(this);
}
}
std::vector<Doc*> UIContext::getAndRemoveAllClosedDocs()
{
return m_closedDocs.getAndRemoveAllClosedDocs();
}
void UIContext::onAddDocument(Doc* doc)
{
@ -312,7 +320,8 @@ void UIContext::onCloseDocument(Doc* doc)
{
ASSERT(doc != nullptr);
ASSERT(doc->context() == nullptr);
m_closedDocs.insert(m_closedDocs.begin(), doc);
m_closedDocs.addClosedDoc(doc);
}
} // namespace app

View File

@ -9,6 +9,7 @@
#define APP_UI_CONTEXT_H_INCLUDED
#pragma once
#include "app/closed_docs.h"
#include "app/context.h"
#include "app/docs_observer.h"
@ -44,9 +45,9 @@ namespace app {
// new one if it's necessary.
Editor* getEditorFor(Doc* document);
// Returns the list of closed docs in this session.
const std::vector<Doc*>& closedDocs() const { return m_closedDocs; }
void reopenClosedDoc(Doc* doc);
bool hasClosedDocs();
void reopenLastClosedDoc();
std::vector<Doc*> getAndRemoveAllClosedDocs();
protected:
void onAddDocument(Doc* doc) override;
@ -59,7 +60,7 @@ namespace app {
private:
DocView* m_lastSelectedView;
std::vector<Doc*> m_closedDocs;
ClosedDocs m_closedDocs;
static UIContext* m_instance;
};