diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png
index 932b131dd..27bea5736 100644
Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ
diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml
index e2e220767..9dcd78d78 100644
--- a/data/skins/default/skin.xml
+++ b/data/skins/default/skin.xml
@@ -413,6 +413,8 @@
+
+
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index add7b0ac6..3d129d41d 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -350,6 +350,7 @@ add_library(app-lib
transformation.cpp
ui/ani_controls.cpp
ui/app_menuitem.cpp
+ ui/backup_indicator.cpp
ui/brush_popup.cpp
ui/button_set.cpp
ui/color_bar.cpp
diff --git a/src/app/app.cpp b/src/app/app.cpp
index c5803261c..1abb084bb 100644
--- a/src/app/app.cpp
+++ b/src/app/app.cpp
@@ -40,6 +40,7 @@
#include "app/shell.h"
#include "app/tools/active_tool.h"
#include "app/tools/tool_box.h"
+#include "app/ui/backup_indicator.h"
#include "app/ui/color_bar.h"
#include "app/ui/document_view.h"
#include "app/ui/editor/editor.h"
@@ -56,6 +57,7 @@
#include "base/convert_to.h"
#include "base/exception.h"
#include "base/fs.h"
+#include "base/scoped_lock.h"
#include "base/split_string.h"
#include "base/unique_ptr.h"
#include "doc/document_observer.h"
@@ -148,6 +150,7 @@ App::App()
, m_isGui(false)
, m_isShell(false)
, m_exporter(NULL)
+ , m_backupIndicator(nullptr)
{
ASSERT(m_instance == NULL);
m_instance = this;
@@ -750,6 +753,11 @@ App::~App()
// Save brushes
m_brushes.reset(nullptr);
+ if (m_backupIndicator) {
+ delete m_backupIndicator;
+ m_backupIndicator = nullptr;
+ }
+
delete m_legacy;
delete m_modules;
delete m_coreModules;
@@ -842,6 +850,20 @@ void App::showNotification(INotificationDelegate* del)
m_mainWindow->showNotification(del);
}
+void App::showBackupNotification(bool state)
+{
+ base::scoped_lock lock(m_backupIndicatorMutex);
+ if (state) {
+ if (!m_backupIndicator)
+ m_backupIndicator = new BackupIndicator;
+ m_backupIndicator->start();
+ }
+ else {
+ if (m_backupIndicator)
+ m_backupIndicator->stop();
+ }
+}
+
void App::updateDisplayTitleBar()
{
std::string defaultTitle = PACKAGE " v" VERSION;
diff --git a/src/app/app.h b/src/app/app.h
index 7b23b7cc6..ebd520e39 100644
--- a/src/app/app.h
+++ b/src/app/app.h
@@ -9,6 +9,7 @@
#pragma once
#include "app/app_brushes.h"
+#include "base/mutex.h"
#include "base/unique_ptr.h"
#include "doc/pixel_format.h"
#include "obs/signal.h"
@@ -27,6 +28,7 @@ namespace ui {
namespace app {
class AppOptions;
+ class BackupIndicator;
class ContextBar;
class Document;
class DocumentExporter;
@@ -83,6 +85,8 @@ namespace app {
}
void showNotification(INotificationDelegate* del);
+ // This can be called from a non-UI thread.
+ void showBackupNotification(bool state);
void updateDisplayTitleBar();
InputChain& inputChain();
@@ -108,6 +112,8 @@ namespace app {
FileList m_files;
base::UniquePtr m_exporter;
base::UniquePtr m_brushes;
+ BackupIndicator* m_backupIndicator;
+ base::mutex m_backupIndicatorMutex;
};
void app_refresh_screen();
diff --git a/src/app/crash/backup_observer.cpp b/src/app/crash/backup_observer.cpp
index 29889e16b..2f33e09ef 100644
--- a/src/app/crash/backup_observer.cpp
+++ b/src/app/crash/backup_observer.cpp
@@ -23,6 +23,24 @@
namespace app {
namespace crash {
+namespace {
+
+class SwitchBackupIcon {
+public:
+ SwitchBackupIcon() {
+ App* app = App::instance();
+ if (app)
+ app->showBackupNotification(true);
+ }
+ ~SwitchBackupIcon() {
+ App* app = App::instance();
+ if (app)
+ app->showBackupNotification(false);
+ }
+};
+
+}
+
BackupObserver::BackupObserver(Session* session, doc::Context* ctx)
: m_session(session)
, m_ctx(ctx)
@@ -66,7 +84,7 @@ void BackupObserver::backgroundThread()
{
int normalPeriod = 60*Preferences::instance().general.dataRecoveryPeriod();
int lockedPeriod = 10;
-#if 0 // Just for testing purposes
+#if 1 // Just for testing purposes
normalPeriod = 5;
lockedPeriod = 5;
#endif
@@ -79,6 +97,7 @@ void BackupObserver::backgroundThread()
if (seconds >= waitUntil) {
TRACE("RECO: Start backup process for %d documents\n", m_documents.size());
+ SwitchBackupIcon icon;
base::scoped_lock hold(m_mutex);
base::Chrono chrono;
bool somethingLocked = false;
diff --git a/src/app/ui/backup_indicator.cpp b/src/app/ui/backup_indicator.cpp
new file mode 100644
index 000000000..74df51674
--- /dev/null
+++ b/src/app/ui/backup_indicator.cpp
@@ -0,0 +1,58 @@
+// Aseprite
+// Copyright (C) 2001-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/ui/backup_indicator.h"
+
+#include "app/ui/status_bar.h"
+#include "base/bind.h"
+#include "ui/manager.h"
+
+namespace app {
+
+BackupIndicator::BackupIndicator()
+ : m_timer(100)
+ , m_small(false)
+ , m_running(false)
+{
+ m_timer.Tick.connect(base::Bind(&BackupIndicator::onTick, this));
+}
+
+BackupIndicator::~BackupIndicator()
+{
+ m_timer.stop();
+}
+
+void BackupIndicator::start()
+{
+ m_running = true;
+ m_timer.start();
+}
+
+void BackupIndicator::stop()
+{
+ m_running = false;
+}
+
+void BackupIndicator::onTick()
+{
+ if (!m_running) {
+ StatusBar::instance()->showBackupIcon(StatusBar::BackupIcon::None);
+ m_timer.stop();
+ return;
+ }
+
+ StatusBar::instance()->showBackupIcon(
+ m_small ? StatusBar::BackupIcon::Small:
+ StatusBar::BackupIcon::Normal);
+
+ m_small = !m_small;
+}
+
+} // namespace app
diff --git a/src/app/ui/backup_indicator.h b/src/app/ui/backup_indicator.h
new file mode 100644
index 000000000..bb4861ccd
--- /dev/null
+++ b/src/app/ui/backup_indicator.h
@@ -0,0 +1,33 @@
+// Aseprite
+// Copyright (C) 2001-2016 David Capello
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#ifndef APP_UI_BACKUP_INDICATOR_H_INCLUDED
+#define APP_UI_BACKUP_INDICATOR_H_INCLUDED
+#pragma once
+
+#include "ui/timer.h"
+
+namespace app {
+
+ class BackupIndicator {
+ public:
+ BackupIndicator();
+ ~BackupIndicator();
+
+ void start();
+ void stop();
+
+ private:
+ void onTick();
+
+ ui::Timer m_timer;
+ bool m_small;
+ bool m_running;
+ };
+
+} // namespace app
+
+#endif
diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp
index ea601a582..5f454a355 100644
--- a/src/app/ui/status_bar.cpp
+++ b/src/app/ui/status_bar.cpp
@@ -182,7 +182,16 @@ class StatusBar::Indicators : public HBox {
public:
- Indicators() {
+ Indicators() : m_backupIcon(BackupIcon::None) {
+ m_leftArea.setBorder(gfx::Border(0));
+ m_leftArea.setVisible(true);
+ m_leftArea.setExpansive(true);
+
+ m_rightArea.setBorder(gfx::Border(0));
+ m_rightArea.setVisible(false);
+
+ addChild(&m_leftArea);
+ addChild(&m_rightArea);
}
void startIndicators() {
@@ -210,7 +219,7 @@ public:
auto indicator = new TextIndicator(text);
m_indicators.push_back(indicator);
m_iterator = m_indicators.end();
- addChild(indicator);
+ m_leftArea.addChild(indicator);
}
void addIconIndicator(she::Surface* icon, bool colored) {
@@ -228,7 +237,7 @@ public:
auto indicator = new IconIndicator(icon, colored);
m_indicators.push_back(indicator);
m_iterator = m_indicators.end();
- addChild(indicator);
+ m_leftArea.addChild(indicator);
}
void addColorIndicator(const app::Color& color) {
@@ -246,7 +255,29 @@ public:
auto indicator = new ColorIndicator(color);
m_indicators.push_back(indicator);
m_iterator = m_indicators.end();
- addChild(indicator);
+ m_leftArea.addChild(indicator);
+ }
+
+ void showBackupIcon(BackupIcon icon) {
+ m_backupIcon = icon;
+ if (m_backupIcon != BackupIcon::None) {
+ she::Surface* icon =
+ (m_backupIcon == BackupIcon::Normal ?
+ SkinTheme::instance()->parts.iconSave()->bitmap(0):
+ SkinTheme::instance()->parts.iconSaveSmall()->bitmap(0));
+
+ m_rightArea.setVisible(true);
+ if (m_rightArea.children().empty()) {
+ m_rightArea.addChild(new IconIndicator(icon, true));
+ }
+ else {
+ ((IconIndicator*)m_rightArea.lastChild())->updateIndicator(icon, true);
+ }
+ }
+ else {
+ m_rightArea.setVisible(false);
+ }
+ layout();
}
private:
@@ -255,7 +286,7 @@ private:
auto end = m_indicators.end();
for (; it != end; ++it) {
auto indicator = *it;
- removeChild(indicator);
+ m_leftArea.removeChild(indicator);
delete indicator;
}
m_indicators.erase(m_iterator, end);
@@ -263,6 +294,9 @@ private:
std::vector m_indicators;
std::vector::iterator m_iterator;
+ BackupIcon m_backupIcon;
+ HBox m_leftArea;
+ HBox m_rightArea;
};
class StatusBar::IndicatorsGeneration {
@@ -572,6 +606,11 @@ void StatusBar::updateFromEditor(Editor* editor)
m_zoomEntry->setZoom(editor->zoom());
}
+void StatusBar::showBackupIcon(BackupIcon icon)
+{
+ m_indicators->showBackupIcon(icon);
+}
+
bool StatusBar::setStatusText(int msecs, const char *format, ...)
{
if ((base::current_tick() > m_timeout) || (msecs > 0)) {
diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h
index e87919850..7b9ce2ce2 100644
--- a/src/app/ui/status_bar.h
+++ b/src/app/ui/status_bar.h
@@ -51,6 +51,8 @@ namespace app {
public:
static StatusBar* instance() { return m_instance; }
+ enum BackupIcon { None, Normal, Small };
+
StatusBar();
~StatusBar();
@@ -65,6 +67,8 @@ namespace app {
// Used by AppEditor to update the zoom level in the status bar.
void updateFromEditor(Editor* editor);
+ void showBackupIcon(BackupIcon icon);
+
protected:
void onResize(ui::ResizeEvent& ev) override;
diff --git a/src/observable b/src/observable
index 83dfe2753..27fa7f6a4 160000
--- a/src/observable
+++ b/src/observable
@@ -1 +1 @@
-Subproject commit 83dfe27536785acedb258e55ac762486d95e2ab0
+Subproject commit 27fa7f6a4bc7f686da49e6e895ff7dde864b88ad