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:
David Capello 2012-07-06 01:06:00 -03:00
parent 09ecf4c588
commit ebb8d0c5bd
30 changed files with 559 additions and 749 deletions

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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,

View File

@ -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;

View File

@ -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;
}
/*********************************************************************/

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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();

View File

@ -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
View 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
View 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

View File

@ -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];

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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.

View File

@ -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

View File

@ -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
};

View File

@ -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"

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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;