/* * This is a plug-in for PIKA. * * Generates clickable image maps. * * Copyright (C) 1998-2006 Maurits Rijk m.rijk@chello.nl * * 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 3 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, see . */ #include "config.h" #include #include #include #include #include #include /* for keyboard values */ #include #include #include "imap_about.h" #include "imap_circle.h" #include "imap_commands.h" #include "imap_default_dialog.h" #include "imap_edit_area_info.h" #include "imap_file.h" #include "imap_grid.h" #include "imap_icons.h" #include "imap_main.h" #include "imap_menu.h" #include "imap_misc.h" #include "imap_object.h" #include "imap_polygon.h" #include "imap_preview.h" #include "imap_rectangle.h" #include "imap_selection.h" #include "imap_settings.h" #include "imap_source.h" #include "imap_statusbar.h" #include "imap_string.h" #include "libpika/stdplugins-intl.h" #define MAX_ZOOM_FACTOR 8 #define ZOOMED(x) (_zoom_factor * (x)) #define GET_REAL_COORD(x) ((x) / _zoom_factor) GType imap_get_type (void) G_GNUC_CONST; static void pika_imap_finalize (GObject *object); static GList * imap_query_procedures (PikaPlugIn *plug_in); static PikaProcedure * imap_create_procedure (PikaPlugIn *plug_in, const gchar *name); static PikaValueArray * imap_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, const PikaValueArray *args, gpointer run_data); static gint dialog (PikaImap *imap); static gint zoom_in (gpointer data); static gint zoom_out (gpointer data); static void on_app_activate (GApplication *gapp, gpointer user_data); static void window_destroy (GtkWidget *widget, gpointer data); G_DEFINE_TYPE (PikaImap, pika_imap, PIKA_TYPE_PLUG_IN) PIKA_MAIN (PIKA_TYPE_IMAP) DEFINE_STD_SET_I18N /* Global variables */ static MapInfo_t _map_info; static PreferencesData_t _preferences = {CSIM, TRUE, FALSE, TRUE, TRUE, FALSE, FALSE, TRUE, DEFAULT_UNDO_LEVELS, DEFAULT_MRU_SIZE}; static MRU_t *_mru; static PikaDrawable *_drawable; static GdkCursorType _cursor = GDK_TOP_LEFT_ARROW; static gboolean _show_url = TRUE; static gchar *_filename = NULL; static gchar *_image_name; static gint _image_width; static gint _image_height; static GtkWidget *_dlg; static Preview_t *_preview; static Selection_t *_selection; static StatusBar_t *_statusbar; static ObjectList_t *_shapes; static gint _zoom_factor = 1; static gboolean (*_button_press_func)(GtkWidget*, GdkEventButton*, gpointer); static gpointer _button_press_param; static int run_flag = 0; static const GActionEntry ACTIONS[] = { /* Sub-menu options */ { "open", do_file_open_dialog }, { "save", save }, { "save-as", do_file_save_as_dialog }, { "close", do_close }, { "quit", do_quit }, { "cut", do_cut }, { "copy", do_copy }, { "paste", do_paste }, { "select-all", do_select_all }, { "deselect-all", do_deselect_all }, { "clear", do_clear }, { "undo", do_undo }, { "redo", do_redo }, { "zoom-in", do_zoom_in }, { "zoom-out", do_zoom_out }, { "move-to-front", do_move_to_front }, { "send-to-back", do_send_to_back }, { "move-up", do_move_up }, { "move-down", do_move_down }, { "insert-point", polygon_insert_point }, { "delete-point", polygon_delete_point }, { "preferences", do_preferences_dialog }, { "source", do_source_dialog }, { "edit-area-info", do_edit_selected_shape }, { "edit-map-info", do_settings_dialog }, { "grid-settings", do_grid_settings_dialog }, { "use-pika-guides", do_use_pika_guides_dialog }, { "create-guides", do_create_guides_dialog }, { "contents", imap_help }, { "about", do_about_dialog }, /* Toggle Options */ { "grid", NULL, NULL, "false", toggle_grid }, { "area-list", NULL, NULL, "true", toggle_area_list }, /* Radio Options */ { "zoom", set_zoom_factor, "s", "'1'", NULL }, { "colormode", set_preview_color, "s", "'color'", NULL }, { "shape", set_func, "s", "'arrow'", NULL }, }; static void pika_imap_class_init (PikaImapClass *klass) { PikaPlugInClass *plug_in_class = PIKA_PLUG_IN_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = pika_imap_finalize; plug_in_class->query_procedures = imap_query_procedures; plug_in_class->create_procedure = imap_create_procedure; plug_in_class->set_i18n = STD_SET_I18N; } static void pika_imap_init (PikaImap *imap) { } static void pika_imap_finalize (GObject *object) { PikaImap *imap = PIKA_IMAP (object); G_OBJECT_CLASS (pika_imap_parent_class)->finalize (object); g_clear_object (&imap->builder); } static GList * imap_query_procedures (PikaPlugIn *plug_in) { return g_list_append (NULL, g_strdup (PLUG_IN_PROC)); } static PikaProcedure * imap_create_procedure (PikaPlugIn *plug_in, const gchar *name) { PikaProcedure *procedure = NULL; if (! strcmp (name, PLUG_IN_PROC)) { procedure = pika_image_procedure_new (plug_in, name, PIKA_PDB_PROC_TYPE_PLUGIN, imap_run, NULL, NULL); pika_procedure_set_image_types (procedure, "*"); pika_procedure_set_sensitivity_mask (procedure, PIKA_PROCEDURE_SENSITIVE_DRAWABLE); pika_procedure_set_menu_label (procedure, _("_Image Map...")); pika_procedure_add_menu_path (procedure, "/Filters/Web"); pika_procedure_set_documentation (procedure, _("Create a clickable imagemap"), NULL, name); pika_procedure_set_attribution (procedure, "Maurits Rijk", "Maurits Rijk", "1998-2005"); } return procedure; } static PikaValueArray * imap_run (PikaProcedure *procedure, PikaRunMode run_mode, PikaImage *image, gint n_drawables, PikaDrawable **drawables, const PikaValueArray *args, gpointer run_data) { PikaImap *imap; gegl_init (NULL, NULL); imap = PIKA_IMAP (pika_procedure_get_plug_in (procedure)); #if GLIB_CHECK_VERSION(2,74,0) imap->app = gtk_application_new (NULL, G_APPLICATION_DEFAULT_FLAGS); #else imap->app = gtk_application_new (NULL, G_APPLICATION_FLAGS_NONE); #endif imap->success = FALSE; imap->builder = gtk_builder_new_from_resource ("/technology.heckin/imagemap/imap-menu.ui"); if (n_drawables != 1) { GError *error = NULL; g_set_error (&error, PIKA_PLUG_IN_ERROR, 0, _("Procedure '%s' only works with one drawable."), pika_procedure_get_name (procedure)); return pika_procedure_new_return_values (procedure, PIKA_PDB_CALLING_ERROR, error); } else { _drawable = drawables[0]; } _image_name = pika_image_get_name (image); _image_width = pika_image_get_width (image); _image_height = pika_image_get_height (image); _map_info.color = pika_drawable_is_rgb (_drawable); imap->drawable = _drawable; if (run_mode != PIKA_RUN_INTERACTIVE) return pika_procedure_new_return_values (procedure, PIKA_PDB_CALLING_ERROR, NULL); g_signal_connect (imap->app, "activate", G_CALLBACK (on_app_activate), imap); g_application_run (G_APPLICATION (imap->app), 0, NULL); g_clear_object (&imap->app); if (! imap->success) return pika_procedure_new_return_values (procedure, PIKA_PDB_EXECUTION_ERROR, NULL); return pika_procedure_new_return_values (procedure, PIKA_PDB_SUCCESS, NULL); } GtkWidget* get_dialog (void) { return _dlg; } MRU_t* get_mru (void) { if (!_mru) _mru = mru_create(); return _mru; } MapInfo_t* get_map_info (void) { return &_map_info; } PreferencesData_t* get_preferences (void) { return &_preferences; } static void init_preferences (void) { ColorSelData_t *colors = &_preferences.colors; colors->normal_fg.red = 0; colors->normal_fg.green = 0xFFFF; colors->normal_fg.blue = 0; colors->normal_bg.red = 0; colors->normal_bg.green = 0; colors->normal_bg.blue = 0xFFFF; colors->selected_fg.red = 0xFFFF; colors->selected_fg.green = 0; colors->selected_fg.blue = 0; colors->selected_bg.red = 0; colors->selected_bg.green = 0; colors->selected_bg.blue = 0xFFFF; colors->interactive_fg.red = 0xFFFF; colors->interactive_fg.green = 0; colors->interactive_fg.blue = 0xFFFF; colors->interactive_bg.red = 0xFFFF; colors->interactive_bg.green = 0xFFFF; colors->interactive_bg.blue = 0; preferences_load (&_preferences); mru_set_size (_mru, _preferences.mru_size); command_list_set_undo_level (_preferences.undo_levels); } gint get_image_width (void) { return _image_width; } gint get_image_height (void) { return _image_height; } void set_busy_cursor (void) { preview_set_cursor(_preview, GDK_WATCH); } void remove_busy_cursor (void) { gdk_window_set_cursor(gtk_widget_get_window (_dlg), NULL); } static gint zoom_in (gpointer data) { if (_zoom_factor < MAX_ZOOM_FACTOR) { set_zoom (_zoom_factor + 1); menu_set_zoom (data, _zoom_factor); } return _zoom_factor; } static gint zoom_out (gpointer data) { if (_zoom_factor > 1) { set_zoom (_zoom_factor - 1); menu_set_zoom (data, _zoom_factor); } return _zoom_factor; } void set_zoom(gint zoom_factor) { set_busy_cursor(); _zoom_factor = zoom_factor; preview_zoom(_preview, zoom_factor); statusbar_set_zoom(_statusbar, zoom_factor); remove_busy_cursor(); } gint get_real_coord(gint coord) { return GET_REAL_COORD(coord); } void draw_line(cairo_t *cr, gint x1, gint y1, gint x2, gint y2) { cairo_move_to (cr, ZOOMED (x1) + .5, ZOOMED (y1) + .5); cairo_line_to (cr, ZOOMED (x2) + .5, ZOOMED (y2) + .5); cairo_stroke (cr); } void draw_rectangle(cairo_t *cr, gboolean filled, gint x, gint y, gint width, gint height) { cairo_rectangle (cr, ZOOMED (x) + (filled ? 0. : .5), ZOOMED (y) + (filled ? 0. : .5), ZOOMED (width), ZOOMED (height)); if (filled) cairo_fill (cr); else cairo_stroke (cr); } void draw_circle(cairo_t *cr, gint x, gint y, gint r) { cairo_arc (cr, ZOOMED (x), ZOOMED (y), ZOOMED (r), 0., 2 * G_PI); cairo_stroke (cr); } void draw_polygon(cairo_t *cr, GList *list) { GList *p; for (p = list; p; p = p->next) { GdkPoint *src = (GdkPoint*) p->data; cairo_line_to (cr, ZOOMED (src->x) + .5, ZOOMED (src->y) + .5); } cairo_close_path (cr); cairo_stroke (cr); } void set_preview_color (GSimpleAction *action, GVariant *new_state, gpointer user_data) { gchar *str; str = g_strdup_printf ("%s", g_variant_get_string (new_state, NULL)); if (! strcmp (str, "gray")) _map_info.show_gray = 1; else _map_info.show_gray = 0; g_free (str); g_simple_action_set_state (action, new_state); set_zoom (_zoom_factor); } void preview_redraw(void) { gtk_widget_queue_draw(_preview->preview); } void set_zoom_factor (GSimpleAction *action, GVariant *new_state, gpointer user_data) { gchar *str; gint factor = 1; str = g_strdup_printf ("%s", g_variant_get_string (new_state, NULL)); factor = atoi (str); g_free (str); set_zoom (factor); g_simple_action_set_state (action, new_state); } const gchar * get_image_name(void) { return _image_name; } const char* get_filename(void) { return _filename; } static gboolean arrow_on_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data) { if (gdk_event_triggers_context_menu ((GdkEvent *) event)) { do_popup_menu (event, data); } else if (event->button == 1) { if (event->type == GDK_2BUTTON_PRESS) edit_shape ((gint) event->x, (gint) event->y); else select_shape (widget, event); } return FALSE; } static void set_arrow_func (gpointer data) { _button_press_func = arrow_on_button_press; _button_press_param = data; _cursor = GDK_TOP_LEFT_ARROW; } static void set_object_func (gboolean (*func)(GtkWidget*, GdkEventButton*, gpointer), gpointer param) { _button_press_func = func; _button_press_param = param; _cursor = GDK_CROSSHAIR; } void set_func (GSimpleAction *action, GVariant *new_state, gpointer user_data) { gchar *str; gint value = 0; str = g_strdup_printf ("%s", g_variant_get_string (new_state, NULL)); if (! strcmp (str, "arrow")) value = 0; else if (! strcmp (str, "rectangle")) value = 1; else if (! strcmp (str, "circle")) value = 2; else if (! strcmp (str, "polygon")) value = 3; g_free (str); switch (value) { case 0: set_arrow_func (user_data); break; case 1: set_object_func (object_on_button_press, get_rectangle_factory); break; case 2: set_object_func (object_on_button_press, get_circle_factory); break; case 3: set_object_func (object_on_button_press, get_polygon_factory); break; default: break; } g_simple_action_set_state (action, new_state); } void add_shape(Object_t *obj) { object_list_append(_shapes, obj); } ObjectList_t* get_shapes(void) { return _shapes; } void update_shape(Object_t *obj) { object_list_update(_shapes, obj); } void do_edit_selected_shape (GSimpleAction *action, GVariant *parameter, gpointer user_data) { object_list_edit_selected(_shapes); } void do_popup_menu (GdkEventButton *event, gpointer data) { gint x = GET_REAL_COORD((gint) event->x); gint y = GET_REAL_COORD((gint) event->y); Object_t *obj = object_list_find (_shapes, x, y); if (obj) obj->class->do_popup (obj, event, data); else do_main_popup_menu (event, data); } static void set_all_sensitivities (gpointer data) { gint count = object_list_nr_selected (_shapes); menu_shapes_selected (count, data); } static void main_set_title (const char *filename) { char *title, *p; g_strreplace(&_filename, filename); p = filename ? g_filename_display_basename (filename) : (gchar *) _(""); title = g_strdup_printf("%s - Image Map", p); if (filename) g_free (p); gtk_window_set_title(GTK_WINDOW(_dlg), title); g_free(title); } void main_set_dimension(gint width, gint height) { statusbar_set_dimension(_statusbar, width / _zoom_factor, height / _zoom_factor); } void main_clear_dimension (void) { statusbar_clear_dimension (_statusbar); } void show_url (void) { _show_url = TRUE; } void hide_url (void) { _show_url = FALSE; statusbar_clear_status(_statusbar); } void select_shape (GtkWidget *widget, GdkEventButton *event) { Object_t *obj; gint x = GET_REAL_COORD ((gint) event->x); gint y = GET_REAL_COORD ((gint) event->y); MoveSashFunc_t sash_func; obj = object_list_near_sash (_shapes, x, y, &sash_func); if (obj) { /* Start resizing */ Command_t *command = move_sash_command_new(widget, obj, x, y, sash_func); command_execute(command); } else { Command_t *command; obj = object_list_find(_shapes, x, y); if (obj) { if (event->state & GDK_SHIFT_MASK) { if (obj->selected) command = unselect_command_new(obj); else command = select_command_new(obj); } else { /* No Shift key pressed */ if (obj->selected) { command = unselect_all_command_new(_shapes, obj); } else { Command_t *sub_command; command = subcommand_start(NULL); sub_command = unselect_all_command_new(_shapes, NULL); command_add_subcommand(command, sub_command); sub_command = select_command_new(obj); command_add_subcommand(command, sub_command); command_set_name(command, sub_command->name); subcommand_end(); } } command_execute(command); command = move_command_new(_preview, obj, x, y); command_execute(command); } else { /* Start selection rectangle */ command = select_region_command_new(widget, _shapes, x, y); command_execute(command); } } } void edit_shape(gint x, gint y) { Object_t *obj; x = GET_REAL_COORD(x); y = GET_REAL_COORD(y); obj = object_list_find(_shapes, x, y); if (obj) { object_select(obj); object_edit(obj, TRUE); } } void do_zoom_in (GSimpleAction *action, GVariant *parameter, gpointer user_data) { gint factor = zoom_in (user_data); menu_set_zoom_sensitivity (user_data, factor); } void do_zoom_out (GSimpleAction *action, GVariant *parameter, gpointer user_data) { gint factor = zoom_out (user_data); menu_set_zoom_sensitivity (user_data, factor); } void draw_shapes(cairo_t *cr) { object_list_draw(_shapes, cr); } static void clear_map_info (void) { const gchar *author = g_get_real_name(); if (!*author) author = g_get_user_name(); g_strreplace(&_map_info.image_name, _image_name); g_strreplace(&_map_info.title, "map"); g_strreplace(&_map_info.author, author); g_strreplace(&_map_info.default_url, ""); g_strreplace(&_map_info.description, ""); _map_info.map_format = _preferences.default_map_type; _map_info.show_gray = FALSE; } static void do_data_changed_dialog (void (*continue_cb)(gpointer), gpointer param) { GtkWidget *dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("Some data has been changed!")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Do you really want to discard your changes?")); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) continue_cb (param); gtk_widget_destroy (dialog); } static void check_if_changed (void (*func)(gpointer), gpointer param) { if (object_list_get_changed (_shapes)) do_data_changed_dialog (func, param); else func (param); } static void close_current (gpointer data) { selection_freeze (_selection); object_list_remove_all (_shapes); selection_thaw (_selection); clear_map_info (); main_set_title (NULL); set_all_sensitivities (data); preview_redraw (); object_list_clear_changed (_shapes); command_list_remove_all (); } static void really_close (gpointer data) { close_current (data); } void do_close (GSimpleAction *action, GVariant *parameter, gpointer user_data) { check_if_changed (really_close, user_data); } static void really_quit (gpointer data) { PikaImap *imap = NULL; if (data) { imap = PIKA_IMAP (data); imap->success = TRUE; } preferences_save (&_preferences); run_flag = 1; window_destroy (_dlg, imap); } void do_quit (GSimpleAction *action, GVariant *parameter, gpointer user_data) { check_if_changed (really_quit, user_data); } void do_undo (GSimpleAction *action, GVariant *parameter, gpointer user_data) { selection_freeze (_selection); last_command_undo (); selection_thaw (_selection); preview_redraw (); } void do_redo (GSimpleAction *action, GVariant *parameter, gpointer user_data) { selection_freeze (_selection); last_command_redo (); selection_thaw (_selection); preview_redraw (); } void save (GSimpleAction *action, GVariant *parameter, gpointer user_data) { if (_filename) save_as (_filename); else do_file_save_as_dialog (NULL, NULL, NULL); } static void write_cern_comment (gpointer param, OutputFunc_t output) { output(param, "rect (4096,4096) (4096,4096) imap:#$"); } static void save_as_cern(gpointer param, OutputFunc_t output) { char *p; gchar *description; gchar *next_token; write_cern_comment(param, output); output(param, "-:Image map file created by PIKA Image Map plug-in\n"); write_cern_comment(param, output); output(param, "-:PIKA Image Map plug-in by Maurits Rijk\n"); write_cern_comment(param, output); output(param, "-:Please do not edit lines starting with \"#$\"\n"); write_cern_comment(param, output); output(param, "VERSION:2.3\n"); write_cern_comment(param, output); output(param, "TITLE:%s\n", _map_info.title); write_cern_comment(param, output); output(param, "AUTHOR:%s\n", _map_info.author); write_cern_comment(param, output); output(param, "FORMAT:cern\n"); description = g_strdup(_map_info.description); next_token = description; for (p = strtok (next_token, "\n"); p; p = strtok(NULL, "\n")) { write_cern_comment(param, output); output(param, "DESCRIPTION:%s\n", p); } g_free(description); if (*_map_info.default_url) output(param, "default %s\n", _map_info.default_url); object_list_write_cern(_shapes, param, output); } static void save_as_csim(gpointer param, OutputFunc_t output) { char *p; gchar *description; output(param, "\n\n", _map_info.image_name, _image_width, _image_height, _map_info.title); output(param, "\n", _map_info.title); output(param, "\n"); output(param, "\n"); output(param, "\n"); output(param, "\n"); output(param, "\n", _map_info.author); description = g_strdup(_map_info.description); for (p = strtok(description, "\n"); p; p = strtok(NULL, "\n")) output(param, "\n", p); g_free(description); object_list_write_csim(_shapes, param, output); if (*_map_info.default_url) output(param, "\n", _map_info.default_url); output(param, "\n"); } static void save_as_ncsa(gpointer param, OutputFunc_t output) { char *p; gchar *description; output(param, "#$-:Image map file created by PIKA Image Map plug-in\n"); output(param, "#$-:PIKA Image Map plug-in by Maurits Rijk\n"); output(param, "#$-:Please do not edit lines starting with \"#$\"\n"); output(param, "#$VERSION:2.3\n"); output(param, "#$TITLE:%s\n", _map_info.title); output(param, "#$AUTHOR:%s\n", _map_info.author); output(param, "#$FORMAT:ncsa\n"); description = g_strdup(_map_info.description); for (p = strtok(description, "\n"); p; p = strtok(NULL, "\n")) output(param, "#$DESCRIPTION:%s\n", p); g_free(description); if (*_map_info.default_url) output(param, "default %s\n", _map_info.default_url); object_list_write_ncsa(_shapes, param, output); } static void save_to_file (gpointer param, const char *format, ...) G_GNUC_PRINTF(2,3); static void save_to_file (gpointer param, const char* format, ...) { va_list ap; va_start (ap, format); vfprintf ((FILE*) param, format, ap); va_end(ap); } void dump_output (gpointer param, OutputFunc_t output) { if (_map_info.map_format == NCSA) save_as_ncsa(param, output); else if (_map_info.map_format == CERN) save_as_cern(param, output); else if (_map_info.map_format == CSIM) save_as_csim(param, output); } void save_as (const gchar *filename) { FILE *out = g_fopen(filename, "w"); if (out) { dump_output(out, save_to_file); fclose(out); statusbar_set_status(_statusbar, _("File \"%s\" saved."), filename); main_set_title(filename); object_list_clear_changed(_shapes); } else { do_file_error_dialog( _("Couldn't save file:"), filename); } } static void do_image_size_changed_dialog (void) { GtkWidget *dialog = gtk_message_dialog_new_with_markup (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s\n\n%s", _("Image size has changed."), _("Resize area's?")); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) { gint per_x = _image_width * 100 / _map_info.old_image_width; gint per_y = _image_height * 100 / _map_info.old_image_height; object_list_resize(_shapes, per_x, per_y); } preview_redraw(); gtk_widget_destroy (dialog); } static void really_load (gpointer data) { PikaImap *imap = PIKA_IMAP (data); gchar *filename = imap->tmp_filename; close_current (imap); selection_freeze(_selection); _map_info.old_image_width = _image_width; _map_info.old_image_height = _image_height; if (load_csim(filename)) { _map_info.map_format = CSIM; if (_image_width != _map_info.old_image_width || _image_height != _map_info.old_image_height) { do_image_size_changed_dialog(); } } else if (load_ncsa(filename)) { _map_info.map_format = NCSA; } else if (load_cern(filename)) { _map_info.map_format = CERN; } else { do_file_error_dialog ( _("Couldn't read file:"), filename); selection_thaw(_selection); close_current (imap); return; } mru_set_first(_mru, filename); selection_thaw (_selection); main_set_title (filename); object_list_clear_changed (_shapes); preview_redraw (); } void load (const gchar *filename, gpointer data) { PikaImap *imap = PIKA_IMAP (data); g_strreplace (&imap->tmp_filename, filename); check_if_changed (really_load, imap); } void toggle_area_list (GSimpleAction *action, GVariant *new_state, gpointer user_data) { selection_toggle_visibility(_selection); g_simple_action_set_state (action, new_state); } static gboolean close_callback (GtkWidget *widget, gpointer data) { do_quit (NULL, NULL, NULL); return TRUE; } static gboolean preview_move (GtkWidget *widget, GdkEventMotion *event) { gint x = GET_REAL_COORD((gint) event->x); gint y = GET_REAL_COORD((gint) event->y); static Object_t *prev_obj = NULL; Object_t *obj = object_list_find(_shapes, x, y); statusbar_set_xy(_statusbar, x, y); if (obj != prev_obj) { prev_obj = obj; if (obj && _show_url) { statusbar_set_status(_statusbar, _("URL: %s"), obj->url); } else { statusbar_clear_status(_statusbar); } } #ifdef _NOT_READY_YET_ if (!obj) { if (grid_near_x(x)) { preview_set_cursor(_preview, GDK_SB_H_DOUBLE_ARROW); } else if (grid_near_y(y)) { preview_set_cursor(_preview, GDK_SB_V_DOUBLE_ARROW); } else { preview_set_cursor(_preview, _cursor); } } #endif return FALSE; } static void preview_enter(GtkWidget *widget, GdkEventCrossing *event) { preview_set_cursor(_preview, _cursor); } static void preview_leave(GtkWidget *widget, GdkEventCrossing *event) { gdk_window_set_cursor(gtk_widget_get_window (_dlg), NULL); statusbar_clear_xy(_statusbar); } static gboolean button_press (GtkWidget* widget, GdkEventButton* event, gpointer data) { if (_button_press_func) return _button_press_func (widget, event, _button_press_param); return FALSE; } /* A few global vars for key movement */ static guint _timeout; static guint _keyval; static gint _dx, _dy; static void move_sash_selected_objects (gint dx, gint dy, gboolean fast) { if (fast) { dx *= 5; dy *= 5; } object_list_move_sash_selected (_shapes, dx, dy); preview_redraw (); } static void move_selected_objects (gint dx, gint dy, gboolean fast) { if (fast) { dx *= 5; dy *= 5; } _dx += dx; _dy += dy; object_list_move_selected(_shapes, dx, dy); preview_redraw (); } static gboolean key_timeout_cb(gpointer data) { switch (_keyval) { case GDK_KEY_Left: case GDK_KEY_Right: case GDK_KEY_Up: case GDK_KEY_Down: command_list_add(move_selected_command_new(_shapes, _dx, _dy)); _dx = _dy = 0; break; } preview_redraw(); _timeout = 0; return FALSE; } static gboolean key_press_cb(GtkWidget *widget, GdkEventKey *event) { gboolean handled = FALSE; gboolean shift = event->state & GDK_SHIFT_MASK; gboolean ctrl = event->state & GDK_CONTROL_MASK; Command_t *command; if (_timeout) g_source_remove(_timeout); _timeout = 0; switch (event->keyval) { case GDK_KEY_Left: if (ctrl) move_sash_selected_objects(-1, 0, shift); else move_selected_objects(-1, 0, shift); handled = TRUE; break; case GDK_KEY_Right: if (ctrl) move_sash_selected_objects(1, 0, shift); else move_selected_objects(1, 0, shift); handled = TRUE; break; case GDK_KEY_Up: if (ctrl) move_sash_selected_objects(0, -1, shift); else move_selected_objects(0, -1, shift); handled = TRUE; break; case GDK_KEY_Down: if (ctrl) move_sash_selected_objects(0, 1, shift); else move_selected_objects(0, 1, shift); handled = TRUE; break; case GDK_KEY_Tab: if (shift) command = select_prev_command_new(_shapes); else command = select_next_command_new(_shapes); command_execute(command); handled = TRUE; break; } if (handled) g_signal_stop_emission_by_name(widget, "key-press-event"); return handled; } static gboolean key_release_cb (GtkWidget *widget, GdkEventKey *event) { _keyval = event->keyval; _timeout = g_timeout_add(250, key_timeout_cb, NULL); return FALSE; } static void geometry_changed (Object_t *obj, gpointer data) { preview_redraw(); } static void data_changed (Object_t *obj, gpointer data) { preview_redraw (); set_all_sensitivities (data); } static void data_selected (Object_t *obj, gpointer data) { set_all_sensitivities (data); } void imap_help (GSimpleAction *action, GVariant *parameter, gpointer user_data) { pika_standard_help_func ("plug-in-imagemap", NULL); } void do_cut (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (cut_command_new (_shapes)); } void do_copy (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (copy_command_new (_shapes)); } void do_paste (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (paste_command_new (_shapes)); } void do_select_all (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (select_all_command_new (_shapes)); } void do_deselect_all (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (unselect_all_command_new (_shapes, NULL)); } void do_clear (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (clear_command_new(_shapes)); } void do_move_up (GSimpleAction *action, GVariant *parameter, gpointer user_data) { /* Fix me! Command_t *command = object_up_command_new(_current_obj->list, _current_obj); command_execute(command); */ } void do_move_down (GSimpleAction *action, GVariant *parameter, gpointer user_data) { /* Fix me! Command_t *command = object_down_command_new(_current_obj->list, _current_obj); command_execute(command); */ } void do_move_to_front (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute(move_to_front_command_new(_shapes)); } void do_send_to_back (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute(send_to_back_command_new(_shapes)); } void do_use_pika_guides_dialog (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (pika_guides_command_new (_shapes, _drawable)); } void do_create_guides_dialog (GSimpleAction *action, GVariant *parameter, gpointer user_data) { command_execute (guides_command_new (_shapes)); } static Command_t* factory_move_up(void) { return move_up_command_new(_shapes); } static Command_t* factory_move_down(void) { return move_down_command_new(_shapes); } static gint dialog (PikaImap *imap) { GtkWidget *hbox; GtkWidget *menubar; GMenuModel *model; GtkWidget *toolbar; GtkWidget *main_vbox; GtkWidget *tools; pika_ui_init (PLUG_IN_BINARY); set_arrow_func (imap); _shapes = make_object_list(); _dlg = imap->dlg = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_resizable (GTK_WINDOW (imap->dlg), TRUE); main_set_title (NULL); pika_help_connect (imap->dlg, pika_standard_help_func, PLUG_IN_PROC, NULL, NULL); gtk_window_set_position (GTK_WINDOW (imap->dlg), GTK_WIN_POS_MOUSE); gtk_window_set_application (GTK_WINDOW (_dlg), imap->app); pika_window_set_transient (GTK_WINDOW (imap->dlg)); g_action_map_add_action_entries (G_ACTION_MAP (imap->app), ACTIONS, G_N_ELEMENTS (ACTIONS), imap); g_signal_connect (imap->dlg, "delete-event", G_CALLBACK (close_callback), NULL); g_signal_connect (imap->dlg, "key-press-event", G_CALLBACK (key_press_cb), NULL); g_signal_connect (imap->dlg, "key-release-event", G_CALLBACK (key_release_cb), NULL); g_signal_connect (imap->dlg, "destroy", G_CALLBACK (window_destroy), NULL); main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (imap->dlg), main_vbox); gtk_widget_show (main_vbox); init_icons(); /* Create menu */ make_menu (imap); model = G_MENU_MODEL (gtk_builder_get_object (imap->builder, "imap-menubar")); menubar = gtk_menu_bar_new_from_model (model); gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, FALSE, 0); gtk_widget_show (menubar); /* Create toolbar */ toolbar = gtk_toolbar_new (); add_tool_button (toolbar, "app.open", PIKA_ICON_DOCUMENT_OPEN, _("Open"), _("Open")); add_tool_button (toolbar, "app.save", PIKA_ICON_DOCUMENT_SAVE, _("Save"), _("Save")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.preferences", PIKA_ICON_PREFERENCES_SYSTEM, _("Preferences"), _("Preferences")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.undo", PIKA_ICON_EDIT_UNDO, _("Undo"), _("Undo")); add_tool_button (toolbar, "app.redo", PIKA_ICON_EDIT_REDO, _("Redo"), _("Redo")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.cut", PIKA_ICON_EDIT_CUT, _("Cut"), _("Cut")); add_tool_button (toolbar, "app.copy", PIKA_ICON_EDIT_COPY, _("Copy"), _("Copy")); add_tool_button (toolbar, "app.paste", PIKA_ICON_EDIT_PASTE, _("Paste"), _("Paste")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.zoom-in", PIKA_ICON_ZOOM_IN, _("Zoom In"), _("Zoom In")); add_tool_button (toolbar, "app.zoom-out", PIKA_ICON_ZOOM_OUT, _("Zoom Out"), _("Zoom Out")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.edit-map-info", PIKA_ICON_DIALOG_INFORMATION, _("Edit Map Info"), _("Edit Map Info")); add_tool_separator (toolbar, FALSE); add_tool_button (toolbar, "app.move-to-front", IMAP_TO_FRONT, _("Move Area to Front"), _("Move Area to Front")); add_tool_button (toolbar, "app.send-to-back", IMAP_TO_BACK, _("Move Area to Bottom"), _("Move Area to Bottom")); add_tool_separator (toolbar, FALSE); imap->grid_toggle = add_toggle_button (toolbar, "app.grid", PIKA_ICON_GRID, _("Grid"), _("Grid")); add_tool_separator (toolbar, FALSE); gtk_container_set_border_width (GTK_CONTAINER (toolbar), 0); gtk_box_pack_start (GTK_BOX (main_vbox), toolbar, FALSE, FALSE, 0); gtk_widget_show (toolbar); /* Dialog area */ hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 1); gtk_box_pack_start (GTK_BOX (main_vbox), hbox, TRUE, TRUE, 0); gtk_widget_show (hbox); /* Pointer tools area */ tools = gtk_toolbar_new (); add_toggle_button (tools, "app.shape::arrow", PIKA_ICON_CURSOR, _("Arrow"), _("Select Existing Area")); add_toggle_button (tools, "app.shape::rectangle", IMAP_RECTANGLE, _("Rectangle"), _("Define Rectangle area")); add_toggle_button (tools, "app.shape::circle", IMAP_CIRCLE, _("Circle"), _("Define Circle/Oval area")); add_toggle_button (tools, "app.shape::polygon", IMAP_POLYGON, _("Polygon"), _("Define Polygon area")); add_tool_separator (tools, FALSE); add_tool_button (tools, "app.edit-area-info", PIKA_ICON_EDIT, _("Edit Area Info..."), _("Edit selected area info")); gtk_orientable_set_orientation (GTK_ORIENTABLE (tools), GTK_ORIENTATION_VERTICAL); gtk_toolbar_set_style (GTK_TOOLBAR (tools), GTK_TOOLBAR_ICONS); gtk_container_set_border_width (GTK_CONTAINER (tools), 0); gtk_widget_show (tools); gtk_box_pack_start (GTK_BOX (hbox), tools, FALSE, FALSE, 0); _preview = make_preview (imap->drawable, imap); g_signal_connect(_preview->preview, "motion-notify-event", G_CALLBACK (preview_move), NULL); g_signal_connect(_preview->preview, "enter-notify-event", G_CALLBACK (preview_enter), NULL); g_signal_connect(_preview->preview, "leave-notify-event", G_CALLBACK (preview_leave), NULL); g_signal_connect(_preview->preview, "button-press-event", G_CALLBACK (button_press), NULL); gtk_box_pack_start (GTK_BOX (hbox), _preview->window, TRUE, TRUE, 0); object_list_add_geometry_cb (_shapes, geometry_changed, NULL); object_list_add_update_cb (_shapes, data_changed, imap); object_list_add_add_cb (_shapes, data_changed, imap); object_list_add_remove_cb (_shapes, data_changed, imap); object_list_add_move_cb (_shapes, data_changed, imap); object_list_add_select_cb (_shapes, data_selected, imap); /* Selection */ _selection = make_selection (_shapes, imap); selection_set_move_up_command (_selection, factory_move_up); selection_set_move_down_command (_selection, factory_move_down); gtk_box_pack_start (GTK_BOX (hbox), _selection->container, FALSE, FALSE, 0); _statusbar = make_statusbar (main_vbox, imap->dlg); statusbar_set_zoom (_statusbar, 1); gtk_widget_show (imap->dlg); _mru = mru_create (); init_preferences (); clear_map_info (); imap->success = TRUE; return run_flag; } static void on_app_activate (GApplication *gapp, gpointer user_data) { PikaImap *imap = PIKA_IMAP (user_data); dialog (imap); gtk_application_set_accels_for_action (imap->app, "app.save-as", (const char*[]) { "S", NULL }); gtk_application_set_accels_for_action (imap->app, "app.close", (const char*[]) { "w", NULL }); gtk_application_set_accels_for_action (imap->app, "app.quit", (const char*[]) { "q", NULL }); gtk_application_set_accels_for_action (imap->app, "app.cut", (const char*[]) { "x", NULL }); gtk_application_set_accels_for_action (imap->app, "app.copy", (const char*[]) { "c", NULL }); gtk_application_set_accels_for_action (imap->app, "app.paste", (const char*[]) { "p", NULL }); gtk_application_set_accels_for_action (imap->app, "app.select-all", (const char*[]) { "A", NULL }); gtk_application_set_accels_for_action (imap->app, "app.deselect-all", (const char*[]) { "A", NULL }); gtk_application_set_accels_for_action (imap->app, "app.zoom-in", (const char*[]) { "plus", NULL }); gtk_application_set_accels_for_action (imap->app, "app.zoom-out", (const char*[]) { "minus", NULL }); } static void window_destroy (GtkWidget *widget, gpointer data) { if (data) { PikaImap *imap = PIKA_IMAP (data); gtk_application_remove_window (imap->app, GTK_WINDOW (imap->dlg)); } else { gtk_widget_destroy (_dlg); } } GtkWidget * add_tool_button (GtkWidget *toolbar, const char *action, const char *icon, const char *label, const char *tooltip) { GtkWidget *tool_icon; GtkToolItem *tool_button; tool_icon = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON); gtk_widget_show (GTK_WIDGET (tool_icon)); tool_button = gtk_tool_button_new (tool_icon, label); gtk_widget_show (GTK_WIDGET (tool_button)); gtk_tool_item_set_tooltip_text (tool_button, tooltip); gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (tool_button), action); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), tool_button, -1); return GTK_WIDGET (tool_button); } GtkWidget * add_toggle_button (GtkWidget *toolbar, const char *action, const char *icon, const char *label, const char *tooltip) { GtkWidget *tool_icon; GtkToolItem *toggle_tool_button; tool_icon = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON); gtk_widget_show (GTK_WIDGET (tool_icon)); toggle_tool_button = gtk_toggle_tool_button_new (); gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (toggle_tool_button), tool_icon); gtk_tool_button_set_label (GTK_TOOL_BUTTON (toggle_tool_button), label); gtk_widget_show (GTK_WIDGET (toggle_tool_button)); gtk_tool_item_set_tooltip_text (toggle_tool_button, tooltip); gtk_actionable_set_detailed_action_name (GTK_ACTIONABLE (toggle_tool_button), action); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toggle_tool_button, -1); return GTK_WIDGET (toggle_tool_button); } void add_tool_separator (GtkWidget *toolbar, gboolean expand) { GtkToolItem *item; item = gtk_separator_tool_item_new (); gtk_toolbar_insert (GTK_TOOLBAR (toolbar), item, -1); gtk_separator_tool_item_set_draw (GTK_SEPARATOR_TOOL_ITEM (item), FALSE); gtk_tool_item_set_expand (item, expand); gtk_widget_show (GTK_WIDGET (item)); }