mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
Replace deprecated "Monitor" struct from gui module with ui::Timers.
- Added ThumbnailGenerator class to avoid mixing the thumbnail generation code with FileList widget. - Added IFileOpProgress for fop_operate() so we can update the job progress when the "file operation" progress changes.
This commit is contained in:
parent
09ecf4c588
commit
ebb8d0c5bd
@ -156,6 +156,7 @@ add_library(aseprite-library
|
||||
recent_files.cpp
|
||||
resource_finder.cpp
|
||||
scoped_allegro.cpp
|
||||
thumbnail_generator.cpp
|
||||
ui_context.cpp
|
||||
undo_transaction.cpp
|
||||
xml_exception.cpp
|
||||
|
@ -25,12 +25,13 @@
|
||||
#include "app.h"
|
||||
#include "base/bind.h"
|
||||
#include "ini_file.h"
|
||||
#include "modules/gui.h"
|
||||
#include "widgets/status_bar.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
|
||||
static const int kMonitoringPeriod = 100;
|
||||
|
||||
namespace app {
|
||||
|
||||
class CheckUpdateBackgroundJob : public updater::CheckUpdateDelegate
|
||||
@ -85,10 +86,10 @@ private:
|
||||
CheckUpdateThreadLauncher::CheckUpdateThreadLauncher()
|
||||
: m_doCheck(true)
|
||||
, m_received(false)
|
||||
, m_guiMonitor(NULL)
|
||||
, m_inits(get_config_int("Updater", "Inits", 0))
|
||||
, m_exits(get_config_int("Updater", "Exits", 0))
|
||||
, m_isDeveloper(get_config_bool("Updater", "IsDeveloper", false))
|
||||
, m_timer(kMonitoringPeriod, NULL)
|
||||
{
|
||||
// Get how many days we have to wait for the next "check for update"
|
||||
int waitDays = get_config_int("Updater", "WaitDays", 0);
|
||||
@ -112,10 +113,8 @@ CheckUpdateThreadLauncher::CheckUpdateThreadLauncher()
|
||||
|
||||
CheckUpdateThreadLauncher::~CheckUpdateThreadLauncher()
|
||||
{
|
||||
if (m_guiMonitor) {
|
||||
remove_gui_monitor(m_guiMonitor);
|
||||
m_guiMonitor = NULL;
|
||||
}
|
||||
if (m_timer.isRunning())
|
||||
m_timer.stop();
|
||||
|
||||
if (m_thread) {
|
||||
if (m_bgJob)
|
||||
@ -142,10 +141,11 @@ void CheckUpdateThreadLauncher::launch()
|
||||
m_bgJob.reset(new CheckUpdateBackgroundJob);
|
||||
m_thread.reset(new base::thread(Bind<void>(&CheckUpdateThreadLauncher::checkForUpdates, this)));
|
||||
|
||||
// Start a timer to monitor the progress of the background job
|
||||
// executed in "m_thread". The "monitorProxy" method will be called
|
||||
// periodically by the GUI main thread.
|
||||
m_guiMonitor = add_gui_monitor(CheckUpdateThreadLauncher::monitorProxy, NULL, (void*)this);
|
||||
// Start a timer to monitoring the progress of the background job
|
||||
// executed in "m_thread". The "onMonitoringTick" method will be
|
||||
// called periodically by the GUI main thread.
|
||||
m_timer.Tick.connect(&CheckUpdateThreadLauncher::onMonitoringTick, this);
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
bool CheckUpdateThreadLauncher::isReceived() const
|
||||
@ -153,7 +153,7 @@ bool CheckUpdateThreadLauncher::isReceived() const
|
||||
return m_received;
|
||||
}
|
||||
|
||||
void CheckUpdateThreadLauncher::monitorActivity()
|
||||
void CheckUpdateThreadLauncher::onMonitoringTick()
|
||||
{
|
||||
// If we do not receive a response yet...
|
||||
if (!m_received)
|
||||
@ -185,14 +185,8 @@ void CheckUpdateThreadLauncher::monitorActivity()
|
||||
// Save the config file right now
|
||||
flush_config_file();
|
||||
|
||||
// Remove the monitor
|
||||
remove_gui_monitor(m_guiMonitor);
|
||||
m_guiMonitor = NULL;
|
||||
}
|
||||
|
||||
void CheckUpdateThreadLauncher::monitorProxy(void* data)
|
||||
{
|
||||
((CheckUpdateThreadLauncher*)data)->monitorActivity();
|
||||
// Stop the monitoring timer.
|
||||
m_timer.stop();
|
||||
}
|
||||
|
||||
// This method is executed in a special thread to send the HTTP request.
|
||||
|
@ -23,10 +23,9 @@
|
||||
|
||||
#include "base/thread.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "ui/timer.h"
|
||||
#include "updater/check_update.h"
|
||||
|
||||
struct Monitor;
|
||||
|
||||
namespace app {
|
||||
|
||||
class CheckUpdateBackgroundJob;
|
||||
@ -47,9 +46,7 @@ namespace app {
|
||||
}
|
||||
|
||||
private:
|
||||
void monitorActivity();
|
||||
static void monitorProxy(void* data);
|
||||
|
||||
void onMonitoringTick();
|
||||
void checkForUpdates();
|
||||
|
||||
updater::Uuid m_uuid;
|
||||
@ -58,7 +55,7 @@ namespace app {
|
||||
bool m_doCheck;
|
||||
bool m_received;
|
||||
updater::CheckUpdateResponse m_response;
|
||||
Monitor* m_guiMonitor;
|
||||
ui::Timer m_timer;
|
||||
|
||||
// Mini-stats
|
||||
int m_inits;
|
||||
|
@ -18,17 +18,17 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <allegro.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "app/file_selector.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/thread.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "commands/command.h"
|
||||
#include "commands/params.h"
|
||||
#include "console.h"
|
||||
#include "document.h"
|
||||
#include "file/file.h"
|
||||
#include "job.h"
|
||||
#include "modules/editors.h"
|
||||
#include "modules/gui.h"
|
||||
#include "raster/sprite.h"
|
||||
@ -37,13 +37,16 @@
|
||||
#include "ui_context.h"
|
||||
#include "widgets/status_bar.h"
|
||||
|
||||
#include <allegro.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static const int kMonitoringPeriod = 100;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// open_file
|
||||
|
||||
class OpenFileCommand : public Command
|
||||
{
|
||||
std::string m_filename;
|
||||
|
||||
public:
|
||||
OpenFileCommand();
|
||||
Command* clone() { return new OpenFileCommand(*this); }
|
||||
@ -51,72 +54,50 @@ public:
|
||||
protected:
|
||||
void onLoadParams(Params* params);
|
||||
void onExecute(Context* context);
|
||||
|
||||
private:
|
||||
std::string m_filename;
|
||||
};
|
||||
|
||||
struct OpenFileData
|
||||
class OpenFileJob : public Job, public IFileOpProgress
|
||||
{
|
||||
Monitor *monitor;
|
||||
FileOp *fop;
|
||||
Progress *progress;
|
||||
ui::AlertPtr alert_window;
|
||||
public:
|
||||
OpenFileJob(FileOp* fop, const char* filename)
|
||||
: Job("Loading file")
|
||||
, m_fop(fop)
|
||||
{
|
||||
}
|
||||
|
||||
void showProgressWindow() {
|
||||
startJob();
|
||||
fop_stop(m_fop);
|
||||
}
|
||||
|
||||
private:
|
||||
// Thread to do the hard work: load the file from the disk.
|
||||
virtual void onJob() OVERRIDE {
|
||||
try {
|
||||
fop_operate(m_fop, this);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
fop_error(m_fop, "Error loading file:\n%s", e.what());
|
||||
}
|
||||
|
||||
if (fop_is_stop(m_fop) && m_fop->document) {
|
||||
delete m_fop->document;
|
||||
m_fop->document = NULL;
|
||||
}
|
||||
|
||||
fop_done(m_fop);
|
||||
}
|
||||
|
||||
virtual void ackFileOpProgress(double progress) OVERRIDE {
|
||||
jobProgress(progress);
|
||||
}
|
||||
|
||||
FileOp* m_fop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread to do the hard work: load the file from the disk.
|
||||
*
|
||||
* [loading thread]
|
||||
*/
|
||||
static void openfile_bg(FileOp* fop)
|
||||
{
|
||||
try {
|
||||
fop_operate(fop);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
fop_error(fop, "Error loading file:\n%s", e.what());
|
||||
}
|
||||
|
||||
if (fop_is_stop(fop) && fop->document) {
|
||||
delete fop->document;
|
||||
fop->document = NULL;
|
||||
}
|
||||
|
||||
fop_done(fop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the gui-monitor (a timer in the gui module that is called
|
||||
* every 100 milliseconds).
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
static void monitor_openfile_bg(void* _data)
|
||||
{
|
||||
OpenFileData* data = (OpenFileData*)_data;
|
||||
FileOp* fop = (FileOp*)data->fop;
|
||||
|
||||
if (data->progress)
|
||||
data->progress->setPos(fop_get_progress(fop));
|
||||
|
||||
// Is done? ...ok, now the sprite is in the main thread only...
|
||||
if (fop_is_done(fop))
|
||||
remove_gui_monitor(data->monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to destroy the data of the monitor.
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
static void monitor_free(void* _data)
|
||||
{
|
||||
OpenFileData* data = (OpenFileData*)_data;
|
||||
|
||||
if (data->alert_window != NULL) {
|
||||
data->monitor = NULL;
|
||||
data->alert_window->closeWindow(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
OpenFileCommand::OpenFileCommand()
|
||||
: Command("OpenFile",
|
||||
"Open Sprite",
|
||||
@ -130,11 +111,6 @@ void OpenFileCommand::onLoadParams(Params* params)
|
||||
m_filename = params->get("filename");
|
||||
}
|
||||
|
||||
/**
|
||||
* Command to open a file.
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
void OpenFileCommand::onExecute(Context* context)
|
||||
{
|
||||
Console console;
|
||||
@ -147,38 +123,17 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
}
|
||||
|
||||
if (!m_filename.empty()) {
|
||||
FileOp *fop = fop_to_load_document(m_filename.c_str(), FILE_LOAD_SEQUENCE_ASK);
|
||||
UniquePtr<FileOp> fop(fop_to_load_document(m_filename.c_str(), FILE_LOAD_SEQUENCE_ASK));
|
||||
bool unrecent = false;
|
||||
|
||||
if (fop) {
|
||||
if (fop->has_error()) {
|
||||
console.printf(fop->error.c_str());
|
||||
fop_free(fop);
|
||||
|
||||
unrecent = true;
|
||||
}
|
||||
else {
|
||||
base::thread thread(&openfile_bg, fop);
|
||||
OpenFileData* data = new OpenFileData;
|
||||
|
||||
data->fop = fop;
|
||||
data->progress = app_get_statusbar()->addProgress();
|
||||
data->alert_window = ui::Alert::create(PACKAGE
|
||||
"<<Loading file:<<%s||&Cancel",
|
||||
get_filename(m_filename.c_str()));
|
||||
|
||||
// Add a monitor to check the loading (FileOp) progress
|
||||
data->monitor = add_gui_monitor(monitor_openfile_bg,
|
||||
monitor_free, data);
|
||||
|
||||
data->alert_window->open_window_fg();
|
||||
|
||||
if (data->monitor != NULL)
|
||||
remove_gui_monitor(data->monitor);
|
||||
|
||||
// Stop the file-operation and wait the thread to exit
|
||||
fop_stop(data->fop);
|
||||
thread.join();
|
||||
OpenFileJob task(fop, get_filename(m_filename.c_str()));
|
||||
task.showProgressWindow();
|
||||
|
||||
// Post-load processing, it is called from the GUI because may require user intervention.
|
||||
fop_post_load(fop);
|
||||
@ -198,10 +153,6 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
}
|
||||
else if (!fop_is_stop(fop))
|
||||
unrecent = true;
|
||||
|
||||
delete data->progress;
|
||||
fop_free(fop);
|
||||
delete data;
|
||||
}
|
||||
|
||||
// The file was not found or was loaded loaded with errors,
|
||||
|
@ -263,7 +263,7 @@ PaletteEntryEditor::PaletteEntryEditor()
|
||||
, m_redrawAll(false)
|
||||
, m_graftChange(false)
|
||||
, m_selfPalChange(false)
|
||||
, m_redrawTimer(this, 250)
|
||||
, m_redrawTimer(250, this)
|
||||
{
|
||||
m_topBox.setBorder(gfx::Border(0));
|
||||
m_topBox.child_spacing = 0;
|
||||
|
@ -18,114 +18,73 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "app.h"
|
||||
#include "app/file_selector.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/thread.h"
|
||||
#include "base/unique_ptr.h"
|
||||
#include "commands/command.h"
|
||||
#include "console.h"
|
||||
#include "document_wrappers.h"
|
||||
#include "file/file.h"
|
||||
#include "job.h"
|
||||
#include "modules/gui.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "recent_files.h"
|
||||
#include "ui/gui.h"
|
||||
#include "widgets/status_bar.h"
|
||||
|
||||
struct SaveFileData
|
||||
#include <allegro.h>
|
||||
|
||||
static const int kMonitoringPeriod = 100;
|
||||
|
||||
class SaveFileJob : public Job, public IFileOpProgress
|
||||
{
|
||||
Monitor *monitor;
|
||||
FileOp *fop;
|
||||
Progress *progress;
|
||||
ui::AlertPtr alert_window;
|
||||
public:
|
||||
SaveFileJob(FileOp* fop, const char* filename)
|
||||
: Job("Saving file")
|
||||
, m_fop(fop)
|
||||
{
|
||||
}
|
||||
|
||||
void showProgressWindow() {
|
||||
startJob();
|
||||
fop_stop(m_fop);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Thread to do the hard work: save the file to the disk.
|
||||
virtual void onJob() OVERRIDE {
|
||||
try {
|
||||
fop_operate(m_fop, this);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
fop_error(m_fop, "Error saving file:\n%s", e.what());
|
||||
}
|
||||
fop_done(m_fop);
|
||||
}
|
||||
|
||||
virtual void ackFileOpProgress(double progress) OVERRIDE {
|
||||
jobProgress(progress);
|
||||
}
|
||||
|
||||
FileOp* m_fop;
|
||||
};
|
||||
|
||||
/**
|
||||
* Thread to do the hard work: save the file to the disk.
|
||||
*
|
||||
* [saving thread]
|
||||
*/
|
||||
static void savefile_bg(void *fop_data)
|
||||
{
|
||||
FileOp *fop = (FileOp *)fop_data;
|
||||
try {
|
||||
fop_operate(fop);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
fop_error(fop, "Error saving file:\n%s", e.what());
|
||||
}
|
||||
fop_done(fop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the gui-monitor (a timer in the gui module that is called
|
||||
* every 100 milliseconds).
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
static void monitor_savefile_bg(void *_data)
|
||||
{
|
||||
SaveFileData *data = (SaveFileData *)_data;
|
||||
FileOp *fop = (FileOp *)data->fop;
|
||||
|
||||
if (data->progress)
|
||||
data->progress->setPos(fop_get_progress(fop));
|
||||
|
||||
if (fop_is_done(fop))
|
||||
remove_gui_monitor(data->monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the monitor is destroyed.
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
static void monitor_free(void *_data)
|
||||
{
|
||||
SaveFileData *data = (SaveFileData*)_data;
|
||||
|
||||
if (data->alert_window != NULL) {
|
||||
data->monitor = NULL;
|
||||
data->alert_window->closeWindow(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void save_document_in_background(Document* document, bool mark_as_saved)
|
||||
{
|
||||
FileOp *fop = fop_to_save_document(document);
|
||||
UniquePtr<FileOp> fop(fop_to_save_document(document));
|
||||
if (!fop)
|
||||
return;
|
||||
|
||||
base::thread thread(&savefile_bg, fop);
|
||||
SaveFileData* data = new SaveFileData;
|
||||
SaveFileJob job(fop, get_filename(document->getFilename()));
|
||||
job.showProgressWindow();
|
||||
|
||||
data->fop = fop;
|
||||
data->progress = app_get_statusbar()->addProgress();
|
||||
data->alert_window = ui::Alert::create(PACKAGE
|
||||
"<<Saving file:<<%s||&Cancel",
|
||||
get_filename(document->getFilename()));
|
||||
|
||||
/* add a monitor to check the saving (FileOp) progress */
|
||||
data->monitor = add_gui_monitor(monitor_savefile_bg,
|
||||
monitor_free, data);
|
||||
|
||||
/* TODO error handling */
|
||||
|
||||
data->alert_window->open_window_fg();
|
||||
|
||||
if (data->monitor != NULL)
|
||||
remove_gui_monitor(data->monitor);
|
||||
|
||||
/* wait the `savefile_bg' thread */
|
||||
thread.join();
|
||||
|
||||
/* show any error */
|
||||
if (fop->has_error()) {
|
||||
Console console;
|
||||
console.printf(fop->error.c_str());
|
||||
}
|
||||
/* no error? */
|
||||
else {
|
||||
App::instance()->getRecentFiles()->addRecentFile(document->getFilename());
|
||||
if (mark_as_saved)
|
||||
@ -135,10 +94,6 @@ static void save_document_in_background(Document* document, bool mark_as_saved)
|
||||
->setStatusText(2000, "File %s, saved.",
|
||||
get_filename(document->getFilename()));
|
||||
}
|
||||
|
||||
delete data->progress;
|
||||
fop_free(fop);
|
||||
delete data;
|
||||
}
|
||||
|
||||
/*********************************************************************/
|
||||
|
@ -32,7 +32,7 @@ using namespace ui;
|
||||
FilterPreview::FilterPreview(FilterManagerImpl* filterMgr)
|
||||
: Widget(JI_WIDGET)
|
||||
, m_filterMgr(filterMgr)
|
||||
, m_timer(this, 1)
|
||||
, m_timer(1, this)
|
||||
{
|
||||
setVisible(false);
|
||||
}
|
||||
|
@ -36,6 +36,8 @@
|
||||
|
||||
using namespace ui;
|
||||
|
||||
static const int kMonitoringPeriod = 100;
|
||||
|
||||
// Applies filters in two threads: a background worker thread to
|
||||
// modify the sprite, and the main thread to monitoring the progress
|
||||
// (and given to the user the possibility to cancel the process).
|
||||
@ -54,30 +56,26 @@ public:
|
||||
|
||||
private:
|
||||
void applyFilterInBackground();
|
||||
void monitor();
|
||||
void onMonitoringTick();
|
||||
|
||||
static void thread_proxy(void* data) {
|
||||
FilterWorker* filterWorker = (FilterWorker*)data;
|
||||
filterWorker->applyFilterInBackground();
|
||||
}
|
||||
|
||||
static void monitor_proxy(void* data) {
|
||||
FilterWorker* filterWorker = (FilterWorker*)data;
|
||||
filterWorker->monitor();
|
||||
}
|
||||
|
||||
FilterManagerImpl* m_filterMgr; // Effect to be applied.
|
||||
Mutex m_mutex; // Mutex to access to 'pos', 'done' and 'cancelled' fields in different threads.
|
||||
float m_pos; // Current progress position
|
||||
bool m_done : 1; // Was the effect completelly applied?
|
||||
bool m_cancelled : 1; // Was the effect cancelled by the user?
|
||||
Monitor* m_monitor; // Monitor to update the progress-bar
|
||||
ui::Timer m_timer; // Monitoring timer to update the progress-bar
|
||||
Progress* m_progressBar; // The progress-bar.
|
||||
AlertPtr m_alertWindow; // Alert for the user to cancel the filter-progress if he wants.
|
||||
};
|
||||
|
||||
FilterWorker::FilterWorker(FilterManagerImpl* filterMgr)
|
||||
: m_filterMgr(filterMgr)
|
||||
, m_timer(kMonitoringPeriod)
|
||||
{
|
||||
m_filterMgr->setProgressDelegate(this);
|
||||
|
||||
@ -90,7 +88,8 @@ FilterWorker::FilterWorker(FilterManagerImpl* filterMgr)
|
||||
m_alertWindow = ui::Alert::create(PACKAGE
|
||||
"<<Applying effect...||&Cancel");
|
||||
|
||||
m_monitor = add_gui_monitor(FilterWorker::monitor_proxy, NULL, this);
|
||||
m_timer.Tick.connect(&FilterWorker::onMonitoringTick, this);
|
||||
m_timer.start();
|
||||
}
|
||||
|
||||
FilterWorker::~FilterWorker()
|
||||
@ -109,8 +108,8 @@ void FilterWorker::run()
|
||||
// Open the alert window in foreground (this is modal, locks the main thread)
|
||||
m_alertWindow->open_window_fg();
|
||||
|
||||
// Remove the monitor
|
||||
remove_gui_monitor(m_monitor);
|
||||
// Stop the monitoring timer.
|
||||
m_timer.stop();
|
||||
|
||||
{
|
||||
ScopedLock lock(m_mutex);
|
||||
@ -162,10 +161,7 @@ void FilterWorker::applyFilterInBackground()
|
||||
|
||||
// Called by the GUI monitor (a timer in the gui module that is called
|
||||
// every 100 milliseconds).
|
||||
//
|
||||
// [main thread]
|
||||
//
|
||||
void FilterWorker::monitor()
|
||||
void FilterWorker::onMonitoringTick()
|
||||
{
|
||||
ScopedLock lock(m_mutex);
|
||||
|
||||
|
@ -91,7 +91,7 @@ Document* load_document(const char* filename)
|
||||
return NULL;
|
||||
|
||||
/* operate in this same thread */
|
||||
fop_operate(fop);
|
||||
fop_operate(fop, NULL);
|
||||
fop_done(fop);
|
||||
|
||||
fop_post_load(fop);
|
||||
@ -115,7 +115,7 @@ int save_document(Document* document)
|
||||
return -1;
|
||||
|
||||
/* operate in this same thread */
|
||||
fop_operate(fop);
|
||||
fop_operate(fop, NULL);
|
||||
fop_done(fop);
|
||||
|
||||
if (fop->has_error()) {
|
||||
@ -395,11 +395,13 @@ FileOp* fop_to_save_document(Document* document)
|
||||
//
|
||||
// After this function you must to mark the "fop" as "done" calling
|
||||
// fop_done() function.
|
||||
void fop_operate(FileOp *fop)
|
||||
void fop_operate(FileOp *fop, IFileOpProgress* progress)
|
||||
{
|
||||
ASSERT(fop != NULL);
|
||||
ASSERT(!fop_is_done(fop));
|
||||
|
||||
fop->progressInterface = progress;
|
||||
|
||||
// Load //////////////////////////////////////////////////////////////////////
|
||||
if (fop->type == FileOpLoad &&
|
||||
fop->format != NULL &&
|
||||
@ -441,7 +443,7 @@ void fop_operate(FileOp *fop)
|
||||
|
||||
fop->seq.has_alpha = false;
|
||||
fop->seq.progress_offset = 0.0f;
|
||||
fop->seq.progress_fraction = 1.0f / (float)frames;
|
||||
fop->seq.progress_fraction = 1.0f / (double)frames;
|
||||
|
||||
std::vector<std::string>::iterator it = fop->seq.filename_list.begin();
|
||||
std::vector<std::string>::iterator end = fop->seq.filename_list.end();
|
||||
@ -549,7 +551,7 @@ void fop_operate(FileOp *fop)
|
||||
int old_frame = sprite->getCurrentFrame();
|
||||
|
||||
fop->seq.progress_offset = 0.0f;
|
||||
fop->seq.progress_fraction = 1.0f / (float)sprite->getTotalFrames();
|
||||
fop->seq.progress_fraction = 1.0f / (double)sprite->getTotalFrames();
|
||||
|
||||
// For each frame in the sprite.
|
||||
for (int frame=0; frame < sprite->getTotalFrames(); ++frame) {
|
||||
@ -612,17 +614,17 @@ void fop_stop(FileOp *fop)
|
||||
fop->stop = true;
|
||||
}
|
||||
|
||||
FileOp::~FileOp()
|
||||
{
|
||||
if (this->format)
|
||||
this->format->destroyData(this);
|
||||
|
||||
delete this->seq.palette;
|
||||
delete this->mutex;
|
||||
}
|
||||
|
||||
void fop_free(FileOp *fop)
|
||||
{
|
||||
if (fop->format)
|
||||
fop->format->destroyData(fop);
|
||||
|
||||
if (fop->seq.palette != NULL)
|
||||
delete fop->seq.palette;
|
||||
|
||||
if (fop->mutex)
|
||||
delete fop->mutex;
|
||||
|
||||
delete fop;
|
||||
}
|
||||
|
||||
@ -746,10 +748,8 @@ void fop_error(FileOp *fop, const char *format, ...)
|
||||
}
|
||||
}
|
||||
|
||||
void fop_progress(FileOp *fop, float progress)
|
||||
void fop_progress(FileOp *fop, double progress)
|
||||
{
|
||||
//rest(8);
|
||||
|
||||
ScopedLock lock(*fop->mutex);
|
||||
|
||||
if (fop->is_sequence()) {
|
||||
@ -760,11 +760,14 @@ void fop_progress(FileOp *fop, float progress)
|
||||
else {
|
||||
fop->progress = progress;
|
||||
}
|
||||
|
||||
if (fop->progressInterface)
|
||||
fop->progressInterface->ackFileOpProgress(progress);
|
||||
}
|
||||
|
||||
float fop_get_progress(FileOp *fop)
|
||||
double fop_get_progress(FileOp *fop)
|
||||
{
|
||||
float progress;
|
||||
double progress;
|
||||
{
|
||||
ScopedLock lock(*fop->mutex);
|
||||
progress = fop->progress;
|
||||
@ -805,6 +808,7 @@ static FileOp* fop_new(FileOpType type)
|
||||
|
||||
fop->mutex = new Mutex();
|
||||
fop->progress = 0.0f;
|
||||
fop->progressInterface = NULL;
|
||||
fop->done = false;
|
||||
fop->stop = false;
|
||||
fop->oneframe = false;
|
||||
|
@ -46,6 +46,13 @@ class FormatOptions;
|
||||
typedef enum { FileOpLoad,
|
||||
FileOpSave } FileOpType;
|
||||
|
||||
class IFileOpProgress
|
||||
{
|
||||
public:
|
||||
virtual ~IFileOpProgress() { }
|
||||
virtual void ackFileOpProgress(double progress) = 0;
|
||||
};
|
||||
|
||||
// Structure to load & save files.
|
||||
struct FileOp
|
||||
{
|
||||
@ -57,7 +64,8 @@ struct FileOp
|
||||
|
||||
// Shared fields between threads.
|
||||
Mutex* mutex; // Mutex to access to the next two fields.
|
||||
float progress; // Progress (1.0 is ready).
|
||||
double progress; // Progress (1.0 is ready).
|
||||
IFileOpProgress* progressInterface;
|
||||
std::string error; // Error string.
|
||||
bool done : 1; // True if the operation finished.
|
||||
bool stop : 1; // Force the break of the operation.
|
||||
@ -71,8 +79,8 @@ struct FileOp
|
||||
Palette* palette; // Palette of the sequence.
|
||||
Image* image; // Image to be saved/loaded.
|
||||
// For the progress bar.
|
||||
float progress_offset; // Progress offset from the current frame.
|
||||
float progress_fraction; // Progress fraction for one frame.
|
||||
double progress_offset; // Progress offset from the current frame.
|
||||
double progress_fraction; // Progress fraction for one frame.
|
||||
// To load sequences.
|
||||
int frame;
|
||||
bool has_alpha;
|
||||
@ -81,6 +89,8 @@ struct FileOp
|
||||
SharedPtr<FormatOptions> format_options;
|
||||
} seq;
|
||||
|
||||
~FileOp();
|
||||
|
||||
bool has_error() const {
|
||||
return !this->error.empty();
|
||||
}
|
||||
@ -105,7 +115,7 @@ int save_document(Document* document);
|
||||
|
||||
FileOp* fop_to_load_document(const char* filename, int flags);
|
||||
FileOp* fop_to_save_document(Document* document);
|
||||
void fop_operate(FileOp* fop);
|
||||
void fop_operate(FileOp* fop, IFileOpProgress* progress);
|
||||
void fop_done(FileOp* fop);
|
||||
void fop_stop(FileOp* fop);
|
||||
void fop_free(FileOp* fop);
|
||||
@ -119,9 +129,9 @@ void fop_sequence_get_color(FileOp* fop, int index, int *r, int *g, int *b);
|
||||
Image* fop_sequence_image(FileOp* fi, PixelFormat pixelFormat, int w, int h);
|
||||
|
||||
void fop_error(FileOp* fop, const char *error, ...);
|
||||
void fop_progress(FileOp* fop, float progress);
|
||||
void fop_progress(FileOp* fop, double progress);
|
||||
|
||||
float fop_get_progress(FileOp* fop);
|
||||
double fop_get_progress(FileOp* fop);
|
||||
bool fop_is_done(FileOp* fop);
|
||||
bool fop_is_stop(FileOp* fop);
|
||||
|
||||
|
@ -309,8 +309,8 @@ bool PngFormat::onLoad(FileOp* fop)
|
||||
}
|
||||
|
||||
fop_progress(fop,
|
||||
(float)((float)pass + (float)(y+1) / (float)(height))
|
||||
/ (float)number_passes);
|
||||
(double)((double)pass + (double)(y+1) / (double)(height))
|
||||
/ (double)number_passes);
|
||||
|
||||
if (fop_is_stop(fop))
|
||||
break;
|
||||
@ -513,8 +513,8 @@ bool PngFormat::onSave(FileOp* fop)
|
||||
png_write_rows(png_ptr, &row_pointer, 1);
|
||||
|
||||
fop_progress(fop,
|
||||
(float)((float)pass + (float)(y+1) / (float)(height))
|
||||
/ (float)number_passes);
|
||||
(double)((double)pass + (double)(y+1) / (double)(height))
|
||||
/ (double)number_passes);
|
||||
}
|
||||
}
|
||||
|
||||
|
73
src/job.cpp
73
src/job.cpp
@ -23,32 +23,36 @@
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/thread.h"
|
||||
#include "job.h"
|
||||
#include "modules/gui.h"
|
||||
#include "ui/alert.h"
|
||||
#include "ui/frame.h"
|
||||
#include "ui/widget.h"
|
||||
#include "widgets/status_bar.h"
|
||||
|
||||
static const int kMonitoringPeriod = 100;
|
||||
|
||||
Job::Job(const char* job_name)
|
||||
{
|
||||
m_mutex = NULL;
|
||||
m_thread = NULL;
|
||||
m_progress = NULL;
|
||||
m_monitor = NULL;
|
||||
m_last_progress = 0.0f;
|
||||
m_last_progress = 0.0;
|
||||
m_done_flag = false;
|
||||
m_canceled_flag = false;
|
||||
|
||||
m_mutex = new Mutex();
|
||||
m_progress = app_get_statusbar()->addProgress();
|
||||
m_monitor = add_gui_monitor(&Job::monitor_proc,
|
||||
&Job::monitor_free,
|
||||
(void*)this);
|
||||
m_alert_window = ui::Alert::create("%s<<Working...||&Cancel", job_name);
|
||||
|
||||
m_timer.reset(new ui::Timer(kMonitoringPeriod, m_alert_window));
|
||||
m_timer->Tick.connect(&Job::onMonitoringTick, this);
|
||||
m_timer->start();
|
||||
}
|
||||
|
||||
Job::~Job()
|
||||
{
|
||||
if (m_alert_window != NULL)
|
||||
m_alert_window->closeWindow(NULL);
|
||||
|
||||
// The job was canceled by the user?
|
||||
{
|
||||
ScopedLock hold(*m_mutex);
|
||||
@ -56,10 +60,8 @@ Job::~Job()
|
||||
m_canceled_flag = true;
|
||||
}
|
||||
|
||||
if (m_monitor) {
|
||||
remove_gui_monitor(m_monitor);
|
||||
m_monitor = NULL;
|
||||
}
|
||||
if (m_timer->isRunning())
|
||||
m_timer->stop();
|
||||
|
||||
if (m_thread) {
|
||||
m_thread->join();
|
||||
@ -79,7 +81,7 @@ void Job::startJob()
|
||||
m_alert_window->open_window_fg();
|
||||
}
|
||||
|
||||
void Job::jobProgress(float f)
|
||||
void Job::jobProgress(double f)
|
||||
{
|
||||
ScopedLock hold(*m_mutex);
|
||||
m_last_progress = f;
|
||||
@ -91,12 +93,7 @@ bool Job::isCanceled()
|
||||
return m_canceled_flag;
|
||||
}
|
||||
|
||||
void Job::onJob()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void Job::onMonitorTick()
|
||||
void Job::onMonitoringTick()
|
||||
{
|
||||
ScopedLock hold(*m_mutex);
|
||||
|
||||
@ -104,14 +101,8 @@ void Job::onMonitorTick()
|
||||
m_progress->setPos(m_last_progress);
|
||||
|
||||
// is job done? we can close the monitor
|
||||
if (m_done_flag)
|
||||
remove_gui_monitor(m_monitor);
|
||||
}
|
||||
|
||||
void Job::onMonitorDestroyed()
|
||||
{
|
||||
if (m_alert_window != NULL) {
|
||||
m_monitor = NULL;
|
||||
if (m_done_flag || m_canceled_flag) {
|
||||
m_timer->stop();
|
||||
m_alert_window->closeWindow(NULL);
|
||||
}
|
||||
}
|
||||
@ -122,14 +113,7 @@ void Job::done()
|
||||
m_done_flag = true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Static methods
|
||||
|
||||
/**
|
||||
* Called to start the worker thread.
|
||||
*
|
||||
* [worker thread]
|
||||
*/
|
||||
// Called to start the worker thread.
|
||||
void Job::thread_proc(Job* self)
|
||||
{
|
||||
try {
|
||||
@ -138,28 +122,5 @@ void Job::thread_proc(Job* self)
|
||||
catch (...) {
|
||||
// TODO handle this exception
|
||||
}
|
||||
|
||||
self->done();
|
||||
}
|
||||
|
||||
/**
|
||||
* Procedure called from the GUI loop to monitoring each 100 milliseconds.
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
void Job::monitor_proc(void* data)
|
||||
{
|
||||
Job* self = (Job*)data;
|
||||
self->onMonitorTick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when the GUI monitor is deleted.
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
void Job::monitor_free(void* data)
|
||||
{
|
||||
Job* self = (Job*)data;
|
||||
self->onMonitorDestroyed();
|
||||
}
|
||||
|
16
src/job.h
16
src/job.h
@ -19,7 +19,9 @@
|
||||
#ifndef JOB_H_INCLUDED
|
||||
#define JOB_H_INCLUDED
|
||||
|
||||
#include "base/unique_ptr.h"
|
||||
#include "ui/alert.h"
|
||||
#include "ui/timer.h"
|
||||
|
||||
namespace base { class thread; }
|
||||
|
||||
@ -39,7 +41,7 @@ public:
|
||||
|
||||
// The onJob() can use this function to report progress of the
|
||||
// background job being done. 1.0 is completed.
|
||||
void jobProgress(float f);
|
||||
void jobProgress(double f);
|
||||
|
||||
// Returns true if the job was canceled by the user (in case he
|
||||
// pressed a "Cancel" button in the GUI). The onJob() thread should
|
||||
@ -51,15 +53,11 @@ protected:
|
||||
// This member function is called from another dedicated thread
|
||||
// outside the GUI one, so you can do some image processing here.
|
||||
// Remember that you cannot use any GUI element in this handler.
|
||||
virtual void onJob();
|
||||
virtual void onJob() = 0;
|
||||
|
||||
// Called each 1000 msecs by the GUI queue processing.
|
||||
// It is executed from the main GUI thread.
|
||||
virtual void onMonitorTick();
|
||||
|
||||
// Called when the monitor is destroyed. It is executed from the
|
||||
// main GUI thread.
|
||||
virtual void onMonitorDestroyed();
|
||||
virtual void onMonitoringTick();
|
||||
|
||||
private:
|
||||
void done();
|
||||
@ -69,11 +67,11 @@ private:
|
||||
static void monitor_free(void* data);
|
||||
|
||||
base::thread* m_thread;
|
||||
Monitor* m_monitor;
|
||||
UniquePtr<ui::Timer> m_timer;
|
||||
Progress* m_progress;
|
||||
Mutex* m_mutex;
|
||||
ui::AlertPtr m_alert_window;
|
||||
float m_last_progress;
|
||||
double m_last_progress;
|
||||
bool m_done_flag;
|
||||
bool m_canceled_flag;
|
||||
|
||||
|
@ -61,8 +61,6 @@
|
||||
#define REFRESH_FULL_SCREEN 1
|
||||
#define SYSTEM_WINDOW_RESIZE 2
|
||||
|
||||
#define MONITOR_TIMER_MSECS 100
|
||||
|
||||
#define SPRITEDITOR_ACTION_COPYSELECTION "CopySelection"
|
||||
#define SPRITEDITOR_ACTION_SNAPTOGRID "SnapToGrid"
|
||||
#define SPRITEDITOR_ACTION_ANGLESNAP "AngleSnap"
|
||||
@ -123,22 +121,6 @@ static Shortcut* get_keyboard_shortcut_for_spriteeditor(const char* action_name)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct Monitor
|
||||
{
|
||||
// returns true when the job is done and the monitor can be removed
|
||||
void (*proc)(void *);
|
||||
void (*free)(void *);
|
||||
void *data;
|
||||
bool lock;
|
||||
bool deleted;
|
||||
|
||||
Monitor(void (*proc)(void *),
|
||||
void (*free)(void *), void *data);
|
||||
~Monitor();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CustomizedGuiManager : public Manager
|
||||
{
|
||||
protected:
|
||||
@ -148,8 +130,6 @@ protected:
|
||||
static CustomizedGuiManager* manager = NULL;
|
||||
static Theme* ase_theme = NULL;
|
||||
|
||||
static UniquePtr<Timer> monitor_timer;
|
||||
static MonitorList* monitors = NULL;
|
||||
static std::vector<Shortcut*>* shortcuts = NULL;
|
||||
|
||||
static bool ji_screen_created = false;
|
||||
@ -201,7 +181,6 @@ int init_module_gui()
|
||||
bool fullscreen;
|
||||
bool maximized;
|
||||
|
||||
monitors = new MonitorList;
|
||||
shortcuts = new std::vector<Shortcut*>;
|
||||
|
||||
// Install the mouse
|
||||
@ -346,18 +325,6 @@ void exit_module_gui()
|
||||
delete shortcuts;
|
||||
shortcuts = NULL;
|
||||
|
||||
// destroy monitors
|
||||
monitor_timer.reset(NULL);
|
||||
|
||||
ASSERT(monitors != NULL);
|
||||
for (MonitorList::iterator
|
||||
it2 = monitors->begin(); it2 != monitors->end(); ++it2) {
|
||||
Monitor* monitor = *it2;
|
||||
delete monitor;
|
||||
}
|
||||
delete monitors;
|
||||
monitors = NULL;
|
||||
|
||||
if (double_buffering) {
|
||||
BITMAP *old_bmp = ji_screen;
|
||||
ji_set_screen(screen, SCREEN_W, SCREEN_H);
|
||||
@ -377,22 +344,6 @@ void exit_module_gui()
|
||||
remove_mouse();
|
||||
}
|
||||
|
||||
Monitor::Monitor(void (*proc)(void *),
|
||||
void (*free)(void *), void *data)
|
||||
{
|
||||
this->proc = proc;
|
||||
this->free = free;
|
||||
this->data = data;
|
||||
this->lock = false;
|
||||
this->deleted = false;
|
||||
}
|
||||
|
||||
Monitor::~Monitor()
|
||||
{
|
||||
if (this->free)
|
||||
(*this->free)(this->data);
|
||||
}
|
||||
|
||||
static void load_gui_config(int& w, int& h, int& bpp, bool& fullscreen, bool& maximized)
|
||||
{
|
||||
w = get_config_int("GfxMode", "Width", 0);
|
||||
@ -970,47 +921,6 @@ static Shortcut* get_keyboard_shortcut_for_spriteeditor(const char* action_name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Adds a routine to be called each 100 milliseconds to monitor
|
||||
// whatever you want. It's mainly used to monitor the progress of a
|
||||
// file-operation (see @ref fop_operate)
|
||||
Monitor* add_gui_monitor(void (*proc)(void *),
|
||||
void (*free)(void *), void *data)
|
||||
{
|
||||
Monitor* monitor = new Monitor(proc, free, data);
|
||||
|
||||
monitors->push_back(monitor);
|
||||
|
||||
if (monitor_timer == NULL)
|
||||
monitor_timer.reset(new Timer(manager, MONITOR_TIMER_MSECS));
|
||||
|
||||
monitor_timer->start();
|
||||
|
||||
return monitor;
|
||||
}
|
||||
|
||||
// Removes and frees a previously added monitor.
|
||||
void remove_gui_monitor(Monitor* monitor)
|
||||
{
|
||||
MonitorList::iterator it =
|
||||
std::find(monitors->begin(), monitors->end(), monitor);
|
||||
|
||||
ASSERT(it != monitors->end());
|
||||
|
||||
if (!monitor->lock)
|
||||
delete monitor;
|
||||
else
|
||||
monitor->deleted = true;
|
||||
|
||||
monitors->erase(it);
|
||||
if (monitors->empty())
|
||||
monitor_timer->stop();
|
||||
}
|
||||
|
||||
void* get_monitor_data(Monitor* monitor)
|
||||
{
|
||||
return monitor->data;
|
||||
}
|
||||
|
||||
// Manager event handler.
|
||||
bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
{
|
||||
@ -1031,32 +941,6 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
check_for_dropped_files();
|
||||
break;
|
||||
|
||||
case JM_TIMER:
|
||||
if (msg->timer.timer == monitor_timer) {
|
||||
for (MonitorList::iterator
|
||||
it = monitors->begin(), next; it != monitors->end(); it = next) {
|
||||
Monitor* monitor = *it;
|
||||
next = it;
|
||||
++next;
|
||||
|
||||
// is the monitor not lock?
|
||||
if (!monitor->lock) {
|
||||
// call the monitor procedure
|
||||
monitor->lock = true;
|
||||
(*monitor->proc)(monitor->data);
|
||||
monitor->lock = false;
|
||||
|
||||
if (monitor->deleted)
|
||||
delete monitor;
|
||||
}
|
||||
}
|
||||
|
||||
// is monitors empty? we can stop the timer so
|
||||
if (monitors->empty())
|
||||
monitor_timer->stop();
|
||||
}
|
||||
break;
|
||||
|
||||
case JM_KEYPRESSED: {
|
||||
Frame* toplevel_frame = getTopFrame();
|
||||
|
||||
|
@ -40,10 +40,6 @@ namespace ui {
|
||||
|
||||
namespace tools { class Tool; }
|
||||
|
||||
class Sprite;
|
||||
struct Monitor;
|
||||
typedef std::list<Monitor*> MonitorList;
|
||||
|
||||
int init_module_gui();
|
||||
void exit_module_gui();
|
||||
|
||||
@ -89,12 +85,4 @@ ui::JAccel get_accel_to_maintain_aspect_ratio();
|
||||
|
||||
tools::Tool* get_selected_quicktool(tools::Tool* currentTool);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Monitors
|
||||
|
||||
Monitor* add_gui_monitor(void (*proc)(void*),
|
||||
void (*free)(void*), void* data);
|
||||
void remove_gui_monitor(Monitor* monitor);
|
||||
void* get_monitor_data(Monitor* monitor);
|
||||
|
||||
#endif
|
||||
|
225
src/thumbnail_generator.cpp
Normal file
225
src/thumbnail_generator.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
/* ASEPRITE
|
||||
* Copyright (C) 2001-2012 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "thumbnail_generator.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "base/bind.h"
|
||||
#include "base/scoped_lock.h"
|
||||
#include "base/thread.h"
|
||||
#include "document.h"
|
||||
#include "file/file.h"
|
||||
#include "file_system.h"
|
||||
#include "raster/image.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/rotate.h"
|
||||
#include "raster/sprite.h"
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#define MAX_THUMBNAIL_SIZE 128
|
||||
|
||||
class ThumbnailGenerator::Worker
|
||||
{
|
||||
public:
|
||||
Worker(FileOp* fop, IFileItem* fileitem)
|
||||
: m_fop(fop)
|
||||
, m_fileitem(fileitem)
|
||||
, m_thumbnail(NULL)
|
||||
, m_palette(NULL)
|
||||
, m_thumbnailBitmap(NULL)
|
||||
, m_thread(Bind<void>(&Worker::loadBgThread, this)) {
|
||||
}
|
||||
|
||||
~Worker() {
|
||||
fop_stop(m_fop);
|
||||
m_thread.join();
|
||||
|
||||
fop_free(m_fop);
|
||||
}
|
||||
|
||||
IFileItem* getFileItem() { return m_fileitem; }
|
||||
bool isDone() const { return fop_is_done(m_fop); }
|
||||
double getProgress() const { return fop_get_progress(m_fop); }
|
||||
|
||||
private:
|
||||
void loadBgThread() {
|
||||
try {
|
||||
fop_operate(m_fop, NULL);
|
||||
|
||||
// Post load
|
||||
fop_post_load(m_fop);
|
||||
|
||||
// Convert the loaded document into the Allegro bitmap "m_thumbnail".
|
||||
const Sprite* sprite = (m_fop->document && m_fop->document->getSprite()) ? m_fop->document->getSprite():
|
||||
NULL;
|
||||
if (!fop_is_stop(m_fop) && sprite) {
|
||||
// The palette to convert the Image to a BITMAP
|
||||
m_palette.reset(new Palette(*sprite->getPalette(0)));
|
||||
|
||||
// Render the 'sprite' in one plain 'image'
|
||||
UniquePtr<Image> image(Image::create(sprite->getPixelFormat(),
|
||||
sprite->getWidth(),
|
||||
sprite->getHeight()));
|
||||
sprite->render(image, 0, 0);
|
||||
|
||||
// Calculate the thumbnail size
|
||||
int thumb_w = MAX_THUMBNAIL_SIZE * image->w / MAX(image->w, image->h);
|
||||
int thumb_h = MAX_THUMBNAIL_SIZE * image->h / MAX(image->w, image->h);
|
||||
if (MAX(thumb_w, thumb_h) > MAX(image->w, image->h)) {
|
||||
thumb_w = image->w;
|
||||
thumb_h = image->h;
|
||||
}
|
||||
thumb_w = MID(1, thumb_w, MAX_THUMBNAIL_SIZE);
|
||||
thumb_h = MID(1, thumb_h, MAX_THUMBNAIL_SIZE);
|
||||
|
||||
// Stretch the 'image'
|
||||
m_thumbnail.reset(Image::create(image->getPixelFormat(), thumb_w, thumb_h));
|
||||
image_clear(m_thumbnail, 0);
|
||||
image_scale(m_thumbnail, image, 0, 0, thumb_w, thumb_h);
|
||||
}
|
||||
|
||||
delete m_fop->document;
|
||||
|
||||
// Set the thumbnail of the file-item.
|
||||
if (m_thumbnail) {
|
||||
BITMAP* bmp = create_bitmap_ex(16, m_thumbnail->w, m_thumbnail->h);
|
||||
image_to_allegro(m_thumbnail, bmp, 0, 0, m_palette);
|
||||
m_fileitem->setThumbnail(bmp);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
fop_error(m_fop, "Error loading file:\n%s", e.what());
|
||||
}
|
||||
fop_done(m_fop);
|
||||
}
|
||||
|
||||
FileOp* m_fop;
|
||||
IFileItem* m_fileitem;
|
||||
UniquePtr<Image> m_thumbnail;
|
||||
BITMAP* m_thumbnailBitmap;
|
||||
UniquePtr<Palette> m_palette;
|
||||
base::thread m_thread;
|
||||
};
|
||||
|
||||
static void delete_singleton(ThumbnailGenerator* singleton)
|
||||
{
|
||||
delete singleton;
|
||||
}
|
||||
|
||||
ThumbnailGenerator* ThumbnailGenerator::instance()
|
||||
{
|
||||
static ThumbnailGenerator* singleton = NULL;
|
||||
if (singleton == NULL) {
|
||||
singleton = new ThumbnailGenerator();
|
||||
App::instance()->Exit.connect(Bind<void>(&delete_singleton, singleton));
|
||||
}
|
||||
return singleton;
|
||||
}
|
||||
|
||||
ThumbnailGenerator::WorkerStatus ThumbnailGenerator::getWorkerStatus(IFileItem* fileitem, double& progress)
|
||||
{
|
||||
ScopedLock hold(m_workersAccess);
|
||||
|
||||
for (WorkerList::iterator
|
||||
it=m_workers.begin(), end=m_workers.end(); it!=end; ++it) {
|
||||
Worker* worker = *it;
|
||||
if (worker->getFileItem() == fileitem) {
|
||||
if (worker->isDone())
|
||||
return ThumbnailIsDone;
|
||||
else {
|
||||
progress = worker->getProgress();
|
||||
return WorkingOnThumbnail;
|
||||
}
|
||||
}
|
||||
}
|
||||
return WithoutWorker;
|
||||
}
|
||||
|
||||
bool ThumbnailGenerator::checkWorkers()
|
||||
{
|
||||
ScopedLock hold(m_workersAccess);
|
||||
bool doingWork = !m_workers.empty();
|
||||
|
||||
for (WorkerList::iterator
|
||||
it=m_workers.begin(); it != m_workers.end(); ) {
|
||||
if ((*it)->isDone()) {
|
||||
delete *it;
|
||||
it = m_workers.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return doingWork;
|
||||
}
|
||||
|
||||
void ThumbnailGenerator::addWorkerToGenerateThumbnail(IFileItem* fileitem)
|
||||
{
|
||||
double progress;
|
||||
|
||||
if (fileitem->isBrowsable() ||
|
||||
fileitem->getThumbnail() != NULL ||
|
||||
getWorkerStatus(fileitem, progress) != WithoutWorker)
|
||||
return;
|
||||
|
||||
FileOp* fop = fop_to_load_document(fileitem->getFileName().c_str(),
|
||||
FILE_LOAD_SEQUENCE_NONE |
|
||||
FILE_LOAD_ONE_FRAME);
|
||||
if (!fop)
|
||||
return;
|
||||
|
||||
if (fop->has_error()) {
|
||||
fop_free(fop);
|
||||
}
|
||||
else {
|
||||
Worker* worker = new Worker(fop, fileitem);
|
||||
try {
|
||||
ScopedLock hold(m_workersAccess);
|
||||
m_workers.push_back(worker);
|
||||
}
|
||||
catch (...) {
|
||||
delete worker;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbnailGenerator::stopAllWorkers()
|
||||
{
|
||||
base::thread* ptr = new base::thread(Bind<void>(&ThumbnailGenerator::stopAllWorkersBackground, this));
|
||||
m_stopThread.reset(ptr);
|
||||
}
|
||||
|
||||
void ThumbnailGenerator::stopAllWorkersBackground()
|
||||
{
|
||||
WorkerList workersCopy;
|
||||
{
|
||||
ScopedLock hold(m_workersAccess);
|
||||
workersCopy = m_workers;
|
||||
m_workers.clear();
|
||||
}
|
||||
|
||||
for (WorkerList::iterator
|
||||
it=workersCopy.begin(), end=workersCopy.end(); it!=end; ++it) {
|
||||
delete *it;
|
||||
}
|
||||
}
|
67
src/thumbnail_generator.h
Normal file
67
src/thumbnail_generator.h
Normal file
@ -0,0 +1,67 @@
|
||||
/* ASEPRITE
|
||||
* Copyright (C) 2001-2012 David Capello
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef THUMBNAIL_GENERATOR_H_INCLUDED
|
||||
#define THUMBNAIL_GENERATOR_H_INCLUDED
|
||||
|
||||
#include "base/mutex.h"
|
||||
#include "base/unique_ptr.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class IFileItem;
|
||||
namespace base { class thread; }
|
||||
|
||||
class ThumbnailGenerator
|
||||
{
|
||||
public:
|
||||
enum WorkerStatus { WithoutWorker, WorkingOnThumbnail, ThumbnailIsDone };
|
||||
|
||||
static ThumbnailGenerator* instance();
|
||||
|
||||
// Generate a thumbnail for the given file-item. It must be called
|
||||
// from the GUI thread.
|
||||
void addWorkerToGenerateThumbnail(IFileItem* fileitem);
|
||||
|
||||
// Returns the status of the worker that is generating the thumbnail
|
||||
// for the given file.
|
||||
WorkerStatus getWorkerStatus(IFileItem* fileitem, double& progress);
|
||||
|
||||
// Checks the status of workers. If there are workers that already
|
||||
// done its job, we've to destroy them. This function must be called
|
||||
// from the GUI thread (because a thread is joint to it).
|
||||
// Returns true if there are workers generating thumbnails.
|
||||
bool checkWorkers();
|
||||
|
||||
// Stops all workers generating thumbnails. This is an non-blocking
|
||||
// operation. The cancelation of all workers is done in a background
|
||||
// thread.
|
||||
void stopAllWorkers();
|
||||
|
||||
private:
|
||||
void stopAllWorkersBackground();
|
||||
|
||||
class Worker;
|
||||
typedef std::vector<Worker*> WorkerList;
|
||||
|
||||
WorkerList m_workers;
|
||||
Mutex m_workersAccess;
|
||||
UniquePtr<base::thread> m_stopThread;
|
||||
};
|
||||
|
||||
#endif
|
@ -28,7 +28,7 @@ namespace ui {
|
||||
|
||||
Entry::Entry(size_t maxsize, const char *format, ...)
|
||||
: Widget(JI_ENTRY)
|
||||
, m_timer(this, 500)
|
||||
, m_timer(500, this)
|
||||
{
|
||||
char buf[4096];
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "ui/gui.h"
|
||||
#include "ui/intern.h"
|
||||
|
||||
#define TIMEOUT_TO_OPEN_SUBMENU 250
|
||||
static const int kTimeoutToOpenSubmenu = 250;
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
@ -1142,7 +1142,7 @@ void MenuItem::closeSubmenu(bool last_of_close_chain)
|
||||
void MenuItem::startTimer()
|
||||
{
|
||||
if (m_submenu_timer == NULL)
|
||||
m_submenu_timer.reset(new Timer(this, TIMEOUT_TO_OPEN_SUBMENU));
|
||||
m_submenu_timer.reset(new Timer(kTimeoutToOpenSubmenu, this));
|
||||
|
||||
m_submenu_timer->start();
|
||||
}
|
||||
|
@ -22,12 +22,12 @@ typedef std::vector<Timer*> Timers;
|
||||
|
||||
static Timers timers; // Registered timers
|
||||
|
||||
Timer::Timer(Widget* owner, int interval)
|
||||
: m_owner(owner)
|
||||
Timer::Timer(int interval, Widget* owner)
|
||||
: m_owner(owner ? owner: Manager::getDefault())
|
||||
, m_interval(interval)
|
||||
, m_lastTime(-1)
|
||||
{
|
||||
ASSERT_VALID_WIDGET(owner);
|
||||
ASSERT(m_owner != NULL);
|
||||
|
||||
timers.push_back(this);
|
||||
}
|
||||
@ -81,24 +81,27 @@ void Timer::pollTimers()
|
||||
int count;
|
||||
|
||||
for (int c=0; c<(int)timers.size(); ++c) {
|
||||
if (timers[c] && timers[c]->m_lastTime >= 0) {
|
||||
Timer* timer = timers[c];
|
||||
if (timer && timer->m_lastTime >= 0) {
|
||||
count = 0;
|
||||
while (t - timers[c]->m_lastTime > timers[c]->m_interval) {
|
||||
timers[c]->m_lastTime += timers[c]->m_interval;
|
||||
while (t - timer->m_lastTime > timer->m_interval) {
|
||||
timer->m_lastTime += timer->m_interval;
|
||||
++count;
|
||||
|
||||
/* we spend too much time here */
|
||||
if (ji_clock - t > timers[c]->m_interval) {
|
||||
timers[c]->m_lastTime = ji_clock;
|
||||
if (ji_clock - t > timer->m_interval) {
|
||||
timer->m_lastTime = ji_clock;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
ASSERT(timer->m_owner != NULL);
|
||||
|
||||
Message* msg = jmessage_new(JM_TIMER);
|
||||
msg->timer.count = count;
|
||||
msg->timer.timer = timers[c];
|
||||
jmessage_add_dest(msg, timers[c]->m_owner);
|
||||
msg->timer.timer = timer;
|
||||
jmessage_add_dest(msg, timer->m_owner);
|
||||
Manager::getDefault()->enqueueMessage(msg);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ namespace ui {
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
Timer(Widget* owner, int interval);
|
||||
Timer(int interval, Widget* owner = NULL);
|
||||
virtual ~Timer();
|
||||
|
||||
int getInterval() const;
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "ui/paint_event.h"
|
||||
#include "ui/preferred_size_event.h"
|
||||
|
||||
#define TOOLTIP_DELAY_MSECS 300
|
||||
static const int kTooltipDelayMsecs = 300;
|
||||
|
||||
using namespace gfx;
|
||||
|
||||
@ -59,7 +59,7 @@ bool TooltipManager::onProcessMessage(Message* msg)
|
||||
m_target.tipInfo = it->second;
|
||||
|
||||
if (m_timer == NULL) {
|
||||
m_timer.reset(new Timer(this, TOOLTIP_DELAY_MSECS));
|
||||
m_timer.reset(new Timer(kTooltipDelayMsecs, this));
|
||||
m_timer->Tick.connect(&TooltipManager::onTick, this);
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ Editor::Editor()
|
||||
: Widget(editor_type())
|
||||
, m_state(new StandbyState())
|
||||
, m_decorator(NULL)
|
||||
, m_mask_timer(this, 100)
|
||||
, m_mask_timer(100, this)
|
||||
, m_customizationDelegate(NULL)
|
||||
{
|
||||
// Add the first state into the history.
|
||||
|
@ -20,27 +20,13 @@
|
||||
|
||||
#include "widgets/file_list.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "base/thread.h"
|
||||
#include "commands/commands.h"
|
||||
#include "console.h"
|
||||
#include "document.h"
|
||||
#include "file/file.h"
|
||||
#include "modules/editors.h"
|
||||
#include "modules/gfx.h"
|
||||
#include "modules/gui.h"
|
||||
#include "modules/palettes.h"
|
||||
#include "raster/image.h"
|
||||
#include "raster/palette.h"
|
||||
#include "raster/rotate.h"
|
||||
#include "raster/sprite.h"
|
||||
#include "thumbnail_generator.h"
|
||||
#include "ui/gui.h"
|
||||
#include "widgets/status_bar.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <allegro.h>
|
||||
|
||||
#define MAX_THUMBNAIL_SIZE 128
|
||||
#define ISEARCH_KEYPRESS_INTERVAL_MSECS 500
|
||||
|
||||
using namespace gfx;
|
||||
@ -48,24 +34,10 @@ using namespace ui;
|
||||
|
||||
namespace widgets {
|
||||
|
||||
struct ThumbnailData
|
||||
{
|
||||
Monitor* monitor;
|
||||
FileOp* fop;
|
||||
IFileItem* fileitem;
|
||||
FileList* fileview;
|
||||
Image* thumbnail;
|
||||
base::thread* thread;
|
||||
Palette* palette;
|
||||
};
|
||||
|
||||
static void openfile_bg(ThumbnailData* data);
|
||||
static void monitor_thumbnail_generation(void *data);
|
||||
static void monitor_free_thumbnail_generation(void *data);
|
||||
|
||||
FileList::FileList()
|
||||
: Widget(JI_WIDGET)
|
||||
, m_timer(this, 200)
|
||||
, m_generateThumbnailTimer(200, this)
|
||||
, m_monitoringTimer(50, this)
|
||||
{
|
||||
setFocusStop(true);
|
||||
|
||||
@ -76,15 +48,21 @@ FileList::FileList()
|
||||
|
||||
m_itemToGenerateThumbnail = NULL;
|
||||
|
||||
m_generateThumbnailTimer.Tick.connect(&FileList::onGenerateThumbnailTick, this);
|
||||
m_monitoringTimer.Tick.connect(&FileList::onMonitoringTick, this);
|
||||
m_monitoringTimer.start();
|
||||
|
||||
regenerateList();
|
||||
}
|
||||
|
||||
FileList::~FileList()
|
||||
{
|
||||
stopThreads();
|
||||
// Stop timers.
|
||||
m_generateThumbnailTimer.stop();
|
||||
m_monitoringTimer.stop();
|
||||
|
||||
// at this point, can't be threads running in background
|
||||
ASSERT(m_monitors.empty());
|
||||
// Stop workers creating thumbnails.
|
||||
ThumbnailGenerator::instance()->stopAllWorkers();
|
||||
}
|
||||
|
||||
void FileList::setExtensions(const char* extensions)
|
||||
@ -166,8 +144,7 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
|
||||
// rows
|
||||
for (FileItemList::iterator
|
||||
it=m_list.begin();
|
||||
it!=m_list.end(); ++it) {
|
||||
it=m_list.begin(), end=m_list.end(); it!=end; ++it) {
|
||||
IFileItem* fi = *it;
|
||||
gfx::Size itemSize = getFileItemSize(fi);
|
||||
|
||||
@ -235,32 +212,18 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
/* fill with the background color */
|
||||
bgcolor);
|
||||
|
||||
// draw progress bar
|
||||
if (!m_monitors.empty()) {
|
||||
for (MonitorList::iterator
|
||||
it2 = m_monitors.begin();
|
||||
it2 != m_monitors.end(); ++it2) {
|
||||
Monitor* monitor = *it2;
|
||||
ThumbnailData* data = (ThumbnailData*)get_monitor_data(monitor);
|
||||
|
||||
// Check if this monitor is for this file-item
|
||||
if (data->fileitem == fi) {
|
||||
// If the file operation is not done, means that we are
|
||||
// still loading the file, so we can show a progress bar
|
||||
if (!fop_is_done(data->fop)) {
|
||||
float progress = fop_get_progress(data->fop);
|
||||
|
||||
draw_progress_bar(ji_screen,
|
||||
this->rc->x2-2-64, y+itemSize.h/2-3,
|
||||
this->rc->x2-2, y+itemSize.h/2+3,
|
||||
progress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// draw progress bars
|
||||
double progress;
|
||||
ThumbnailGenerator::WorkerStatus workerStatus =
|
||||
ThumbnailGenerator::instance()->getWorkerStatus(fi, progress);
|
||||
if (workerStatus == ThumbnailGenerator::WorkingOnThumbnail) {
|
||||
draw_progress_bar(ji_screen,
|
||||
this->rc->x2-2-64, y+itemSize.h/2-3,
|
||||
this->rc->x2-2, y+itemSize.h/2+3,
|
||||
progress);
|
||||
}
|
||||
|
||||
// thumbnail position
|
||||
// Thumbnail position
|
||||
if (fi == m_selected) {
|
||||
thumbnail = fi->getThumbnail();
|
||||
if (thumbnail)
|
||||
@ -277,7 +240,7 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
this->rc->x2-1, this->rc->y2-1,
|
||||
ji_color_background());
|
||||
|
||||
/* draw the thumbnail */
|
||||
// Draw the thumbnail
|
||||
if (thumbnail) {
|
||||
x = vp.x+vp.w-2-thumbnail->w;
|
||||
y = thumbnail_y-thumbnail->h/2;
|
||||
@ -466,18 +429,6 @@ bool FileList::onProcessMessage(Message* msg)
|
||||
}
|
||||
break;
|
||||
|
||||
case JM_TIMER:
|
||||
/* is time to generate the thumbnail? */
|
||||
if (msg->timer.timer == &m_timer) {
|
||||
IFileItem* fileitem;
|
||||
|
||||
m_timer.stop();
|
||||
|
||||
fileitem = m_itemToGenerateThumbnail;
|
||||
generateThumbnail(fileitem);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return Widget::onProcessMessage(msg);
|
||||
@ -498,6 +449,21 @@ void FileList::onCurrentFolderChanged()
|
||||
CurrentFolderChanged();
|
||||
}
|
||||
|
||||
void FileList::onMonitoringTick()
|
||||
{
|
||||
if (ThumbnailGenerator::instance()->checkWorkers())
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void FileList::onGenerateThumbnailTick()
|
||||
{
|
||||
m_generateThumbnailTimer.stop();
|
||||
|
||||
IFileItem* fileitem = m_itemToGenerateThumbnail;
|
||||
if (fileitem)
|
||||
ThumbnailGenerator::instance()->addWorkerToGenerateThumbnail(fileitem);
|
||||
}
|
||||
|
||||
gfx::Size FileList::getFileItemSize(IFileItem* fi) const
|
||||
{
|
||||
int len = 0;
|
||||
@ -600,199 +566,8 @@ void FileList::generatePreviewOfSelectedItem()
|
||||
!m_selected->getThumbnail())
|
||||
{
|
||||
m_itemToGenerateThumbnail = m_selected;
|
||||
m_timer.start();
|
||||
m_generateThumbnailTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it does some hard work like access to the disk.
|
||||
bool FileList::generateThumbnail(IFileItem* fileitem)
|
||||
{
|
||||
if (fileitem->isBrowsable() ||
|
||||
fileitem->getThumbnail() != NULL)
|
||||
return false;
|
||||
|
||||
FileOp* fop =
|
||||
fop_to_load_document(fileitem->getFileName().c_str(),
|
||||
FILE_LOAD_SEQUENCE_NONE |
|
||||
FILE_LOAD_ONE_FRAME);
|
||||
if (!fop)
|
||||
return true;
|
||||
|
||||
if (fop->has_error()) {
|
||||
fop_free(fop);
|
||||
}
|
||||
else {
|
||||
ThumbnailData* data = new ThumbnailData;
|
||||
|
||||
data->fop = fop;
|
||||
data->fileitem = fileitem;
|
||||
data->fileview = this;
|
||||
data->thumbnail = NULL;
|
||||
|
||||
data->thread = new base::thread(&openfile_bg, data);
|
||||
if (data->thread) {
|
||||
// add a monitor to check the loading (FileOp) progress
|
||||
data->monitor = add_gui_monitor(monitor_thumbnail_generation,
|
||||
monitor_free_thumbnail_generation, data);
|
||||
|
||||
m_monitors.push_back(data->monitor);
|
||||
invalidate();
|
||||
}
|
||||
else {
|
||||
fop_free(fop);
|
||||
delete data;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileList::stopThreads()
|
||||
{
|
||||
// stop the generation of threads
|
||||
m_timer.stop();
|
||||
|
||||
// join all threads (removing all monitors)
|
||||
for (MonitorList::iterator
|
||||
it = m_monitors.begin();
|
||||
it != m_monitors.end(); ) {
|
||||
Monitor* monitor = *it;
|
||||
++it;
|
||||
remove_gui_monitor(monitor);
|
||||
}
|
||||
|
||||
// clear the list of monitors
|
||||
m_monitors.clear();
|
||||
}
|
||||
|
||||
// Thread to do the hard work: load the file from the disk (in
|
||||
// background).
|
||||
//
|
||||
// [loading thread]
|
||||
static void openfile_bg(ThumbnailData* data)
|
||||
{
|
||||
FileOp* fop = (FileOp*)data->fop;
|
||||
|
||||
try {
|
||||
fop_operate(fop);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
fop_error(fop, "Error loading file:\n%s", e.what());
|
||||
}
|
||||
|
||||
fop_done(fop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the GUI-monitor (a timer in the gui module that is called
|
||||
* every 100 milliseconds).
|
||||
*
|
||||
* [main thread]
|
||||
*/
|
||||
static void monitor_thumbnail_generation(void *_data)
|
||||
{
|
||||
ThumbnailData* data = (ThumbnailData*)_data;
|
||||
FileOp* fop = data->fop;
|
||||
|
||||
/* is done? ...ok, now the thumbnail is in the main thread only... */
|
||||
if (fop_is_done(fop)) {
|
||||
// Post load
|
||||
fop_post_load(fop);
|
||||
|
||||
// Convert the loaded document into the Allegro bitmap "data->thumbnail".
|
||||
{
|
||||
int thumb_w, thumb_h;
|
||||
Image* image;
|
||||
|
||||
Sprite* sprite = (fop->document && fop->document->getSprite()) ? fop->document->getSprite():
|
||||
NULL;
|
||||
if (!fop_is_stop(fop) && sprite) {
|
||||
// The palette to convert the Image to a BITMAP
|
||||
data->palette = new Palette(*sprite->getPalette(0));
|
||||
|
||||
// Render the 'sprite' in one plain 'image'
|
||||
image = Image::create(sprite->getPixelFormat(), sprite->getWidth(), sprite->getHeight());
|
||||
sprite->render(image, 0, 0);
|
||||
|
||||
// Calculate the thumbnail size
|
||||
thumb_w = MAX_THUMBNAIL_SIZE * image->w / MAX(image->w, image->h);
|
||||
thumb_h = MAX_THUMBNAIL_SIZE * image->h / MAX(image->w, image->h);
|
||||
if (MAX(thumb_w, thumb_h) > MAX(image->w, image->h)) {
|
||||
thumb_w = image->w;
|
||||
thumb_h = image->h;
|
||||
}
|
||||
thumb_w = MID(1, thumb_w, MAX_THUMBNAIL_SIZE);
|
||||
thumb_h = MID(1, thumb_h, MAX_THUMBNAIL_SIZE);
|
||||
|
||||
// Stretch the 'image'
|
||||
data->thumbnail = Image::create(image->getPixelFormat(), thumb_w, thumb_h);
|
||||
image_clear(data->thumbnail, 0);
|
||||
image_scale(data->thumbnail, image, 0, 0, thumb_w, thumb_h);
|
||||
image_free(image);
|
||||
}
|
||||
|
||||
delete fop->document;
|
||||
}
|
||||
|
||||
/* set the thumbnail of the file-item */
|
||||
if (data->thumbnail) {
|
||||
BITMAP *bmp = create_bitmap_ex(16,
|
||||
data->thumbnail->w,
|
||||
data->thumbnail->h);
|
||||
|
||||
image_to_allegro(data->thumbnail, bmp, 0, 0, data->palette);
|
||||
|
||||
delete data->thumbnail; // image
|
||||
delete data->palette;
|
||||
data->thumbnail = NULL;
|
||||
data->palette = NULL;
|
||||
|
||||
data->fileitem->setThumbnail(bmp);
|
||||
|
||||
/* is the selected file-item the one that now has a thumbnail? */
|
||||
if (data->fileview->getSelectedFileItem() == data->fileitem) {
|
||||
/* we have to dirty the file-view to show the thumbnail */
|
||||
data->fileview->invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
remove_gui_monitor(data->monitor);
|
||||
}
|
||||
else {
|
||||
data->fileview->invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* [main thread]
|
||||
*/
|
||||
static void monitor_free_thumbnail_generation(void *_data)
|
||||
{
|
||||
ThumbnailData *data = (ThumbnailData*)_data;
|
||||
FileOp *fop = data->fop;
|
||||
|
||||
fop_stop(fop);
|
||||
data->thread->join();
|
||||
delete data->thread;
|
||||
|
||||
// Remove the monitor from the FileList.
|
||||
data->fileview->removeMonitor(data->monitor);
|
||||
|
||||
// Destroy the thumbnail
|
||||
if (data->thumbnail) {
|
||||
image_free(data->thumbnail);
|
||||
data->thumbnail = NULL;
|
||||
}
|
||||
|
||||
fop_free(fop);
|
||||
delete data;
|
||||
}
|
||||
|
||||
void FileList::removeMonitor(Monitor* monitor)
|
||||
{
|
||||
MonitorList::iterator it = std::find(m_monitors.begin(), m_monitors.end(), monitor);
|
||||
ASSERT(it != m_monitors.end());
|
||||
m_monitors.erase(it);
|
||||
}
|
||||
|
||||
} // namespace widgets
|
||||
|
@ -26,10 +26,6 @@
|
||||
#include "ui/timer.h"
|
||||
#include "ui/widget.h"
|
||||
|
||||
#include "modules/gui.h" // For monitors
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace widgets {
|
||||
|
||||
class FileList : public ui::Widget
|
||||
@ -48,8 +44,6 @@ namespace widgets {
|
||||
|
||||
void goUp();
|
||||
|
||||
void removeMonitor(Monitor* monitor);
|
||||
|
||||
Signal0<void> FileSelected;
|
||||
Signal0<void> FileAccepted;
|
||||
Signal0<void> CurrentFolderChanged;
|
||||
@ -61,14 +55,14 @@ namespace widgets {
|
||||
virtual void onCurrentFolderChanged();
|
||||
|
||||
private:
|
||||
void onGenerateThumbnailTick();
|
||||
void onMonitoringTick();
|
||||
gfx::Size getFileItemSize(IFileItem* fi) const;
|
||||
void makeSelectedFileitemVisible();
|
||||
void regenerateList();
|
||||
int getSelectedIndex();
|
||||
void selectIndex(int index);
|
||||
void generatePreviewOfSelectedItem();
|
||||
bool generateThumbnail(IFileItem* fileitem);
|
||||
void stopThreads();
|
||||
|
||||
IFileItem* m_currentFolder;
|
||||
FileItemList m_list;
|
||||
@ -81,10 +75,16 @@ namespace widgets {
|
||||
std::string m_isearch;
|
||||
int m_isearchClock;
|
||||
|
||||
/* thumbnail generation process */
|
||||
// Timer to start generating the thumbnail after an item is
|
||||
// selected.
|
||||
ui::Timer m_generateThumbnailTimer;
|
||||
|
||||
// Monitoring the progress of each thumbnail.
|
||||
ui::Timer m_monitoringTimer;
|
||||
|
||||
// Used keep the last-selected item in the list so we know
|
||||
// thumbnail to generate when the m_generateThumbnailTimer ticks.
|
||||
IFileItem* m_itemToGenerateThumbnail;
|
||||
ui::Timer m_timer;
|
||||
MonitorList m_monitors; // list of monitors watching threads
|
||||
|
||||
};
|
||||
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "file/file.h"
|
||||
#include "ini_file.h"
|
||||
#include "modules/gfx.h"
|
||||
#include "modules/gui.h"
|
||||
#include "recent_files.h"
|
||||
#include "skin/skin_parts.h"
|
||||
#include "ui/gui.h"
|
||||
|
@ -70,7 +70,7 @@ public:
|
||||
void setInterval(int msecs)
|
||||
{
|
||||
if (!m_timer)
|
||||
m_timer.reset(new ui::Timer(this, msecs));
|
||||
m_timer.reset(new ui::Timer(msecs, this));
|
||||
else
|
||||
m_timer->setInterval(msecs);
|
||||
}
|
||||
@ -450,7 +450,7 @@ Progress::~Progress()
|
||||
}
|
||||
}
|
||||
|
||||
void Progress::setPos(float pos)
|
||||
void Progress::setPos(double pos)
|
||||
{
|
||||
if (m_pos != pos) {
|
||||
m_pos = pos;
|
||||
@ -458,7 +458,7 @@ void Progress::setPos(float pos)
|
||||
}
|
||||
}
|
||||
|
||||
float Progress::getPos() const
|
||||
double Progress::getPos() const
|
||||
{
|
||||
return m_pos;
|
||||
}
|
||||
|
@ -50,14 +50,14 @@ class Progress
|
||||
|
||||
public:
|
||||
~Progress();
|
||||
void setPos(float pos);
|
||||
float getPos() const;
|
||||
void setPos(double pos);
|
||||
double getPos() const;
|
||||
|
||||
private:
|
||||
Progress();
|
||||
Progress(StatusBar* statusbar);
|
||||
StatusBar* m_statusbar;
|
||||
float m_pos;
|
||||
double m_pos;
|
||||
};
|
||||
|
||||
class StatusBarListener
|
||||
|
@ -70,7 +70,7 @@ private:
|
||||
Tabs::Tabs(TabsDelegate* delegate)
|
||||
: Widget(tabs_type())
|
||||
, m_delegate(delegate)
|
||||
, m_timer(this, 1000/60)
|
||||
, m_timer(1000/60, this)
|
||||
{
|
||||
m_hot = NULL;
|
||||
m_selected = NULL;
|
||||
|
@ -148,7 +148,7 @@ void toolbar_select_tool(Widget* toolbar, Tool* tool)
|
||||
|
||||
ToolBar::ToolBar()
|
||||
: Widget(JI_WIDGET)
|
||||
, m_tipTimer(this, 300)
|
||||
, m_tipTimer(300, this)
|
||||
{
|
||||
this->border_width.l = 1*jguiscale();
|
||||
this->border_width.t = 0;
|
||||
|
Loading…
x
Reference in New Issue
Block a user