Added IAppHook interface for a cleaner event/hook interface.

This commit is contained in:
David Capello 2009-10-14 14:09:59 +00:00
parent b758b113e8
commit f2f78377ad
15 changed files with 251 additions and 212 deletions

View File

@ -1,3 +1,10 @@
2009-10-14 David Capello <davidcapello@gmail.com>
* src/core/app.h (IAppHook, AppEvent): Added IAppHook interface
for a cleaner event/hook interface.
* src/core/app.cpp (Option): Converted to a class.
2009-10-08 David Capello <davidcapello@gmail.com>
* src/modules/tools.cpp (tool_marker): Modified marquee tooltip.

View File

@ -65,7 +65,7 @@ void AboutCommand::execute(Context* context)
box1 = jbox_new(JI_VERTICAL);
label1 = jlabel_new("Allegro Sprite Editor - " VERSION);
label2 = jlabel_new(_("Just Another Tool to Create Sprites"));
label2 = jlabel_new(_("Just another tool to create sprites"));
separator1 = ji_separator_new(NULL, JI_HORIZONTAL);
if (authors_txt) {

View File

@ -40,6 +40,17 @@
#include "widgets/statebar.h"
#include "sprite_wrappers.h"
class FreeWidget : public IAppHook
{
JWidget m_widget;
public:
FreeWidget(JWidget widget) : m_widget(widget) { }
void on_event()
{
jwidget_free(m_widget);
}
};
static JWidget window = NULL;
static bool brush_preview_msg_proc(JWidget widget, JMessage msg);
@ -201,9 +212,7 @@ void ConfigureTools::execute(Context* context)
HOOK(cursor_color, SIGNAL_COLORBUTTON_CHANGE, cursor_button_change_hook, 0);
HOOK(check_onionskin, JI_SIGNAL_CHECK_CHANGE, onionskin_check_change_hook, 0);
app_add_hook(APP_EXIT,
reinterpret_cast<void(*)(void*)>(jwidget_free),
window);
App::instance()->add_hook(AppEvent::Exit, new FreeWidget(window));
}
/* default position */

View File

@ -72,6 +72,17 @@ bool ColorCurveCommand::enabled(Context* context)
sprite != NULL;
}
class DestroyCurve : public IAppHook
{
Curve* m_curve;
public:
DestroyCurve(Curve* curve) : m_curve(curve) { }
void on_event()
{
curve_free(m_curve);
}
};
void ColorCurveCommand::execute(Context* context)
{
const CurrentSpriteReader sprite(context);
@ -85,9 +96,7 @@ void ColorCurveCommand::execute(Context* context)
curve_add_point(the_curve, curve_point_new(0, 0));
curve_add_point(the_curve, curve_point_new(255, 255));
app_add_hook(APP_EXIT,
reinterpret_cast<void(*)(void*)>(curve_free),
the_curve);
App::instance()->add_hook(AppEvent::Exit, new DestroyCurve(the_curve));
}
JWidgetPtr window(load_widget("colcurv.jid", "color_curve"));

View File

@ -68,34 +68,44 @@
#include <winalleg.h>
#endif
/* options */
enum {
OPEN_GFX_FILE,
};
struct Option
class Option
{
int type;
char *data;
};
int m_type;
jstring m_data;
struct AppHook
{
void (*proc)(void *);
void *data;
public:
AppHook(void (*proc)(void *), void *data) {
this->proc = proc;
this->data = data;
enum {
OpenSprite,
};
Option(int type, const char* data)
{
m_type = type;
m_data = data;
}
int type() const { return m_type; }
const char* data() const { return m_data.c_str(); }
};
class App::Pimpl
{
public:
const char* m_exe_name;
std::vector<Option*> m_options;
CommandsModule m_commands_modules;
UIContext m_ui_context;
int m_return_code;
std::vector<std::vector<IAppHook*> > m_apphooks;
Pimpl() { }
~Pimpl() { }
};
App* App::m_instance = NULL;
static char *exe_name; /* name of the program */
static JList apphooks[APP_EVENTS];
static JWidget top_window = NULL; /* top level window (the desktop) */
static JWidget box_menubar = NULL; /* box where the menu bar is */
static JWidget box_colorbar = NULL; /* box where the color bar is */
@ -108,26 +118,10 @@ static JWidget colorbar = NULL; /* the color bar widget */
static JWidget toolbar = NULL; /* the tool bar widget */
static JWidget tabsbar = NULL; /* the tabs bar widget */
static JList options; /* list of "Option" structures (options to execute) */
static char *palette_filename = NULL;
static void tabsbar_select_callback(JWidget tabs, void *data, int button);
static void check_args(int argc, char *argv[]);
static void usage(int status);
static Option *option_new(int type, const char *data);
static void option_free(Option *option);
class App::Pimpl
{
CommandsModule m_commands_modules;
UIContext m_ui_context;
public:
Pimpl() { }
~Pimpl() { }
};
/**
* Initializes the application loading the modules, setting the
* graphics mode, loading the configuration and resources, etc.
@ -137,11 +131,11 @@ App::App(int argc, char *argv[])
assert(m_instance == NULL);
m_instance = this;
exe_name = argv[0];
/* initialize application hooks */
for (int c=0; c<APP_EVENTS; ++c)
apphooks[c] = NULL;
// create private implementation data
m_pimpl = new Pimpl;
m_pimpl->m_exe_name = argv[0];
m_pimpl->m_return_code = 0;
m_pimpl->m_apphooks.resize(AppEvent::NumEvents);
/* initialize language suppport */
intl_init();
@ -155,7 +149,7 @@ App::App(int argc, char *argv[])
/* init configuration */
ase_config_init();
/* load the language file */
// load the language file
intl_load_lang();
/* search options in the arguments */
@ -168,11 +162,8 @@ App::App(int argc, char *argv[])
/* install 'raster' stuff */
gfxobj_init();
// create singletons
m_pimpl = new Pimpl;
// install the modules
modules_init(REQUIRE_INTERFACE);
modules_init(ase_mode & MODE_GUI ? REQUIRE_INTERFACE: 0);
/* custom default palette? */
if (palette_filename) {
@ -197,11 +188,8 @@ App::App(int argc, char *argv[])
* Runs the ASE application. In GUI mode it's the top-level window, in
* console/scripting it just runs the specified scripts.
*/
void App::run()
int App::run()
{
Option *option;
JLink link;
/* initialize GUI interface */
if (ase_mode & MODE_GUI) {
JWidget view, editor;
@ -217,7 +205,7 @@ void App::run()
if (!top_window) {
allegro_message("Error loading data data/jids/main.jid file.\n"
"You have to reinstall the program.\n");
exit(1);
return 1;
}
box_menubar = jwidget_find_name(top_window, "menubar");
@ -285,23 +273,24 @@ void App::run()
/* set_display_switch_mode(SWITCH_BACKAMNESIA); */
set_display_switch_mode(SWITCH_BACKGROUND);
/* procress options */
// procress options
PRINTF("Processing options...\n");
for (std::vector<Option*>::iterator
it = m_pimpl->m_options.begin(); it != m_pimpl->m_options.end(); ++it) {
Option* option = *it;
JI_LIST_FOR_EACH(options, link) {
option = reinterpret_cast<Option*>(link->data);
switch (option->type()) {
switch (option->type) {
case OPEN_GFX_FILE: {
case Option::OpenSprite: {
/* load the sprite */
Sprite *sprite = sprite_load(option->data);
Sprite *sprite = sprite_load(option->data());
if (!sprite) {
/* error */
if (ase_mode & MODE_GUI)
jalert(_("Error<<Error loading file \"%s\"||&Close"), option->data);
jalert(_("Error<<Error loading file \"%s\"||&Close"), option->data());
else
user_printf(_("Error loading file \"%s\"\n"), option->data);
user_printf(_("Error loading file \"%s\"\n"), option->data());
}
else {
/* mount and select the sprite */
@ -314,16 +303,15 @@ void App::run()
set_sprite_in_more_reliable_editor(context->get_first_sprite());
/* recent file */
recent_file(option->data);
recent_file(option->data());
}
}
break;
}
}
option_free(option);
delete option;
}
jlist_free(options);
m_pimpl->m_options.clear();
/* just batch mode */
if (ase_mode & MODE_BATCH) {
@ -357,6 +345,7 @@ void App::run()
jwidget_free(top_window);
top_window = NULL;
}
return 0;
}
/**
@ -364,63 +353,63 @@ void App::run()
*/
App::~App()
{
assert(m_instance == this);
try {
assert(m_instance == this);
// remove ase handlers
PRINTF("Uninstalling ASE\n");
// remove ase handlers
PRINTF("Uninstalling ASE\n");
app_trigger_event(APP_EXIT);
App::trigger_event(AppEvent::Exit);
// destroy application hooks
for (int c=0; c<APP_EVENTS; ++c) {
if (apphooks[c] != NULL) {
JLink link;
JI_LIST_FOR_EACH(apphooks[c], link) {
AppHook* apphook = reinterpret_cast<AppHook*>(link->data);
// destroy application hooks
for (int c=0; c<AppEvent::NumEvents; ++c) {
for (std::vector<IAppHook*>::iterator
it = m_pimpl->m_apphooks[c].begin();
it != m_pimpl->m_apphooks[c].end(); ++it) {
IAppHook* apphook = *it;
delete apphook;
}
jlist_free(apphooks[c]);
apphooks[c] = NULL;
// clear the list of hooks (so nobody can call the deleted hooks)
m_pimpl->m_apphooks[c].clear();
}
}
// finalize modules, configuration and core
modules_exit();
delete m_pimpl;
editor_cursor_exit();
boundary_exit();
// finalize modules, configuration and core
modules_exit();
delete m_pimpl;
editor_cursor_exit();
boundary_exit();
gfxobj_exit();
ase_config_exit();
file_system_exit();
core_exit();
intl_exit();
gfxobj_exit();
ase_config_exit();
file_system_exit();
core_exit();
intl_exit();
m_instance = NULL;
m_instance = NULL;
}
catch (...) {
allegro_message("Uncaught exception in ~App");
// no throw
}
}
void app_add_hook(int app_event, void (*proc)(void *data), void *data)
void App::add_hook(AppEvent::Type event, IAppHook* hook)
{
assert(app_event >= 0 && app_event < APP_EVENTS);
assert(event >= 0 && event < AppEvent::NumEvents);
if (apphooks[app_event] == NULL)
apphooks[app_event] = jlist_new();
jlist_append(apphooks[app_event], new AppHook(proc, data));
m_pimpl->m_apphooks[event].push_back(hook);
}
void app_trigger_event(int app_event)
void App::trigger_event(AppEvent::Type event)
{
assert(app_event >= 0 && app_event < APP_EVENTS);
assert(event >= 0 && event < AppEvent::NumEvents);
if (apphooks[app_event] != NULL) {
JList list = apphooks[app_event];
JLink link;
JI_LIST_FOR_EACH(list, link) {
AppHook *h = (AppHook *)link->data;
(h->proc)(h->data);
}
for (std::vector<IAppHook*>::iterator
it = m_pimpl->m_apphooks[event].begin();
it != m_pimpl->m_apphooks[event].end(); ++it) {
IAppHook* apphook = *it;
apphook->on_event();
}
}
@ -589,14 +578,12 @@ static void tabsbar_select_callback(JWidget tabs, void *data, int button)
/**
* Looks the inpunt arguments in the command line.
*/
static void check_args(int argc, char *argv[])
void App::check_args(int argc, char *argv[])
{
Console console;
int i, n, len;
char *arg;
options = jlist_new();
for (i=1; i<argc; i++) {
arg = argv[i];
@ -610,7 +597,7 @@ static void check_args(int argc, char *argv[])
if (++i < argc)
palette_filename = argv[i];
else
usage(1);
usage(false);
}
/* video resolution */
else if (strncmp(arg+n, "resolution", len) == 0) {
@ -646,8 +633,9 @@ static void check_args(int argc, char *argv[])
}
}
else {
console.printf(_("%s: option \"res\" requires an argument\n"), exe_name);
usage(1);
console.printf(_("%s: option \"res\" requires an argument\n"),
m_pimpl->m_exe_name);
usage(false);
}
}
/* verbose mode */
@ -656,7 +644,7 @@ static void check_args(int argc, char *argv[])
}
/* show help */
else if (strncmp(arg+n, "help", len) == 0) {
usage(0);
usage(true);
}
/* show version */
else if (strncmp(arg+n, "version", len) == 0) {
@ -666,34 +654,38 @@ static void check_args(int argc, char *argv[])
}
/* invalid argument */
else {
usage(1);
usage(false);
}
}
/* graphic file to open */
else if (n == 0)
jlist_append(options, option_new(OPEN_GFX_FILE, argv[i]));
m_pimpl->m_options.push_back(new Option(Option::OpenSprite, argv[i]));
}
}
/**
* Shows the available options for the program
*/
static void usage(int status)
void App::usage(bool show_help)
{
Console console;
/* show options */
if (!status) {
/* copyright */
console.printf
("ase %s -- allegro-sprite-editor, %s\n"
COPYRIGHT "\n\n",
VERSION, _("The Ultimate Sprites Factory"));
ase_mode |= MODE_BATCH;
if (!show_help)
m_pimpl->m_return_code = 1;
/* usage */
// show options
if (show_help) {
// copyright
console.printf
("ase %s -- Allegro Sprite Editor, %s\n"
COPYRIGHT "\n\n",
VERSION, _("Just another tool to create sprites"));
// usage
console.printf
("%s\n %s [%s] [%s]...\n\n",
_("Usage:"), exe_name, _("OPTION"), _("FILE"));
_("Usage:"), m_pimpl->m_exe_name, _("OPTION"), _("FILE"));
/* options */
console.printf
@ -713,28 +705,12 @@ static void usage(int status)
/* web-site */
console.printf
("%s: %s\n%s\n\n %s\n\n",
("%s: %s\n\n",
_("Find more information in the ASE's official web site at:"), WEBSITE);
}
/* how to show options */
else {
console.printf(_("Try \"%s --help\" for more information.\n"), exe_name);
console.printf(_("Try \"%s --help\" for more information.\n"),
m_pimpl->m_exe_name);
}
exit(status);
}
static Option *option_new(int type, const char *data)
{
Option *option = new Option;
option->type = type;
option->data = jstrdup(data);
return option;
}
static void option_free(Option* option)
{
jfree(option->data);
delete option;
}

View File

@ -21,19 +21,30 @@
#include "jinete/jbase.h"
// enumeration of ASE events in the highest application level
enum {
APP_EXIT,
APP_PALETTE_CHANGE,
APP_EVENTS
};
class Layer;
class Sprite;
class Params;
class Command;
class CommandsModule;
class AppEvent
{
public:
// enumeration of ASE events in the highest application level
enum Type {
Exit,
PaletteChange,
NumEvents
};
};
class IAppHook
{
public:
virtual ~IAppHook() { }
virtual void on_event() = 0;
};
class App
{
static App* m_instance;
@ -47,11 +58,16 @@ public:
static App* instance() { return m_instance; }
void run();
};
int run();
void app_add_hook(int app_event, void (*proc)(void *data), void *data);
void app_trigger_event(int app_event);
void add_hook(AppEvent::Type event, IAppHook* hook);
void trigger_event(AppEvent::Type event);
private:
void check_args(int argc, char *argv[]);
void usage(bool show_help);
};
void app_refresh_screen(const Sprite* sprite);

View File

@ -30,7 +30,7 @@
#include "modules/tools.h"
#define DEF_MODULE(name, reqs) \
{ #name, init_module_##name, exit_module_##name, (reqs), FALSE }
{ #name, init_module_##name, exit_module_##name, (reqs), false }
typedef struct Module
{
@ -38,7 +38,7 @@ typedef struct Module
int (*init)();
void (*exit)();
int reqs;
int installed;
bool installed;
} Module;
static Module module[] =
@ -46,9 +46,9 @@ static Module module[] =
/* This sorting is very important because last modules depend of
first ones. */
DEF_MODULE(palette, REQUIRE_INTERFACE),
DEF_MODULE(effect, REQUIRE_INTERFACE),
DEF_MODULE(tools, REQUIRE_INTERFACE),
DEF_MODULE(palette, 0),
DEF_MODULE(effect, 0),
DEF_MODULE(tools, 0),
DEF_MODULE(graphics, REQUIRE_INTERFACE),
DEF_MODULE(gui, REQUIRE_INTERFACE),
DEF_MODULE(recent, REQUIRE_INTERFACE),
@ -61,13 +61,13 @@ static int modules = sizeof(module) / sizeof(Module);
void modules_init(int requirements)
{
for (int c=0; c<modules; c++)
if (module[c].reqs & requirements) {
if ((module[c].reqs & requirements) == module[c].reqs) {
PRINTF("Installing module: %s\n", module[c].name);
if ((*module[c].init)() < 0)
throw ase_exception(std::string("Error initializing module: ") + module[c].name);
module[c].installed = TRUE;
module[c].installed = true;
}
}
@ -77,6 +77,6 @@ void modules_exit()
if (module[c].installed) {
PRINTF("Unstalling module: %s\n", module[c].name);
(*module[c].exit)();
module[c].installed = FALSE;
module[c].installed = false;
}
}

View File

@ -46,6 +46,17 @@
# define MAX_PATH 4096 /* TODO this is needed for Linux, is it correct? */
#endif
class FreeList : public IAppHook
{
JList m_list;
public:
FreeList(JList list) : m_list(list) { }
void on_event()
{
jlist_free(m_list);
}
};
/* Variables used only to maintain the history of navigation. */
static JLink navigation_position = NULL; /* current position in the navigation history */
static JList navigation_history = NULL; /* set of FileItems navigated */
@ -89,9 +100,8 @@ jstring ase_file_selector(const jstring& message,
if (!navigation_history) {
navigation_history = jlist_new();
app_add_hook(APP_EXIT,
reinterpret_cast<void(*)(void*)>(jlist_free),
navigation_history);
App::instance()->add_hook(AppEvent::Exit,
new FreeList(navigation_history));
}
// we have to find where the user should begin to browse files (start_folder)

View File

@ -52,13 +52,14 @@ public:
*/
int main(int argc, char *argv[])
{
int status = 1; // 1 = error
try {
Allegro allegro;
try {
Jinete jinete;
App app(argc, argv);
app.run();
status = app.run();
}
catch (std::exception& e) {
allegro_message(e.what());
@ -70,7 +71,7 @@ int main(int argc, char *argv[])
catch (...) {
printf("Uncaught exception");
}
return 0;
return status;
}
END_OF_MAIN();

View File

@ -68,34 +68,32 @@ static void convert_data_to_bitmap(DATA *data, BITMAP **bmp)
}
}
static void gen_gfx(void *data)
class GenGfx : public IAppHook
{
int c;
public:
void on_event()
{
for (int c=0; c<GFX_BITMAP_COUNT; c++) {
if (gfx_bmps[c])
destroy_bitmap(gfx_bmps[c]);
for (c=0; c<GFX_BITMAP_COUNT; c++) {
if (gfx_bmps[c])
destroy_bitmap(gfx_bmps[c]);
gfx_bmps[c] = NULL;
gfx_bmps[c] = NULL;
}
}
}
};
int init_module_graphics()
{
int c;
for (c=0; c<GFX_BITMAP_COUNT; c++)
for (int c=0; c<GFX_BITMAP_COUNT; c++)
gfx_bmps[c] = NULL;
app_add_hook(APP_PALETTE_CHANGE, gen_gfx, NULL);
App::instance()->add_hook(AppEvent::PaletteChange, new GenGfx);
return 0;
}
void exit_module_graphics()
{
int c;
for (c=0; c<GFX_BITMAP_COUNT; c++)
for (int c=0; c<GFX_BITMAP_COUNT; c++)
if (gfx_bmps[c]) {
destroy_bitmap(gfx_bmps[c]);
gfx_bmps[c] = NULL;

View File

@ -149,7 +149,11 @@ static void save_gui_config();
static bool button_with_icon_msg_proc(JWidget widget, JMessage msg);
static bool manager_msg_proc(JWidget widget, JMessage msg);
static void regen_theme_and_fixup_icons(void *data);
class RegenIcons : public IAppHook
{
public:
void on_event();
};
/**
* Used by set_display_switch_callback(SWITCH_IN, ...).
@ -298,7 +302,7 @@ int init_module_gui()
reload_default_font();
/* hook for palette change to regenerate the theme */
app_add_hook(APP_PALETTE_CHANGE, regen_theme_and_fixup_icons, NULL);
App::instance()->add_hook(AppEvent::PaletteChange, new RegenIcons());
/* icon buttons */
icon_buttons = jlist_new();
@ -955,9 +959,9 @@ void* get_monitor_data(Monitor* monitor)
return monitor->data;
}
/**********************************************************************/
/* manager event handler */
/**
* Manager event handler.
*/
static bool manager_msg_proc(JWidget widget, JMessage msg)
{
switch (msg->type) {
@ -1069,10 +1073,10 @@ static bool manager_msg_proc(JWidget widget, JMessage msg)
return false;
}
/**********************************************************************/
/* graphics */
static void regen_theme_and_fixup_icons(void *data)
/**
* IAppHook to regenerate graphics when the App palette is changed.
*/
void RegenIcons::on_event()
{
JWidget button;
JLink link;

View File

@ -195,8 +195,8 @@ bool set_current_palette(Palette *_palette, bool forced)
create_rgb_table(my_rgb_map, rgbpal, NULL);
set_palette(rgbpal); /* change system color palette */
/* call hooks */
app_trigger_event(APP_PALETTE_CHANGE);
// call hooks
App::instance()->trigger_event(AppEvent::PaletteChange);
ret = TRUE;
}

View File

@ -137,7 +137,7 @@ static AlgoHLine inks_hline[][3] =
/* CURSOR COLOR */
/***********************************************************/
static void update_cursor_color(void *data)
static void update_cursor_color()
{
if (ji_screen)
_cursor_color = get_color_for_allegro(bitmap_color_depth(ji_screen),
@ -148,6 +148,15 @@ static void update_cursor_color(void *data)
_cursor_mask = (color_type(cursor_color) == COLOR_TYPE_MASK);
}
class UpdateCursorColor : public IAppHook
{
public:
void on_event()
{
update_cursor_color();
}
};
/***********************************************************/
/* TOOLS */
/***********************************************************/
@ -192,7 +201,7 @@ int init_module_tools()
air_speed = MID(1, air_speed, 100);
tiled_mode = (tiled_t)MID(0, (int)tiled_mode, TILED_BOTH);
app_add_hook(APP_PALETTE_CHANGE, update_cursor_color, NULL);
App::instance()->add_hook(AppEvent::PaletteChange, new UpdateCursorColor);
return 0;
}
@ -309,7 +318,7 @@ color_t get_cursor_color()
void set_cursor_color(color_t color)
{
cursor_color = color;
update_cursor_color(NULL);
update_cursor_color();
}
/* returns the size which use the current tool */

View File

@ -35,8 +35,6 @@ static std::map<gfxobj_id, GfxObj*>* objects_map; // graphics objects map
static void insert_gfxobj(GfxObj* gfxobj);
static void erase_gfxobj(GfxObj* gfxobj);
//////////////////////////////////////////////////////////////////////
void gfxobj_init()
{
objects_map = new std::map<gfxobj_id, GfxObj*>;
@ -52,7 +50,7 @@ void gfxobj_exit()
//////////////////////////////////////////////////////////////////////
// GfxObj class
GfxObj::GfxObj(int type)
{
this->type = type;
@ -89,7 +87,6 @@ void GfxObj::assign_id()
}
//////////////////////////////////////////////////////////////////////
GfxObj* gfxobj_find(gfxobj_id id)
{

View File

@ -79,7 +79,6 @@ enum {
ACTION_ROTATE_BR,
};
static void destroy_clipboard(void* data);
static void set_clipboard(Image* image, Palette* palette, bool set_system_clipboard);
static bool copy_from_sprite(const Sprite* sprite);
@ -106,17 +105,21 @@ static bool first_time = true;
static Palette* clipboard_palette = NULL;
static Image* clipboard_image = NULL;
static void destroy_clipboard(void* data)
class DestroyClipboard : public IAppHook
{
delete clipboard_palette;
delete clipboard_image;
}
public:
void on_event()
{
delete clipboard_palette;
delete clipboard_image;
}
};
static void set_clipboard(Image* image, Palette* palette, bool set_system_clipboard)
{
if (first_time) {
first_time = false;
app_add_hook(APP_EXIT, destroy_clipboard, NULL);
App::instance()->add_hook(AppEvent::Exit, new DestroyClipboard());
}
delete clipboard_palette;