aseprite/src/modules/gui.cpp
David Capello 3f6f1471fd Removed mouse speed option.
Removed _setup_mouse_speed() function and dialog/options.cpp file.
All the code is in cmd_options.cpp now.
2010-04-25 15:52:19 -03:00

1210 lines
28 KiB
C++

/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2010 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 <algorithm>
#include <allegro.h>
#include <allegro/internal/aintern.h>
#include <cassert>
#include <list>
#include <vector>
#ifdef ALLEGRO_WINDOWS
#include <winalleg.h>
#endif
#include "jinete/jinete.h"
#include "jinete/jintern.h"
#include "Vaca/SharedPtr.h"
#include "commands/command.h"
#include "commands/commands.h"
#include "commands/params.h"
#include "console.h"
#include "app.h"
#include "core/cfg.h"
#include "core/core.h"
#include "core/dirs.h"
#include "core/drop_files.h"
#include "intl/msgids.h"
#include "gfxmode.h"
#include "modules/editors.h"
#include "modules/gfx.h"
#include "modules/gui.h"
#include "modules/palettes.h"
#include "modules/rootmenu.h"
#include "modules/skinneable_theme.h"
#include "raster/sprite.h"
#include "sprite_wrappers.h"
#include "ui_context.h"
#include "util/recscr.h"
#include "widgets/editor.h"
#include "widgets/statebar.h"
#include "widgets/toolbar.h"
#include "tools/toolbox.h"
#define REBUILD_RECENT_LIST 2
#define REFRESH_FULL_SCREEN 4
#define SYSTEM_WINDOW_RESIZE 8
#define MONITOR_TIMER_MSECS 100
//////////////////////////////////////////////////////////////////////
#ifdef ALLEGRO_WINDOWS
# define DEF_SCALE 2
#else
# define DEF_SCALE 1
#endif
static struct
{
int width;
int height;
int scale;
} try_resolutions[] = { { 1024, 768, DEF_SCALE },
{ 800, 600, DEF_SCALE },
{ 640, 480, DEF_SCALE },
{ 320, 240, 1 },
{ 320, 200, 1 },
{ 0, 0, 0 } };
static int try_depths[] = { 32, 24, 16, 15 };
static GfxMode lastWorkingGfxMode;
//////////////////////////////////////////////////////////////////////
enum ShortcutType { Shortcut_ExecuteCommand,
Shortcut_ChangeTool };
struct Shortcut
{
JAccel accel;
ShortcutType type;
union {
Command* command;
Tool* tool;
};
Params* params;
Shortcut(ShortcutType type);
~Shortcut();
void add_shortcut(const char* shortcut_string);
bool is_key_pressed(JMessage msg);
};
static Shortcut* get_keyboard_shortcut_for_command(const char* command_name, Params* params);
static Shortcut* get_keyboard_shortcut_for_tool(Tool* tool);
//////////////////////////////////////////////////////////////////////
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();
};
//////////////////////////////////////////////////////////////////////
static JWidget manager = NULL;
static JTheme ase_theme = NULL;
static int monitor_timer = -1;
static MonitorList* monitors = NULL;
static std::vector<Shortcut*>* shortcuts = NULL;
static bool ji_screen_created = false;
static volatile int next_idle_flags = 0;
static JList icon_buttons;
/* default GUI screen configuration */
static bool double_buffering;
static int screen_scaling;
static void reload_default_font();
/* load & save graphics configuration */
static void load_gui_config(int& w, int& h, int& bpp, bool& fullscreen, bool& maximized);
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 on_palette_change_signal();
/**
* Used by set_display_switch_callback(SWITCH_IN, ...).
*/
static void display_switch_in_callback()
{
next_idle_flags |= REFRESH_FULL_SCREEN;
}
END_OF_STATIC_FUNCTION(display_switch_in_callback);
#ifdef HAVE_RESIZE_PATCH
static void resize_callback(void)
{
next_idle_flags |= SYSTEM_WINDOW_RESIZE;
}
#endif
/**
* Initializes GUI.
*/
int init_module_gui()
{
int min_possible_dsk_res = 0;
int c, w, h, bpp, autodetect;
bool fullscreen;
bool maximized;
monitors = new MonitorList;
shortcuts = new std::vector<Shortcut*>;
/* install the mouse */
if (install_mouse() < 0) {
user_printf(_("Error installing mouse handler\n"));
return -1;
}
/* install the keyboard */
if (install_keyboard() < 0) {
user_printf(_("Error installing keyboard handler\n"));
return -1;
}
/* disable Ctrl+Shift+End in non-DOS */
#if !defined(ALLEGRO_DOS)
three_finger_flag = false;
#endif
three_finger_flag = true; /* TODO remove this line */
/* set the graphics mode... */
load_gui_config(w, h, bpp, fullscreen, maximized);
autodetect = fullscreen ? GFX_AUTODETECT_FULLSCREEN:
GFX_AUTODETECT_WINDOWED;
/* default resolution */
if (!w || !h) {
bool has_desktop = false;
int dsk_w, dsk_h;
has_desktop = get_desktop_resolution(&dsk_w, &dsk_h) == 0;
/* we must extract some space for the windows borders */
dsk_w -= 16;
dsk_h -= 32;
/* try to get desktop resolution */
if (has_desktop) {
for (c=0; try_resolutions[c].width; ++c) {
if (try_resolutions[c].width <= dsk_w &&
try_resolutions[c].height <= dsk_h) {
min_possible_dsk_res = c;
fullscreen = false;
w = try_resolutions[c].width;
h = try_resolutions[c].height;
screen_scaling = try_resolutions[c].scale;
break;
}
}
}
/* full screen */
else {
fullscreen = true;
w = 320;
h = 200;
screen_scaling = 1;
}
}
/* default color depth */
if (!bpp) {
bpp = desktop_color_depth();
if (!bpp)
bpp = 16;
}
for (;;) {
if (bpp == 8) {
allegro_message("You cannot use ASE in 8 bits per pixel\n");
return -1;
}
// Original
set_color_depth(bpp);
if (set_gfx_mode(autodetect, w, h, 0, 0) == 0)
break;
for (c=min_possible_dsk_res; try_resolutions[c].width; ++c) {
if (set_gfx_mode(autodetect,
try_resolutions[c].width,
try_resolutions[c].height, 0, 0) == 0) {
screen_scaling = try_resolutions[c].scale;
goto gfx_done;
}
}
if (bpp == 15) {
user_printf(_("Error setting graphics mode\n%s\n"
"Try \"ase -res WIDTHxHEIGHTxBPP\"\n"), allegro_error);
return -1;
}
else {
for (c=0; try_depths[c]; ++c) {
if (bpp == try_depths[c]) {
bpp = try_depths[c+1];
break;
}
}
}
}
gfx_done:;
/* window title */
set_window_title(PACKAGE " v" VERSION);
/* create the default-manager */
manager = jmanager_new();
jwidget_add_hook(manager, JI_WIDGET, manager_msg_proc, NULL);
/* setup the standard jinete theme for widgets */
ji_set_theme(ase_theme = new SkinneableTheme());
/* set hook to translate strings */
ji_set_translation_hook(msgids_get);
#ifdef HAVE_RESIZE_PATCH
set_resize_callback(resize_callback);
#ifdef ALLEGRO_WINDOWS
if (maximized) {
ShowWindow(win_get_window(), SW_MAXIMIZE);
}
#endif
#endif
/* configure ji_screen */
gui_setup_screen(true);
/* add a hook to display-switch so when the user returns to the
screen it's completelly refreshed/redrawn */
LOCK_VARIABLE(next_idle_flags);
LOCK_FUNCTION(display_switch_in_callback);
set_display_switch_callback(SWITCH_IN, display_switch_in_callback);
/* set graphics options for next time */
save_gui_config();
// Hook for palette change to regenerate the theme
App::instance()->PaletteChange.connect(&on_palette_change_signal);
/* icon buttons */
icon_buttons = jlist_new();
return 0;
}
void exit_module_gui()
{
// destroy shortcuts
assert(shortcuts != NULL);
for (std::vector<Shortcut*>::iterator
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
delete shortcut;
}
delete shortcuts;
shortcuts = NULL;
// destroy monitors
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);
if (ji_screen_created)
destroy_bitmap(old_bmp);
ji_screen_created = false;
}
jlist_free(icon_buttons);
icon_buttons = NULL;
jmanager_free(manager);
ji_set_theme(NULL);
delete ase_theme;
remove_keyboard();
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);
h = get_config_int("GfxMode", "Height", 0);
bpp = get_config_int("GfxMode", "Depth", 0);
fullscreen = get_config_bool("GfxMode", "FullScreen", false);
screen_scaling = get_config_int("GfxMode", "Scale", 1);
screen_scaling = MID(1, screen_scaling, 4);
#if defined HAVE_RESIZE_PATCH && defined ALLEGRO_WINDOWS
maximized = get_config_bool("GfxMode", "Maximized", false);
#else
maximized = false;
#endif
// Avoid 8 bpp
if (bpp == 8)
bpp = 32;
}
static void save_gui_config()
{
if (screen) {
set_config_int("GfxMode", "Width", SCREEN_W);
set_config_int("GfxMode", "Height", SCREEN_H);
set_config_int("GfxMode", "Depth", bitmap_color_depth(screen));
}
if (gfx_driver)
set_config_bool("GfxMode", "FullScreen", gfx_driver->windowed ? false: true);
set_config_int("GfxMode", "Scale", screen_scaling);
#if defined HAVE_RESIZE_PATCH && defined ALLEGRO_WINDOWS
set_config_bool("GfxMode", "Maximized",
GetWindowLong(win_get_window(), GWL_STYLE) & WS_MAXIMIZE ? true: false);
#endif
}
int get_screen_scaling()
{
return screen_scaling;
}
void set_screen_scaling(int scaling)
{
screen_scaling = scaling;
}
void update_screen_for_sprite(const Sprite* sprite)
{
if (!(ase_mode & MODE_GUI))
return;
/* without sprite */
if (!sprite) {
/* well, change to the default palette */
if (set_current_palette(NULL, false)) {
/* if the palette changes, refresh the whole screen */
jmanager_refresh_screen();
}
}
/* with a sprite */
else {
/* select the palette of the sprite */
if (set_current_palette(sprite->getPalette(sprite->getCurrentFrame()), false)) {
/* if the palette changes, refresh the whole screen */
jmanager_refresh_screen();
}
else {
/* if it's the same palette update only the editors with the sprite */
update_editors_with_sprite(sprite);
}
}
}
void gui_run()
{
jmanager_run(manager);
}
void gui_feedback()
{
#ifdef HAVE_RESIZE_PATCH
if (next_idle_flags & SYSTEM_WINDOW_RESIZE) {
next_idle_flags ^= SYSTEM_WINDOW_RESIZE;
resize_screen();
gui_setup_screen(false);
app_get_top_window()->remap_window();
jmanager_refresh_screen();
}
#endif
/* menu stuff */
if (next_idle_flags & REBUILD_RECENT_LIST) {
if (app_realloc_recent_list())
next_idle_flags ^= REBUILD_RECENT_LIST;
}
if (next_idle_flags & REFRESH_FULL_SCREEN) {
next_idle_flags ^= REFRESH_FULL_SCREEN;
try {
const CurrentSpriteReader sprite(UIContext::instance());
update_screen_for_sprite(sprite);
}
catch (...) {
// do nothing
}
}
/* record file if is necessary */
rec_screen_poll();
gui_flip_screen();
}
void gui_flip_screen()
{
// Double buffering?
if (double_buffering && ji_screen && screen) {
jmouse_draw_cursor();
if (ji_dirty_region) {
ji_flip_dirty_region();
}
else {
if (JI_SCREEN_W == SCREEN_W && JI_SCREEN_H == SCREEN_H) {
blit(ji_screen, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
}
else {
stretch_blit(ji_screen, screen,
0, 0, ji_screen->w, ji_screen->h,
0, 0, SCREEN_W, SCREEN_H);
}
}
}
// This is a strange Allegro bug where the "screen" pointer is lost,
// so we have to reconstruct it changing the gfx mode again
else if (screen == NULL) {
PRINTF("Gfx mode lost, trying to restore gfx mode...\n");
if (!lastWorkingGfxMode.setGfxMode()) {
PRINTF("Fatal error\n");
set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
user_printf(_("FATAL ERROR: Unable to restore the old graphics mode!\n"));
exit(1);
}
PRINTF("Successfully restored\n");
}
else
lastWorkingGfxMode.updateWithCurrentMode();
}
/**
* Sets the ji_screen variable.
*
* This routine should be called everytime you changes the graphics
* mode.
*/
void gui_setup_screen(bool reload_font)
{
bool regen = false;
bool reinit = false;
// Double buffering is required when screen scaling is used
double_buffering = (screen_scaling > 1);
// Is double buffering active?
if (double_buffering) {
BITMAP *old_bmp = ji_screen;
ji_set_screen(create_bitmap(SCREEN_W / screen_scaling,
SCREEN_H / screen_scaling));
if (ji_screen_created)
destroy_bitmap(old_bmp);
ji_screen_created = true;
}
else {
ji_set_screen(screen);
ji_screen_created = false;
}
// Update guiscale factor
int old_guiscale = jguiscale();
ji_get_theme()->guiscale = (screen_scaling == 1 &&
JI_SCREEN_W > 512 &&
JI_SCREEN_H > 256) ? 2: 1;
// If the guiscale have changed
if (old_guiscale != jguiscale()) {
reload_font = true;
regen = true;
}
if (reload_font) {
reload_default_font();
reinit = true;
}
// Regenerate the theme
if (regen) {
ji_regen_theme();
reinit = true;
}
if (reinit)
_ji_reinit_theme_in_all_widgets();
// Set the configuration
save_gui_config();
}
static void reload_default_font()
{
JTheme theme = ji_get_theme();
SkinneableTheme* skinneable_theme = static_cast<SkinneableTheme*>(theme);
const char *user_font;
DIRS *dirs, *dir;
// No font for now
if (theme->default_font && theme->default_font != font)
destroy_font(theme->default_font);
theme->default_font = NULL;
// Directories
dirs = dirs_new();
user_font = get_config_string("Options", "UserFont", "");
if ((user_font) && (*user_font))
dirs_add_path(dirs, user_font);
// TODO This should be in SkinneableTheme class
dirs_cat_dirs(dirs, filename_in_datadir(skinneable_theme->get_font_filename().c_str()));
// Try to load the font
for (dir=dirs; dir; dir=dir->next) {
theme->default_font = ji_font_load(dir->path);
if (theme->default_font) {
if (ji_font_is_scalable(theme->default_font))
ji_font_set_size(theme->default_font, 8*jguiscale());
break;
}
}
dirs_free(dirs);
// default font: the Allegro one
if (!theme->default_font)
theme->default_font = font;
// Set all widgets fonts
_ji_set_font_of_all_widgets(theme->default_font);
}
void load_window_pos(JWidget window, const char *section)
{
// Default position
Rect orig_pos = window->getBounds();
Rect pos = orig_pos;
// Load configurated position
pos = get_config_rect(section, "WindowPos", pos);
pos.w = MID(orig_pos.w, pos.w, JI_SCREEN_W);
pos.h = MID(orig_pos.h, pos.h, JI_SCREEN_H);
pos.setOrigin(Point(MID(0, pos.x, JI_SCREEN_W-pos.w),
MID(0, pos.y, JI_SCREEN_H-pos.h)));
window->setBounds(pos);
}
void save_window_pos(JWidget window, const char *section)
{
set_config_rect(section, "WindowPos", window->getBounds());
}
JWidget load_widget(const char *filename, const char *name)
{
JWidget widget;
DIRS *it, *dirs;
char buf[512];
bool found = false;
dirs = dirs_new();
usprintf(buf, "widgets/%s", filename);
dirs_add_path(dirs, filename);
dirs_cat_dirs(dirs, filename_in_datadir(buf));
for (it=dirs; it; it=it->next) {
if (exists(it->path)) {
ustrcpy(buf, it->path);
found = true;
break;
}
}
dirs_free(dirs);
if (!found)
throw widget_file_not_found(filename);
widget = ji_load_widget(buf, name);
if (!widget)
throw widget_not_found(name);
return widget;
}
JWidget find_widget(JWidget widget, const char *name)
{
JWidget child = jwidget_find_name(widget, name);
if (!child)
throw widget_not_found(name);
return child;
}
void schedule_rebuild_recent_list()
{
next_idle_flags |= REBUILD_RECENT_LIST;
}
/**********************************************************************/
/* hook signals */
typedef struct HookData {
int signal_num;
bool (*signal_handler)(JWidget widget, void *data);
void *data;
} HookData;
static int hook_type()
{
static int type = 0;
if (!type)
type = ji_register_widget_type();
return type;
}
static bool hook_msg_proc(JWidget widget, JMessage msg)
{
switch (msg->type) {
case JM_DESTROY:
jfree(jwidget_get_data(widget, hook_type()));
break;
case JM_SIGNAL: {
HookData* hook_data = reinterpret_cast<HookData*>(jwidget_get_data(widget, hook_type()));
if (hook_data &&
hook_data->signal_num == msg->signal.num) {
return (*hook_data->signal_handler)(widget, hook_data->data);
}
break;
}
}
return false;
}
/**
* @warning You can't use this function for the same widget two times.
*/
void hook_signal(JWidget widget,
int signal_num,
bool (*signal_handler)(JWidget widget, void* data),
void* data)
{
HookData* hook_data = jnew(HookData, 1);
hook_data->signal_num = signal_num;
hook_data->signal_handler = signal_handler;
hook_data->data = data;
jwidget_add_hook(widget, hook_type(), hook_msg_proc, hook_data);
}
/**
* Utility routine to get various widgets by name.
*
* @code
* if (!get_widgets(wnd,
* "name1", &widget1,
* "name2", &widget2,
* "name3", &widget3,
* NULL)) {
* ...
* }
* @endcode
*/
void get_widgets(JWidget window, ...)
{
JWidget *widget;
char *name;
va_list ap;
va_start(ap, window);
while ((name = va_arg(ap, char *))) {
widget = va_arg(ap, JWidget *);
if (!widget)
break;
*widget = jwidget_find_name(window, name);
if (!*widget)
throw widget_not_found(name);
}
va_end(ap);
}
void setup_mini_look(Widget* widget)
{
Vaca::SharedPtr<SkinProperty> skinProp(new SkinProperty);
skinProp->setMiniLook(true);
widget->setProperty(skinProp);
}
/**********************************************************************/
/* Icon in buttons */
/* adds a button in the list of "icon_buttons" to restore the icon
when the palette changes, the "user_data[3]" is used to save the
"gfx_id" (the same ID that is used in "get_gfx()" from
"modules/gfx.c"), also this routine adds a hook to
JI_SIGNAL_DESTROY to remove the button from the "icon_buttons"
list when the widget is free */
void add_gfxicon_to_button(JWidget button, int gfx_id, int icon_align)
{
button->user_data[3] = (void *)gfx_id;
jwidget_add_hook(button, JI_WIDGET, button_with_icon_msg_proc, NULL);
ji_generic_button_set_icon(button, get_gfx(gfx_id));
ji_generic_button_set_icon_align(button, icon_align);
jlist_append(icon_buttons, button);
}
void set_gfxicon_in_button(JWidget button, int gfx_id)
{
button->user_data[3] = (void *)gfx_id;
ji_generic_button_set_icon(button, get_gfx(gfx_id));
jwidget_dirty(button);
}
static bool button_with_icon_msg_proc(JWidget widget, JMessage msg)
{
if (msg->type == JM_DESTROY)
jlist_remove(icon_buttons, widget);
return false;
}
/**********************************************************************/
/* Button style (convert radio or check buttons and draw it like
normal buttons) */
JWidget radio_button_new(int radio_group, int b1, int b2, int b3, int b4)
{
JWidget widget = ji_generic_button_new(NULL, JI_RADIO, JI_BUTTON);
if (widget) {
jradio_set_group(widget, radio_group);
jbutton_set_bevel(widget, b1, b2, b3, b4);
setup_mini_look(widget);
}
return widget;
}
JWidget check_button_new(const char *text, int b1, int b2, int b3, int b4)
{
JWidget widget = ji_generic_button_new(text, JI_CHECK, JI_BUTTON);
if (widget) {
jbutton_set_bevel(widget, b1, b2, b3, b4);
setup_mini_look(widget);
}
return widget;
}
//////////////////////////////////////////////////////////////////////
// Keyboard shortcuts
//////////////////////////////////////////////////////////////////////
JAccel add_keyboard_shortcut_to_execute_command(const char* shortcut_string, const char* command_name, Params* params)
{
Shortcut* shortcut = get_keyboard_shortcut_for_command(command_name, params);
if (!shortcut) {
shortcut = new Shortcut(Shortcut_ExecuteCommand);
shortcut->command = CommandsModule::instance()->get_command_by_name(command_name);
shortcut->params = params ? params->clone(): new Params;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
JAccel add_keyboard_shortcut_to_change_tool(const char* shortcut_string, Tool* tool)
{
Shortcut* shortcut = get_keyboard_shortcut_for_tool(tool);
if (!shortcut) {
shortcut = new Shortcut(Shortcut_ChangeTool);
shortcut->tool = tool;
shortcuts->push_back(shortcut);
}
shortcut->add_shortcut(shortcut_string);
return shortcut->accel;
}
Command* get_command_from_key_message(JMessage msg)
{
for (std::vector<Shortcut*>::iterator
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
if (shortcut->type == Shortcut_ExecuteCommand &&
// TODO why?
// shortcut->argument.empty() &&
shortcut->is_key_pressed(msg)) {
return shortcut->command;
}
}
return NULL;
}
JAccel get_accel_to_execute_command(const char* command_name, Params* params)
{
Shortcut* shortcut = get_keyboard_shortcut_for_command(command_name, params);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
JAccel get_accel_to_change_tool(Tool* tool)
{
Shortcut* shortcut = get_keyboard_shortcut_for_tool(tool);
if (shortcut)
return shortcut->accel;
else
return NULL;
}
Shortcut::Shortcut(ShortcutType type)
{
this->type = type;
this->accel = jaccel_new();
this->command = NULL;
this->tool = NULL;
this->params = NULL;
}
Shortcut::~Shortcut()
{
delete params;
jaccel_free(accel);
}
void Shortcut::add_shortcut(const char* shortcut_string)
{
char buf[256];
usprintf(buf, "<%s>", shortcut_string);
jaccel_add_keys_from_string(this->accel, buf);
}
bool Shortcut::is_key_pressed(JMessage msg)
{
if (accel) {
return jaccel_check(accel,
msg->any.shifts,
msg->key.ascii,
msg->key.scancode);
}
return false;
}
static Shortcut* get_keyboard_shortcut_for_command(const char* command_name, Params* params)
{
Command* command = CommandsModule::instance()->get_command_by_name(command_name);
if (!command)
return NULL;
for (std::vector<Shortcut*>::iterator
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
if (shortcut->type == Shortcut_ExecuteCommand &&
shortcut->command == command &&
((!params && shortcut->params->empty()) ||
(params && *shortcut->params == *params))) {
return shortcut;
}
}
return NULL;
}
static Shortcut* get_keyboard_shortcut_for_tool(Tool* tool)
{
for (std::vector<Shortcut*>::iterator
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
if (shortcut->type == Shortcut_ChangeTool &&
shortcut->tool == tool) {
return shortcut;
}
}
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 < 0)
monitor_timer = jmanager_add_timer(manager, MONITOR_TIMER_MSECS);
jmanager_start_timer(monitor_timer);
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())
jmanager_stop_timer(monitor_timer);
}
void* get_monitor_data(Monitor* monitor)
{
return monitor->data;
}
// Manager event handler.
static bool manager_msg_proc(JWidget widget, JMessage msg)
{
switch (msg->type) {
case JM_QUEUEPROCESSING:
gui_feedback();
/* open dropped files */
check_for_dropped_files();
break;
case JM_TIMER:
if (msg->timer.timer_id == 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())
jmanager_stop_timer(monitor_timer);
}
break;
case JM_KEYPRESSED:
for (std::vector<Shortcut*>::iterator
it = shortcuts->begin(); it != shortcuts->end(); ++it) {
Shortcut* shortcut = *it;
if (shortcut->is_key_pressed(msg)) {
switch (shortcut->type) {
case Shortcut_ChangeTool: {
Tool* current_tool = UIContext::instance()->getSettings()->getCurrentTool();
Tool* select_this_tool = shortcut->tool;
ToolBox* toolbox = App::instance()->get_toolbox();
std::vector<Tool*> possibles;
// Iterate over all tools
for (ToolIterator it = toolbox->begin(); it != toolbox->end(); ++it) {
Shortcut* shortcut = get_keyboard_shortcut_for_tool(*it);
// Collect all tools with the pressed keyboard-shortcut
if (shortcut && shortcut->is_key_pressed(msg))
possibles.push_back(*it);
}
if (possibles.size() >= 2) {
bool done = false;
for (size_t i=0; i<possibles.size(); ++i) {
if (possibles[i] != current_tool &&
toolbar_is_tool_visible(app_get_toolbar(), possibles[i])) {
select_this_tool = possibles[i];
done = true;
break;
}
}
if (!done) {
for (size_t i=0; i<possibles.size(); ++i) {
// If one of the possibilities is the current tool
if (possibles[i] == current_tool) {
// We select the next tool in the possibilities
select_this_tool = possibles[(i+1) % possibles.size()];
break;
}
}
}
}
toolbar_select_tool(app_get_toolbar(), select_this_tool);
break;
}
case Shortcut_ExecuteCommand: {
Command* command = shortcut->command;
// the screen shot is available in everywhere
if (strcmp(command->short_name(), CommandId::screen_shot) == 0) {
UIContext::instance()->execute_command(command, shortcut->params);
return true;
}
// all other keys are only available in the main-window
else {
JLink link;
JI_LIST_FOR_EACH(widget->children, link) {
Frame* child = reinterpret_cast<Frame*>(link->data);
/* there are a foreground window executing? */
if (child->is_foreground()) {
break;
}
/* is it the desktop and the top-window= */
else if (child->is_desktop() && child == app_get_top_window()) {
/* ok, so we can execute the command represented by the
pressed-key in the message... */
UIContext::instance()->execute_command(command, shortcut->params);
return true;
}
}
}
break;
}
}
break;
}
}
break;
}
return false;
}
// Slot for App::PaletteChange to regenerate graphics when the App palette is changed
static void on_palette_change_signal()
{
JWidget button;
JLink link;
// Regenerate the theme
ji_regen_theme();
// Fixup the icons
JI_LIST_FOR_EACH(icon_buttons, link) {
button = reinterpret_cast<JWidget>(link->data);
ji_generic_button_set_icon(button, get_gfx((size_t)button->user_data[3]));
}
}