|
GnuCash 2.4.99
|
00001 /* 00002 * gnc-plugin-menu-additions.c -- 00003 * Copyright (C) 2005 David Hampton hampton@employees.org> 00004 * 00005 * From: 00006 * gnc-menu-extensions.c -- functions to build dynamic menus 00007 * Copyright (C) 1999 Rob Browning 00008 * 00009 * This program is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU General Public License as 00011 * published by the Free Software Foundation; either version 2 of 00012 * the License, or (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, contact: 00021 * 00022 * Free Software Foundation Voice: +1-617-542-5942 00023 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 00024 * Boston, MA 02110-1301, USA gnu@gnu.org 00025 */ 00026 00036 #include "config.h" 00037 00038 #include <gtk/gtk.h> 00039 #include <string.h> 00040 #include "swig-runtime.h" 00041 00042 #include "guile-util.h" 00043 #include "gnc-engine.h" 00044 #include "gnc-main-window.h" 00045 #include "gnc-plugin-menu-additions.h" 00046 #include "gnc-window.h" 00047 #include "gnc-gconf-utils.h" 00048 #include "gnc-ui.h" 00049 #include "gnc-menu-extensions.h" 00050 00051 static GObjectClass *parent_class = NULL; 00052 00053 static void gnc_plugin_menu_additions_class_init (GncPluginMenuAdditionsClass *klass); 00054 static void gnc_plugin_menu_additions_init (GncPluginMenuAdditions *plugin); 00055 static void gnc_plugin_menu_additions_finalize (GObject *object); 00056 00057 static void gnc_plugin_menu_additions_add_to_window (GncPlugin *plugin, GncMainWindow *window, GQuark type); 00058 static void gnc_plugin_menu_additions_remove_from_window (GncPlugin *plugin, GncMainWindow *window, GQuark type); 00059 00060 /* Command callbacks */ 00061 00062 /* This static indicates the debugging module that this .o belongs to. */ 00063 static QofLogModule log_module = GNC_MOD_GUI; 00064 00065 #define PLUGIN_ACTIONS_NAME "gnc-plugin-menu-additions-actions" 00066 00068 typedef struct GncPluginMenuAdditionsPrivate 00069 { 00070 gpointer dummy; 00071 } GncPluginMenuAdditionsPrivate; 00072 00073 #define GNC_PLUGIN_MENU_ADDITIONS_GET_PRIVATE(o) \ 00074 (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNC_TYPE_PLUGIN_MENU_ADDITIONS, GncPluginMenuAdditionsPrivate)) 00075 00076 00079 typedef struct _GncPluginMenuAdditionsPerWindow 00080 { 00084 GncMainWindow *window; 00085 GtkUIManager *ui_manager; 00086 GtkActionGroup *group; 00087 gint merge_id; 00088 } GncPluginMenuAdditionsPerWindow; 00089 00090 /************************************************************ 00091 * Object Implementation * 00092 ************************************************************/ 00093 00094 GType 00095 gnc_plugin_menu_additions_get_type (void) 00096 { 00097 static GType gnc_plugin_menu_additions_type = 0; 00098 00099 if (gnc_plugin_menu_additions_type == 0) 00100 { 00101 static const GTypeInfo our_info = 00102 { 00103 sizeof (GncPluginMenuAdditionsClass), 00104 NULL, /* base_init */ 00105 NULL, /* base_finalize */ 00106 (GClassInitFunc) gnc_plugin_menu_additions_class_init, 00107 NULL, /* class_finalize */ 00108 NULL, /* class_data */ 00109 sizeof (GncPluginMenuAdditions), 00110 0, 00111 (GInstanceInitFunc) gnc_plugin_menu_additions_init 00112 }; 00113 00114 gnc_plugin_menu_additions_type = g_type_register_static (GNC_TYPE_PLUGIN, 00115 "GncPluginMenuAdditions", 00116 &our_info, 0); 00117 } 00118 00119 return gnc_plugin_menu_additions_type; 00120 } 00121 00122 static void 00123 gnc_plugin_menu_additions_class_init (GncPluginMenuAdditionsClass *klass) 00124 { 00125 GObjectClass *object_class = G_OBJECT_CLASS (klass); 00126 GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass); 00127 00128 parent_class = g_type_class_peek_parent (klass); 00129 00130 object_class->finalize = gnc_plugin_menu_additions_finalize; 00131 00132 /* plugin info */ 00133 plugin_class->plugin_name = GNC_PLUGIN_MENU_ADDITIONS_NAME; 00134 00135 /* function overrides */ 00136 plugin_class->add_to_window = gnc_plugin_menu_additions_add_to_window; 00137 plugin_class->remove_from_window = gnc_plugin_menu_additions_remove_from_window; 00138 00139 g_type_class_add_private(klass, sizeof(GncPluginMenuAdditionsPrivate)); 00140 } 00141 00142 static void 00143 gnc_plugin_menu_additions_init (GncPluginMenuAdditions *plugin) 00144 { 00145 ENTER("plugin %p", plugin); 00146 LEAVE(""); 00147 } 00148 00149 static void 00150 gnc_plugin_menu_additions_finalize (GObject *object) 00151 { 00152 GncPluginMenuAdditions *plugin; 00153 GncPluginMenuAdditionsPrivate *priv; 00154 00155 g_return_if_fail (GNC_IS_PLUGIN_MENU_ADDITIONS (object)); 00156 00157 ENTER("plugin %p", object); 00158 plugin = GNC_PLUGIN_MENU_ADDITIONS (object); 00159 priv = GNC_PLUGIN_MENU_ADDITIONS_GET_PRIVATE (plugin); 00160 00161 G_OBJECT_CLASS (parent_class)->finalize (object); 00162 LEAVE(""); 00163 } 00164 00165 00166 /* Create a new menu_additions plugin. This plugin attaches the menu 00167 * items from Scheme code to any window that is opened. 00168 * 00169 * @return A pointer to the new object. 00170 */ 00171 GncPlugin * 00172 gnc_plugin_menu_additions_new (void) 00173 { 00174 GncPlugin *plugin_page = NULL; 00175 00176 ENTER(""); 00177 plugin_page = GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_MENU_ADDITIONS, NULL)); 00178 LEAVE("plugin %p", plugin_page); 00179 return plugin_page; 00180 } 00181 00182 /************************************************************ 00183 * Plugin Function Implementation * 00184 ************************************************************/ 00185 00186 static SCM 00187 gnc_main_window_to_scm (GncMainWindow *window) 00188 { 00189 static swig_type_info * main_window_type = NULL; 00190 00191 if (!window) 00192 return SCM_BOOL_F; 00193 00194 if (!main_window_type) 00195 main_window_type = SWIG_TypeQuery("_p_GncMainWindow"); 00196 00197 return SWIG_NewPointerObj(window, main_window_type, 0); 00198 } 00199 00200 00212 static void 00213 gnc_plugin_menu_additions_action_cb (GtkAction *action, 00214 GncMainWindowActionData *data) 00215 { 00216 00217 g_return_if_fail(GTK_IS_ACTION(action)); 00218 g_return_if_fail(data != NULL); 00219 00220 gnc_extension_invoke_cb(data->data, gnc_main_window_to_scm(data->window)); 00221 } 00222 00223 00235 static gint 00236 gnc_menu_additions_sort (ExtensionInfo *a, ExtensionInfo *b) 00237 { 00238 if (a->type == b->type) 00239 return strcmp(a->sort_key, b->sort_key); 00240 else if (a->type == GTK_UI_MANAGER_MENU) 00241 return -1; 00242 else if (b->type == GTK_UI_MANAGER_MENU) 00243 return 1; 00244 else 00245 return 0; 00246 } 00247 00248 00254 static gpointer 00255 gnc_menu_additions_init_accel_table (gpointer unused) 00256 { 00257 return g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); 00258 } 00259 00260 00271 static void 00272 gnc_menu_additions_do_preassigned_accel (ExtensionInfo *info, GHashTable *table) 00273 { 00274 gchar *map, *new_map, *accel_key; 00275 const gchar *ptr; 00276 00277 ENTER("Checking %s/%s [%s]", info->path, info->ae.label, info->ae.name); 00278 if (info->accel_assigned) 00279 { 00280 LEAVE("Already processed"); 00281 return; 00282 } 00283 00284 if (!g_utf8_validate(info->ae.label, -1, NULL)) 00285 { 00286 g_warning("Extension menu label '%s' is not valid utf8.", info->ae.label); 00287 info->accel_assigned = TRUE; 00288 LEAVE("Label is invalid utf8"); 00289 return; 00290 } 00291 00292 /* Was an accelerator pre-assigned in the source? */ 00293 ptr = g_utf8_strchr(info->ae.label, -1, '_'); 00294 if (ptr == NULL) 00295 { 00296 LEAVE("not preassigned"); 00297 return; 00298 } 00299 00300 accel_key = g_utf8_strdown(g_utf8_next_char(ptr), 1); 00301 DEBUG("Accelerator preassigned: '%s'", accel_key); 00302 00303 /* Now build a new map. Old one freed automatically. */ 00304 map = g_hash_table_lookup(table, info->path); 00305 if (map == NULL) 00306 map = ""; 00307 new_map = g_strconcat(map, accel_key, (gchar *)NULL); 00308 DEBUG("path '%s', map '%s' -> '%s'", info->path, map, new_map); 00309 g_hash_table_replace(table, info->path, new_map); 00310 00311 info->accel_assigned = TRUE; 00312 g_free(accel_key); 00313 LEAVE("preassigned"); 00314 } 00315 00316 00328 static void 00329 gnc_menu_additions_assign_accel (ExtensionInfo *info, GHashTable *table) 00330 { 00331 gchar *map, *new_map, *new_label, *start, buf[16]; 00332 const gchar *ptr; 00333 gunichar uni; 00334 gint len; 00335 00336 ENTER("Checking %s/%s [%s]", info->path, info->ae.label, info->ae.name); 00337 if (info->accel_assigned) 00338 { 00339 LEAVE("Already processed"); 00340 return; 00341 } 00342 00343 /* Get map of used keys */ 00344 map = g_hash_table_lookup(table, info->path); 00345 if (map == NULL) 00346 map = g_strdup(""); 00347 DEBUG("map '%s', path %s", map, info->path); 00348 00349 for (ptr = info->ae.label; *ptr; ptr = g_utf8_next_char(ptr)) 00350 { 00351 uni = g_utf8_get_char(ptr); 00352 if (!g_unichar_isalpha(uni)) 00353 continue; 00354 uni = g_unichar_tolower(uni); 00355 len = g_unichar_to_utf8(uni, buf); 00356 buf[len] = '\0'; 00357 DEBUG("Testing character '%s'", buf); 00358 if (!g_utf8_strchr(map, -1, uni)) 00359 break; 00360 } 00361 00362 if (ptr == NULL) 00363 { 00364 /* Ran out of characters. Nothing to do. */ 00365 info->accel_assigned = TRUE; 00366 LEAVE("All characters already assigned"); 00367 return; 00368 } 00369 00370 /* Now build a new string in the form "<start>_<end>". */ 00371 start = g_strndup(info->ae.label, ptr - info->ae.label); 00372 DEBUG("start %p, len %ld, text '%s'", start, g_utf8_strlen(start, -1), start); 00373 new_label = g_strconcat(start, "_", ptr, (gchar *)NULL); 00374 g_free(start); 00375 DEBUG("label '%s' -> '%s'", info->ae.label, new_label); 00376 g_free((gchar *)info->ae.label); 00377 info->ae.label = new_label; 00378 00379 /* Now build a new map. Old one freed automatically. */ 00380 new_map = g_strconcat(map, buf, (gchar *)NULL); 00381 DEBUG("map '%s' -> '%s'", map, new_map); 00382 g_hash_table_replace(table, info->path, new_map); 00383 00384 info->accel_assigned = TRUE; 00385 LEAVE("assigned"); 00386 } 00387 00388 00398 static void 00399 gnc_menu_additions_menu_setup_one (ExtensionInfo *ext_info, 00400 GncPluginMenuAdditionsPerWindow *per_window) 00401 { 00402 GncMainWindowActionData *cb_data; 00403 00404 DEBUG( "Adding %s/%s [%s] as [%s]", ext_info->path, ext_info->ae.label, 00405 ext_info->ae.name, ext_info->typeStr ); 00406 00407 cb_data = g_new0 (GncMainWindowActionData, 1); 00408 cb_data->window = per_window->window; 00409 cb_data->data = ext_info->extension; 00410 00411 if (ext_info->type == GTK_UI_MANAGER_MENUITEM) 00412 ext_info->ae.callback = (GCallback)gnc_plugin_menu_additions_action_cb; 00413 00414 gtk_action_group_add_actions_full(per_window->group, &ext_info->ae, 1, 00415 cb_data, g_free); 00416 gtk_ui_manager_add_ui(per_window->ui_manager, per_window->merge_id, 00417 ext_info->path, ext_info->ae.label, ext_info->ae.name, 00418 ext_info->type, FALSE); 00419 gtk_ui_manager_ensure_update(per_window->ui_manager); 00420 } 00421 00422 00435 static void 00436 gnc_plugin_menu_additions_add_to_window (GncPlugin *plugin, 00437 GncMainWindow *window, 00438 GQuark type) 00439 { 00440 GncPluginMenuAdditionsPerWindow per_window; 00441 static GOnce accel_table_init = G_ONCE_INIT; 00442 static GHashTable *table; 00443 GSList *menu_list; 00444 00445 ENTER(" "); 00446 00447 per_window.window = window; 00448 per_window.ui_manager = window->ui_merge; 00449 per_window.group = gtk_action_group_new ("MenuAdditions" ); 00450 gnc_gtk_action_group_set_translation_domain (per_window.group, GETTEXT_PACKAGE); 00451 per_window.merge_id = gtk_ui_manager_new_merge_id(window->ui_merge); 00452 gtk_ui_manager_insert_action_group(window->ui_merge, per_window.group, 0); 00453 00454 menu_list = g_slist_sort(gnc_extensions_get_menu_list(), 00455 (GCompareFunc)gnc_menu_additions_sort); 00456 00457 /* Assign accelerators */ 00458 table = g_once(&accel_table_init, gnc_menu_additions_init_accel_table, NULL); 00459 g_slist_foreach(menu_list, 00460 (GFunc)gnc_menu_additions_do_preassigned_accel, table); 00461 g_slist_foreach(menu_list, (GFunc)gnc_menu_additions_assign_accel, table); 00462 00463 /* Add to window. */ 00464 g_slist_foreach(menu_list, (GFunc)gnc_menu_additions_menu_setup_one, 00465 &per_window); 00466 00467 /* Tell the window code about the actions that were just added 00468 * behind its back (so to speak) */ 00469 gnc_main_window_manual_merge_actions (window, PLUGIN_ACTIONS_NAME, 00470 per_window.group, per_window.merge_id); 00471 00472 g_slist_free(menu_list); 00473 LEAVE(" "); 00474 } 00475 00476 00488 static void 00489 gnc_plugin_menu_additions_remove_from_window (GncPlugin *plugin, 00490 GncMainWindow *window, 00491 GQuark type) 00492 { 00493 GtkActionGroup *group; 00494 00495 ENTER(" "); 00496 00497 /* Have to remove our actions manually. Its only automatic if the 00498 * actions name is installed into the plugin class. */ 00499 group = gnc_main_window_get_action_group(window, PLUGIN_ACTIONS_NAME); 00500 if (group) 00501 gtk_ui_manager_remove_action_group(window->ui_merge, group); 00502 00503 /* Note: This code does not clean up the per-callback data structures 00504 * that are created by the gnc_menu_additions_menu_setup_one() 00505 * function. Its not much memory and shouldn't be a problem. */ 00506 00507 LEAVE(" "); 00508 } 00509
1.7.4