gnc-plugin-menu-additions.c

Go to the documentation of this file.
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     static const GTypeInfo our_info = {
00101       sizeof (GncPluginMenuAdditionsClass),
00102       NULL,             /* base_init */
00103       NULL,             /* base_finalize */
00104       (GClassInitFunc) gnc_plugin_menu_additions_class_init,
00105       NULL,             /* class_finalize */
00106       NULL,             /* class_data */
00107       sizeof (GncPluginMenuAdditions),
00108       0,
00109       (GInstanceInitFunc) gnc_plugin_menu_additions_init
00110     };
00111 
00112     gnc_plugin_menu_additions_type = g_type_register_static (GNC_TYPE_PLUGIN,
00113                                                          "GncPluginMenuAdditions",
00114                                                          &our_info, 0);
00115   }
00116 
00117   return gnc_plugin_menu_additions_type;
00118 }
00119 
00120 static void
00121 gnc_plugin_menu_additions_class_init (GncPluginMenuAdditionsClass *klass)
00122 {
00123   GObjectClass *object_class = G_OBJECT_CLASS (klass);
00124   GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass);
00125 
00126   parent_class = g_type_class_peek_parent (klass);
00127 
00128   object_class->finalize = gnc_plugin_menu_additions_finalize;
00129 
00130   /* plugin info */
00131   plugin_class->plugin_name   = GNC_PLUGIN_MENU_ADDITIONS_NAME;
00132 
00133   /* function overrides */
00134   plugin_class->add_to_window = gnc_plugin_menu_additions_add_to_window;
00135   plugin_class->remove_from_window = gnc_plugin_menu_additions_remove_from_window;
00136 
00137   g_type_class_add_private(klass, sizeof(GncPluginMenuAdditionsPrivate));
00138 }
00139 
00140 static void
00141 gnc_plugin_menu_additions_init (GncPluginMenuAdditions *plugin)
00142 {
00143   ENTER("plugin %p", plugin);
00144   LEAVE("");
00145 }
00146 
00147 static void
00148 gnc_plugin_menu_additions_finalize (GObject *object)
00149 {
00150   GncPluginMenuAdditions *plugin;
00151   GncPluginMenuAdditionsPrivate *priv;
00152 
00153   g_return_if_fail (GNC_IS_PLUGIN_MENU_ADDITIONS (object));
00154 
00155   ENTER("plugin %p", object);
00156   plugin = GNC_PLUGIN_MENU_ADDITIONS (object);
00157   priv = GNC_PLUGIN_MENU_ADDITIONS_GET_PRIVATE (plugin);
00158 
00159   G_OBJECT_CLASS (parent_class)->finalize (object);
00160   LEAVE("");
00161 }
00162 
00163 
00164 /*  Create a new menu_additions plugin.  This plugin attaches the menu
00165  *  items from Scheme code to any window that is opened.
00166  *
00167  *  @return A pointer to the new object.
00168  */
00169 GncPlugin *
00170 gnc_plugin_menu_additions_new (void)
00171 {
00172   GncPlugin *plugin_page = NULL;
00173 
00174   ENTER("");
00175   plugin_page = GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_MENU_ADDITIONS, NULL));
00176   LEAVE("plugin %p", plugin_page);
00177   return plugin_page;
00178 }
00179 
00180 /************************************************************
00181  *              Plugin Function Implementation              *
00182  ************************************************************/
00183 
00184 static SCM
00185 gnc_main_window_to_scm (GncMainWindow *window)
00186 {
00187   static swig_type_info * main_window_type = NULL;
00188 
00189   if (!window)
00190     return SCM_BOOL_F;
00191 
00192   if (!main_window_type)
00193     main_window_type = SWIG_TypeQuery("_p_GncMainWindow");
00194 
00195   return SWIG_NewPointerObj(window, main_window_type, 0);
00196 }
00197 
00198 
00210 static void
00211 gnc_plugin_menu_additions_action_cb (GtkAction *action,
00212                                      GncMainWindowActionData *data)
00213 {
00214 
00215   g_return_if_fail(GTK_IS_ACTION(action));
00216   g_return_if_fail(data != NULL);
00217 
00218   gnc_extension_invoke_cb(data->data, gnc_main_window_to_scm(data->window));
00219 }
00220 
00221 
00233 static gint
00234 gnc_menu_additions_sort (ExtensionInfo *a, ExtensionInfo *b)
00235 {
00236   if (a->type == b->type)
00237     return strcmp(a->sort_key, b->sort_key);
00238   else if (a->type == GTK_UI_MANAGER_MENU)
00239     return -1;
00240   else if (b->type == GTK_UI_MANAGER_MENU)
00241     return 1;
00242   else
00243     return 0;
00244 }
00245 
00246 
00252 static gpointer
00253 gnc_menu_additions_init_accel_table (gpointer unused)
00254 {
00255   return g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
00256 }
00257 
00258 
00269 static void
00270 gnc_menu_additions_do_preassigned_accel (ExtensionInfo *info, GHashTable *table)
00271 {
00272   gchar *map, *new_map, *accel_key;
00273   const gchar *ptr;
00274 
00275   ENTER("Checking %s/%s [%s]", info->path, info->ae.label, info->ae.name);
00276   if (info->accel_assigned) {
00277     LEAVE("Already processed");
00278     return;
00279   }
00280 
00281   if (!g_utf8_validate(info->ae.label, -1, NULL)) {
00282     g_warning("Extension menu label '%s' is not valid utf8.", info->ae.label);
00283     info->accel_assigned = TRUE;
00284     LEAVE("Label is invalid utf8");
00285     return;
00286   }
00287 
00288   /* Was an accelerator pre-assigned in the source? */
00289   ptr = g_utf8_strchr(info->ae.label, -1, '_');
00290   if (ptr == NULL) {
00291     LEAVE("not preassigned");
00292     return;
00293   }
00294 
00295   accel_key = g_utf8_strdown(g_utf8_next_char(ptr), 1);
00296   DEBUG("Accelerator preassigned: '%s'", accel_key);
00297 
00298   /* Now build a new map. Old one freed automatically. */
00299   map = g_hash_table_lookup(table, info->path);
00300   if (map == NULL)
00301     map = "";
00302   new_map = g_strconcat(map, accel_key, (gchar *)NULL);
00303   DEBUG("path '%s', map '%s' -> '%s'", info->path, map, new_map);
00304   g_hash_table_replace(table, info->path, new_map);
00305 
00306   info->accel_assigned = TRUE;
00307   g_free(accel_key);
00308   LEAVE("preassigned");
00309 }
00310 
00311 
00323 static void
00324 gnc_menu_additions_assign_accel (ExtensionInfo *info, GHashTable *table)
00325 {
00326   gchar *map, *new_map, *new_label, *start, buf[16];
00327   const gchar *ptr;
00328   gunichar uni;
00329   gint len;
00330 
00331   ENTER("Checking %s/%s [%s]", info->path, info->ae.label, info->ae.name);
00332   if (info->accel_assigned) {
00333     LEAVE("Already processed");
00334     return;
00335   }
00336 
00337   /* Get map of used keys */
00338   map = g_hash_table_lookup(table, info->path);
00339   if (map == NULL)
00340     map = g_strdup("");
00341   DEBUG("map '%s', path %s", map, info->path);
00342 
00343   for (ptr = info->ae.label; *ptr; ptr = g_utf8_next_char(ptr)) {
00344     uni = g_utf8_get_char(ptr);
00345     if (!g_unichar_isalpha(uni))
00346       continue;
00347     uni = g_unichar_tolower(uni);
00348     len = g_unichar_to_utf8(uni, buf);
00349     buf[len] = '\0';
00350     DEBUG("Testing character '%s'", buf);
00351     if (!g_utf8_strchr(map, -1, uni))
00352       break;
00353   }
00354 
00355   if (ptr == NULL) {
00356     /* Ran out of characters. Nothing to do. */
00357     info->accel_assigned = TRUE;
00358     LEAVE("All characters already assigned");
00359     return;
00360   }
00361 
00362   /* Now build a new string in the form "<start>_<end>". */
00363   start = g_strndup(info->ae.label, ptr - info->ae.label);
00364   DEBUG("start %p, len %ld, text '%s'", start, g_utf8_strlen(start, -1), start);
00365   new_label = g_strconcat(start, "_", ptr, (gchar *)NULL);
00366   g_free(start);
00367   DEBUG("label '%s' -> '%s'", info->ae.label, new_label);
00368   g_free((gchar *)info->ae.label);
00369   info->ae.label = new_label;
00370 
00371   /* Now build a new map. Old one freed automatically. */
00372   new_map = g_strconcat(map, buf, (gchar *)NULL);
00373   DEBUG("map '%s' -> '%s'", map, new_map);
00374   g_hash_table_replace(table, info->path, new_map);
00375 
00376   info->accel_assigned = TRUE;
00377   LEAVE("assigned");
00378 }
00379 
00380 
00390 static void
00391 gnc_menu_additions_menu_setup_one (ExtensionInfo *ext_info,
00392                                    GncPluginMenuAdditionsPerWindow *per_window)
00393 {
00394   GncMainWindowActionData *cb_data;
00395 
00396   DEBUG( "Adding %s/%s [%s] as [%s]", ext_info->path, ext_info->ae.label,
00397          ext_info->ae.name, ext_info->typeStr );
00398 
00399   cb_data = g_new0 (GncMainWindowActionData, 1);
00400   cb_data->window = per_window->window;
00401   cb_data->data = ext_info->extension;
00402 
00403   if (ext_info->type == GTK_UI_MANAGER_MENUITEM)
00404     ext_info->ae.callback = (GCallback)gnc_plugin_menu_additions_action_cb;
00405 
00406   gtk_action_group_add_actions_full(per_window->group, &ext_info->ae, 1,
00407                                     cb_data, g_free);
00408   gtk_ui_manager_add_ui(per_window->ui_manager, per_window->merge_id,
00409                         ext_info->path, ext_info->ae.label, ext_info->ae.name,
00410                         ext_info->type, FALSE);
00411   gtk_ui_manager_ensure_update(per_window->ui_manager);
00412 }
00413 
00414 
00427 static void
00428 gnc_plugin_menu_additions_add_to_window (GncPlugin *plugin,
00429                                          GncMainWindow *window,
00430                                          GQuark type)
00431 {
00432   GncPluginMenuAdditionsPerWindow per_window;
00433   static GOnce accel_table_init = G_ONCE_INIT;
00434   static GHashTable *table;
00435   GSList *menu_list;
00436 
00437   ENTER(" ");
00438 
00439   per_window.window = window;
00440   per_window.ui_manager = window->ui_merge;
00441   per_window.group = gtk_action_group_new ("MenuAdditions" );
00442   gnc_gtk_action_group_set_translation_domain (per_window.group, GETTEXT_PACKAGE);
00443   per_window.merge_id = gtk_ui_manager_new_merge_id(window->ui_merge);
00444   gtk_ui_manager_insert_action_group(window->ui_merge, per_window.group, 0);
00445 
00446   menu_list = g_slist_sort(gnc_extensions_get_menu_list(),
00447                            (GCompareFunc)gnc_menu_additions_sort);
00448 
00449   /* Assign accelerators */
00450   table = g_once(&accel_table_init, gnc_menu_additions_init_accel_table, NULL);
00451   g_slist_foreach(menu_list,
00452                   (GFunc)gnc_menu_additions_do_preassigned_accel, table);
00453   g_slist_foreach(menu_list, (GFunc)gnc_menu_additions_assign_accel, table);
00454 
00455   /* Add to window. */
00456   g_slist_foreach(menu_list, (GFunc)gnc_menu_additions_menu_setup_one,
00457                   &per_window);
00458 
00459   /* Tell the window code about the actions that were just added
00460    * behind its back (so to speak) */
00461   gnc_main_window_manual_merge_actions (window, PLUGIN_ACTIONS_NAME,
00462                                         per_window.group, per_window.merge_id);
00463 
00464   LEAVE(" ");
00465 }
00466 
00467 
00479 static void
00480 gnc_plugin_menu_additions_remove_from_window (GncPlugin *plugin,
00481                                               GncMainWindow *window,
00482                                               GQuark type)
00483 {
00484   GtkActionGroup *group;
00485 
00486   ENTER(" ");
00487 
00488   /* Have to remove our actions manually. Its only automatic if the
00489    * actions name is installed into the plugin class. */
00490   group = gnc_main_window_get_action_group(window, PLUGIN_ACTIONS_NAME);
00491   if (group)
00492     gtk_ui_manager_remove_action_group(window->ui_merge, group);
00493 
00494   /* Note: This code does not clean up the per-callback data structures
00495    * that are created by the gnc_menu_additions_menu_setup_one()
00496    * function. Its not much memory and shouldn't be a problem. */
00497 
00498   LEAVE(" ");
00499 }
00500 

Generated on Sun Sep 7 05:07:19 2008 for GnuCash by  doxygen 1.5.2