diff -aur compiz-deskmenu/deskmenu-menu.c copy of compiz-deskmenu//deskmenu-menu.c --- compiz-deskmenu/deskmenu-menu.c 2010-11-08 14:20:23.000000000 -0800 +++ compiz-deskmenu2/deskmenu-menu.c 2010-11-08 14:13:24.000000000 -0800 @@ -1,658 +1,671 @@ -/* - * compiz-deskmenu 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. - * - * compiz-deskmenu 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 . - * - * Copyright 2008 Christopher Williams - */ - -#include -#include -#include -#include - -#define HAVE_WNCK 1 - -#if HAVE_WNCK -#include "deskmenu-wnck.h" -#endif - -#include "deskmenu-menu.h" -#include "deskmenu-glue.h" - - -G_DEFINE_TYPE(Deskmenu, deskmenu, G_TYPE_OBJECT) - -GQuark -deskmenu_error_quark (void) -{ - static GQuark quark = 0; - if (!quark) - quark = g_quark_from_static_string ("deskmenu_error"); - return quark; -} - -static void -quit (GtkWidget *widget, - gpointer data) -{ - gtk_main_quit (); -} - -static void -launcher_activated (GtkWidget *widget, - gchar *command) -{ - GError *error = NULL; - Deskmenu *deskmenu; - - deskmenu = g_object_get_data (G_OBJECT (widget), "deskmenu"); - - if (!gdk_spawn_on_screen (gdk_screen_get_default (), - g_get_home_dir (), - g_strsplit (command, " ", 0), - deskmenu->envp, G_SPAWN_SEARCH_PATH, - NULL, NULL, NULL, &error)) - { - GtkWidget *message = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, - GTK_BUTTONS_CLOSE, "%s", error->message); - gtk_dialog_run (GTK_DIALOG (message)); - gtk_widget_destroy (message); - } - -} - -static void -launcher_name_exec_update (GtkWidget *label) -{ - gchar *exec, *stdout; - exec = g_object_get_data (G_OBJECT (label), "exec"); - if (g_spawn_command_line_sync (exec, &stdout, NULL, NULL, NULL)) - gtk_label_set_text (GTK_LABEL (label), g_strstrip(stdout)); - else - gtk_label_set_text (GTK_LABEL (label), "execution error"); - g_free (stdout); -} - -static void -deskmenu_construct_item (Deskmenu *deskmenu) -{ - DeskmenuItem *item = deskmenu->current_item; - GtkWidget *menu_item; - gchar *name, *icon, *command; - - switch (item->type) - { - case DESKMENU_ITEM_LAUNCHER: - if (item->name_exec) - { - GtkWidget *label; - GHook *hook; - - name = g_strstrip (item->name->str); - - menu_item = gtk_image_menu_item_new (); - label = gtk_label_new_with_mnemonic (NULL); - gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - - g_object_set_data (G_OBJECT (label), "exec", g_strdup (name)); - gtk_container_add (GTK_CONTAINER (menu_item), label); - hook = g_hook_alloc (deskmenu->show_hooks); - - hook->data = (gpointer) label; - hook->func = (GHookFunc *) launcher_name_exec_update; - g_hook_append (deskmenu->show_hooks, hook); - } - else - { - if (item->name) - name = g_strstrip (item->name->str); - else - name = ""; - - menu_item = gtk_image_menu_item_new_with_mnemonic (name); - - } - - if (item->icon) - { - icon = g_strstrip (item->icon->str); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), - gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU)); - } - - if (item->command) - { - command = g_strstrip (item->command->str); - g_object_set_data (G_OBJECT (menu_item), "deskmenu", deskmenu); - g_signal_connect (G_OBJECT (menu_item), "activate", - G_CALLBACK (launcher_activated), g_strdup (command)); - } - - gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), - menu_item); - break; - -#if HAVE_WNCK - case DESKMENU_ITEM_WINDOWLIST: - menu_item = gtk_menu_item_new_with_mnemonic ("_Windows"); - - DeskmenuWindowlist *windowlist = deskmenu_windowlist_new (); - - gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), - windowlist->menu); - gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), - menu_item); - break; - - case DESKMENU_ITEM_VIEWPORTLIST: - menu_item = gtk_menu_item_new_with_mnemonic ("_Viewports"); - - DeskmenuVplist *vplist = deskmenu_vplist_new (); - - if (item->wrap - && strcmp (g_strstrip (item->wrap->str), "true") == 0) - vplist->wrap = TRUE; - - gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), - vplist->menu); - gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), - menu_item); - break; -#endif - case DESKMENU_ITEM_RELOAD: - menu_item = gtk_image_menu_item_new_with_mnemonic ("_Reload"); - gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), - gtk_image_new_from_stock (GTK_STOCK_REFRESH, - GTK_ICON_SIZE_MENU)); - g_signal_connect (G_OBJECT (menu_item), "activate", - G_CALLBACK (quit), NULL); - gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), - menu_item); - break; - - default: - break; - } - -} -/* The handler functions. */ - -static void -start_element (GMarkupParseContext *context, - const gchar *element_name, - const gchar **attr_names, - const gchar **attr_values, - gpointer user_data, - GError **error) -{ - Deskmenu *deskmenu = DESKMENU (user_data); - DeskmenuElementType element_type; - const gchar **ncursor = attr_names, **vcursor = attr_values; - GtkWidget *item, *menu; - - element_type = GPOINTER_TO_INT (g_hash_table_lookup - (deskmenu->element_hash, element_name)); - - if ((deskmenu->menu && !deskmenu->current_menu) - || (!deskmenu->menu && element_type != DESKMENU_ELEMENT_MENU)) - { - gint line_num, char_num; - g_markup_parse_context_get_position (context, &line_num, &char_num); - g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - "Error on line %d char %d: Element '%s' declared outside of " - "toplevel menu element", line_num, char_num, element_name); - return; - } - - switch (element_type) - { - case DESKMENU_ELEMENT_MENU: - - if (deskmenu->current_item != NULL) - { - gint line_num, char_num; - g_markup_parse_context_get_position (context, &line_num, - &char_num); - g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - "Error on line %d char %d: Element 'menu' cannot be nested " - "inside of an item element", line_num, char_num); - return; - } - - if (!deskmenu->menu) - { - deskmenu->menu = gtk_menu_new (); - g_object_set_data (G_OBJECT (deskmenu->menu), "parent menu", - NULL); - deskmenu->current_menu = deskmenu->menu; - } - else - { - gchar *name = NULL; - while (*ncursor) - { - if (strcmp (*ncursor, "name") == 0) - name = g_strdup (*vcursor); - else - g_set_error (error, G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - "Unknown attribute: %s", *ncursor); - ncursor++; - vcursor++; - } - if (name) - item = gtk_menu_item_new_with_mnemonic (name); - else - item = gtk_menu_item_new_with_mnemonic (""); - gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), - item); - menu = gtk_menu_new (); - g_object_set_data (G_OBJECT (menu), "parent menu", - deskmenu->current_menu); - deskmenu->current_menu = menu; - gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), - deskmenu->current_menu); - g_free (name); - } - break; - - case DESKMENU_ELEMENT_SEPARATOR: - break; /* build it in the end function */ - - case DESKMENU_ELEMENT_ITEM: - - if (deskmenu->current_item != NULL) - { - gint line_num, char_num; - g_markup_parse_context_get_position (context, &line_num, - &char_num); - g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, - "Error on line %d char %d: Element 'item' cannot be nested " - "inside of another item element", line_num, char_num); - return; - } - - deskmenu->current_item = g_slice_new0 (DeskmenuItem); - while (*ncursor) - { - if (strcmp (*ncursor, "type") == 0) - deskmenu->current_item->type = GPOINTER_TO_INT - (g_hash_table_lookup (deskmenu->item_hash, *vcursor)); - else - g_set_error (error, G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - "Unknown attribute: %s", *ncursor); - ncursor++; - vcursor++; - } - break; - - case DESKMENU_ELEMENT_NAME: - while (*ncursor) - { - if ((strcmp (*ncursor, "mode") == 0) - && (strcmp (*vcursor, "exec") == 0)) - deskmenu->current_item->name_exec = TRUE; - ncursor++; - vcursor++; - } /* no break here to let it fall through */ - case DESKMENU_ELEMENT_ICON: - case DESKMENU_ELEMENT_COMMAND: - case DESKMENU_ELEMENT_WRAP: - if (deskmenu->current_item) - deskmenu->current_item->current_element = element_type; - break; - - default: - g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, - "Unknown element: %s", element_name); - break; - } -} - -static void -text (GMarkupParseContext *context, - const gchar *text, - gsize text_len, - gpointer user_data, - GError **error) -{ - Deskmenu *deskmenu = DESKMENU (user_data); - DeskmenuItem *item = deskmenu->current_item; - - if (!(item && item->current_element)) - return; - - switch (item->current_element) - { - case DESKMENU_ELEMENT_NAME: - if (!item->name) - item->name = g_string_new_len (text, text_len); - else - g_string_append_len (item->name, text, text_len); - break; - - case DESKMENU_ELEMENT_ICON: - if (!item->icon) - item->icon = g_string_new_len (text, text_len); - else - g_string_append_len (item->icon, text, text_len); - break; - - case DESKMENU_ELEMENT_COMMAND: - if (!item->command) - item->command = g_string_new_len (text, text_len); - else - g_string_append_len (item->command, text, text_len); - break; - - case DESKMENU_ELEMENT_WRAP: - if (!item->wrap) - item->wrap = g_string_new_len (text, text_len); - else - g_string_append_len (item->wrap, text, text_len); - break; - - default: - break; - } - -} - -static void -end_element (GMarkupParseContext *context, - const gchar *element_name, - gpointer user_data, - GError **error) -{ - - DeskmenuElementType element_type; - Deskmenu *deskmenu = DESKMENU (user_data); - GtkWidget *parent, *item; - element_type = GPOINTER_TO_INT (g_hash_table_lookup - (deskmenu->element_hash, element_name)); - - switch (element_type) - { - case DESKMENU_ELEMENT_MENU: - - g_return_if_fail (deskmenu->current_item == NULL); - - parent = g_object_get_data (G_OBJECT (deskmenu->current_menu), - "parent menu"); - - deskmenu->current_menu = parent; - - break; - - case DESKMENU_ELEMENT_SEPARATOR: - item = gtk_separator_menu_item_new (); - gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), - item); - break; - - case DESKMENU_ELEMENT_ITEM: - - g_return_if_fail (deskmenu->current_item != NULL); - - /* finally make the item ^_^ */ - deskmenu_construct_item (deskmenu); - - /* free data used to make it */ - if (deskmenu->current_item->name) - g_string_free (deskmenu->current_item->name, TRUE); - if (deskmenu->current_item->icon) - g_string_free (deskmenu->current_item->icon, TRUE); - if (deskmenu->current_item->command) - g_string_free (deskmenu->current_item->command, TRUE); - if (deskmenu->current_item->wrap) - g_string_free (deskmenu->current_item->wrap, TRUE); - g_slice_free (DeskmenuItem, deskmenu->current_item); - deskmenu->current_item = NULL; - break; - - default: - break; - } -} - -/* The list of what handler does what. */ -static GMarkupParser parser = { - start_element, - end_element, - text, - NULL, - NULL -}; - - -/* Class init */ -static void -deskmenu_class_init (DeskmenuClass *deskmenu_class) -{ - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (deskmenu_class), - &dbus_glib_deskmenu_object_info); -} - -/* Instance init */ -static void -deskmenu_init (Deskmenu *deskmenu) -{ - - deskmenu->show_hooks = g_slice_new0 (GHookList); - - g_hook_list_init (deskmenu->show_hooks, sizeof (GHook)); - - - deskmenu->menu = NULL; - deskmenu->current_menu = NULL; - deskmenu->current_item = NULL; - - deskmenu->envp = NULL; - - deskmenu->item_hash = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_insert (deskmenu->item_hash, "launcher", - GINT_TO_POINTER (DESKMENU_ITEM_LAUNCHER)); -#if HAVE_WNCK - g_hash_table_insert (deskmenu->item_hash, "windowlist", - GINT_TO_POINTER (DESKMENU_ITEM_WINDOWLIST)); - g_hash_table_insert (deskmenu->item_hash, "viewportlist", - GINT_TO_POINTER (DESKMENU_ITEM_VIEWPORTLIST)); -#endif - g_hash_table_insert (deskmenu->item_hash, "reload", - GINT_TO_POINTER (DESKMENU_ITEM_RELOAD)); - - deskmenu->element_hash = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_insert (deskmenu->element_hash, "menu", - GINT_TO_POINTER (DESKMENU_ELEMENT_MENU)); - g_hash_table_insert (deskmenu->element_hash, "separator", - GINT_TO_POINTER (DESKMENU_ELEMENT_SEPARATOR)); - g_hash_table_insert (deskmenu->element_hash, "item", - GINT_TO_POINTER (DESKMENU_ELEMENT_ITEM)); - g_hash_table_insert (deskmenu->element_hash, "name", - GINT_TO_POINTER (DESKMENU_ELEMENT_NAME)); - g_hash_table_insert (deskmenu->element_hash, "icon", - GINT_TO_POINTER (DESKMENU_ELEMENT_ICON)); - g_hash_table_insert (deskmenu->element_hash, "command", - GINT_TO_POINTER (DESKMENU_ELEMENT_COMMAND)); - g_hash_table_insert (deskmenu->element_hash, "wrap", - GINT_TO_POINTER (DESKMENU_ELEMENT_WRAP)); - -} - -static void -deskmenu_parse_file (Deskmenu *deskmenu, - gchar *configpath) -{ - GError *error = NULL; - gboolean success = FALSE; - gchar *text; - gsize length; - - if (!configpath) - configpath = g_build_path (G_DIR_SEPARATOR_S, - g_get_user_config_dir (), - "compiz", - "deskmenu", - "menu.xml", - NULL); - - GMarkupParseContext *context = g_markup_parse_context_new (&parser, - 0, deskmenu, NULL); - - if (!g_file_get_contents (configpath, &text, &length, NULL)) - { - const gchar* const *cursor = g_get_system_config_dirs (); - gchar *path = NULL; - while (*cursor) - { - g_free (configpath); - g_free (path); - path = g_strdup (*cursor); - configpath = g_build_path (G_DIR_SEPARATOR_S, - path, - "compiz", - "deskmenu", - "menu.xml", - NULL); - - if (g_file_get_contents (configpath, &text, &length, NULL)) - { - success = TRUE; - g_free (path); - break; - } - cursor++; - } - } - else - { - success = TRUE; - } - - if (!success) - { - g_printerr ("Couldn't find a menu file\n"); - exit (1); - } - - if (!g_markup_parse_context_parse (context, text, length, &error) - || !g_markup_parse_context_end_parse (context, &error)) - { - g_printerr ("Parse of %s failed with message: %s \n", - configpath, error->message); - g_error_free (error); - exit (1); - } - - g_free(text); - g_free (configpath); - g_markup_parse_context_free (context); - - gtk_widget_show_all (deskmenu->menu); -} - -/* The show method */ -gboolean -deskmenu_show (Deskmenu *deskmenu, - gchar **env, - GError **error) -{ - g_hook_list_invoke (deskmenu->show_hooks, FALSE); - - if (deskmenu->envp) - g_strfreev (deskmenu->envp); - - deskmenu->envp = g_strdupv (env); - - gtk_menu_popup (GTK_MENU (deskmenu->menu), - NULL, NULL, NULL, NULL, - 0, 0); - return TRUE; -} - -/* The reload method */ -gboolean -deskmenu_reload (Deskmenu *deskmenu, - GError **error) -{ - gtk_main_quit (); - return TRUE; -} - -int -main (int argc, - char **argv) -{ - DBusGConnection *connection; - GError *error = NULL; - GObject *deskmenu; - - g_type_init (); - - gchar *filename = NULL; - GOptionContext *context; - GOptionEntry entries[] = - { - { "menu", 'm', 0, G_OPTION_ARG_FILENAME, &filename, - "Use FILE instead of the default menu file", "FILE" }, - { NULL, 0, 0, 0, NULL, NULL, NULL } - }; - - context = g_option_context_new (NULL); - g_option_context_add_main_entries (context, entries, NULL); - g_option_context_add_group (context, gtk_get_option_group (TRUE)); - if (!g_option_context_parse (context, &argc, &argv, &error)) - { - g_printerr ("option parsing failed: %s", error->message); - g_error_free (error); - exit (1); - } - g_option_context_free (context); - - /* Obtain a connection to the session bus */ - connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); - if (connection == NULL) - { - g_printerr ("Failed to open connection to bus: %s", error->message); - g_error_free (error); - exit (1); - } - -#if HAVE_WNCK - wnck_set_client_type (WNCK_CLIENT_TYPE_PAGER); -#endif - - gtk_init (&argc, &argv); - - deskmenu = g_object_new (DESKMENU_TYPE, NULL); - - deskmenu_parse_file (DESKMENU (deskmenu), filename); - - dbus_g_connection_register_g_object (connection, - DESKMENU_PATH_DBUS, - deskmenu); - - if (!dbus_bus_request_name (dbus_g_connection_get_connection (connection), - DESKMENU_SERVICE_DBUS, - DBUS_NAME_FLAG_REPLACE_EXISTING, - NULL)) - return 1; - - gtk_main (); - - return 0; -} - +/* + * compiz-deskmenu 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. + * + * compiz-deskmenu 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 . + * + * Copyright 2008 Christopher Williams + */ + +#include +#include +#include +#include + +#define HAVE_WNCK 1 + +#if HAVE_WNCK +#include "deskmenu-wnck.h" +#endif + +#include "deskmenu-menu.h" +#include "deskmenu-glue.h" + + +G_DEFINE_TYPE(Deskmenu, deskmenu, G_TYPE_OBJECT) + +GQuark +deskmenu_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("deskmenu_error"); + return quark; +} + +static void +quit (GtkWidget *widget, + gpointer data) +{ + gtk_main_quit (); +} + +//This is how menu command is launched +static void +launcher_activated (GtkWidget *widget, + gchar *command) +{ + GError *error = NULL; + Deskmenu *deskmenu; + + deskmenu = g_object_get_data (G_OBJECT (widget), "deskmenu"); + //stolen from openbox + gchar *parse_expand_tilde(const gchar *command) + { + gchar *ret; + GRegex *regex; + + if (!command) + return NULL; + + regex = g_regex_new("(?:^|(?<=[ \\t]))~(?:(?=[/ \\t])|$)", + G_REGEX_MULTILINE | G_REGEX_RAW, 0, NULL); + ret = g_regex_replace_literal(regex, command, -1, 0, g_get_home_dir(), 0, NULL); + g_regex_unref(regex); + + return ret; + } + //end stolen + if (!gdk_spawn_command_line_on_screen (gdk_screen_get_default (), parse_expand_tilde(command), &error)) + { + GtkWidget *message = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, "%s", error->message); + gtk_dialog_run (GTK_DIALOG (message)); + gtk_widget_destroy (message); + } + +} + +static void +launcher_name_exec_update (GtkWidget *label) +{ + gchar *exec, *stdout; + exec = g_object_get_data (G_OBJECT (label), "exec"); + if (g_spawn_command_line_sync (exec, &stdout, NULL, NULL, NULL)) + gtk_label_set_text (GTK_LABEL (label), g_strstrip(stdout)); + else + gtk_label_set_text (GTK_LABEL (label), "execution error"); + g_free (stdout); +} + +static void +deskmenu_construct_item (Deskmenu *deskmenu) +{ + DeskmenuItem *item = deskmenu->current_item; + GtkWidget *menu_item; + gchar *name, *icon, *command; + + switch (item->type) + { + case DESKMENU_ITEM_LAUNCHER: + if (item->name_exec) + { + GtkWidget *label; + GHook *hook; + + name = g_strstrip (item->name->str); + + menu_item = gtk_image_menu_item_new (); + label = gtk_label_new_with_mnemonic (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + + g_object_set_data (G_OBJECT (label), "exec", g_strdup (name)); + gtk_container_add (GTK_CONTAINER (menu_item), label); + hook = g_hook_alloc (deskmenu->show_hooks); + + hook->data = (gpointer) label; + hook->func = (GHookFunc *) launcher_name_exec_update; + g_hook_append (deskmenu->show_hooks, hook); + } + else + { + if (item->name) + name = g_strstrip (item->name->str); + else + name = ""; + + menu_item = gtk_image_menu_item_new_with_mnemonic (name); + + } + + if (item->icon) + { + icon = g_strstrip (item->icon->str); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU)); + } + + if (item->command) + { + command = g_strstrip (item->command->str); + g_object_set_data (G_OBJECT (menu_item), "deskmenu", deskmenu); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (launcher_activated), g_strdup (command)); + } + + gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), + menu_item); + break; + +#if HAVE_WNCK + case DESKMENU_ITEM_WINDOWLIST: + menu_item = gtk_menu_item_new_with_mnemonic ("_Windows"); + + DeskmenuWindowlist *windowlist = deskmenu_windowlist_new (); + + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), + windowlist->menu); + gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), + menu_item); + break; + + case DESKMENU_ITEM_VIEWPORTLIST: + menu_item = gtk_menu_item_new_with_mnemonic ("_Viewports"); + + DeskmenuVplist *vplist = deskmenu_vplist_new (); + + if (item->wrap + && strcmp (g_strstrip (item->wrap->str), "true") == 0) + vplist->wrap = TRUE; + + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), + vplist->menu); + gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), + menu_item); + break; +#endif + case DESKMENU_ITEM_RELOAD: + menu_item = gtk_image_menu_item_new_with_mnemonic ("_Reload"); + gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), + gtk_image_new_from_stock (GTK_STOCK_REFRESH, + GTK_ICON_SIZE_MENU)); + g_signal_connect (G_OBJECT (menu_item), "activate", + G_CALLBACK (quit), NULL); + gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), + menu_item); + break; + + default: + break; + } + +} +/* The handler functions. */ + +static void +start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attr_names, + const gchar **attr_values, + gpointer user_data, + GError **error) +{ + Deskmenu *deskmenu = DESKMENU (user_data); + DeskmenuElementType element_type; + const gchar **ncursor = attr_names, **vcursor = attr_values; + GtkWidget *item, *menu; + + element_type = GPOINTER_TO_INT (g_hash_table_lookup + (deskmenu->element_hash, element_name)); + + if ((deskmenu->menu && !deskmenu->current_menu) + || (!deskmenu->menu && element_type != DESKMENU_ELEMENT_MENU)) + { + gint line_num, char_num; + g_markup_parse_context_get_position (context, &line_num, &char_num); + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + "Error on line %d char %d: Element '%s' declared outside of " + "toplevel menu element", line_num, char_num, element_name); + return; + } + + switch (element_type) + { + case DESKMENU_ELEMENT_MENU: + + if (deskmenu->current_item != NULL) + { + gint line_num, char_num; + g_markup_parse_context_get_position (context, &line_num, + &char_num); + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + "Error on line %d char %d: Element 'menu' cannot be nested " + "inside of an item element", line_num, char_num); + return; + } + + if (!deskmenu->menu) + { + deskmenu->menu = gtk_menu_new (); + g_object_set_data (G_OBJECT (deskmenu->menu), "parent menu", + NULL); + deskmenu->current_menu = deskmenu->menu; + } + else + { + gchar *name = NULL; + while (*ncursor) + { + if (strcmp (*ncursor, "name") == 0) + name = g_strdup (*vcursor); + else + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, + "Unknown attribute: %s", *ncursor); + ncursor++; + vcursor++; + } + if (name) + item = gtk_menu_item_new_with_mnemonic (name); + else + item = gtk_menu_item_new_with_mnemonic (""); + gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), + item); + menu = gtk_menu_new (); + g_object_set_data (G_OBJECT (menu), "parent menu", + deskmenu->current_menu); + deskmenu->current_menu = menu; + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), + deskmenu->current_menu); + g_free (name); + } + break; + + case DESKMENU_ELEMENT_SEPARATOR: + break; /* build it in the end function */ + + case DESKMENU_ELEMENT_ITEM: + + if (deskmenu->current_item != NULL) + { + gint line_num, char_num; + g_markup_parse_context_get_position (context, &line_num, + &char_num); + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, + "Error on line %d char %d: Element 'item' cannot be nested " + "inside of another item element", line_num, char_num); + return; + } + + deskmenu->current_item = g_slice_new0 (DeskmenuItem); + while (*ncursor) + { + if (strcmp (*ncursor, "type") == 0) + deskmenu->current_item->type = GPOINTER_TO_INT + (g_hash_table_lookup (deskmenu->item_hash, *vcursor)); + else + g_set_error (error, G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, + "Unknown attribute: %s", *ncursor); + ncursor++; + vcursor++; + } + break; + + case DESKMENU_ELEMENT_NAME: + while (*ncursor) + { + if ((strcmp (*ncursor, "mode") == 0) + && (strcmp (*vcursor, "exec") == 0)) + deskmenu->current_item->name_exec = TRUE; + ncursor++; + vcursor++; + } /* no break here to let it fall through */ + case DESKMENU_ELEMENT_ICON: + case DESKMENU_ELEMENT_COMMAND: + case DESKMENU_ELEMENT_WRAP: + if (deskmenu->current_item) + deskmenu->current_item->current_element = element_type; + break; + + default: + g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, + "Unknown element: %s", element_name); + break; + } +} + +static void +text (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + Deskmenu *deskmenu = DESKMENU (user_data); + DeskmenuItem *item = deskmenu->current_item; + + if (!(item && item->current_element)) + return; + + switch (item->current_element) + { + case DESKMENU_ELEMENT_NAME: + if (!item->name) + item->name = g_string_new_len (text, text_len); + else + g_string_append_len (item->name, text, text_len); + break; + + case DESKMENU_ELEMENT_ICON: + if (!item->icon) + item->icon = g_string_new_len (text, text_len); + else + g_string_append_len (item->icon, text, text_len); + break; + + case DESKMENU_ELEMENT_COMMAND: + if (!item->command) + item->command = g_string_new_len (text, text_len); + else + g_string_append_len (item->command, text, text_len); + break; + + case DESKMENU_ELEMENT_WRAP: + if (!item->wrap) + item->wrap = g_string_new_len (text, text_len); + else + g_string_append_len (item->wrap, text, text_len); + break; + + default: + break; + } + +} + +static void +end_element (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + + DeskmenuElementType element_type; + Deskmenu *deskmenu = DESKMENU (user_data); + GtkWidget *parent, *item; + element_type = GPOINTER_TO_INT (g_hash_table_lookup + (deskmenu->element_hash, element_name)); + + switch (element_type) + { + case DESKMENU_ELEMENT_MENU: + + g_return_if_fail (deskmenu->current_item == NULL); + + parent = g_object_get_data (G_OBJECT (deskmenu->current_menu), + "parent menu"); + + deskmenu->current_menu = parent; + + break; + + case DESKMENU_ELEMENT_SEPARATOR: + item = gtk_separator_menu_item_new (); + gtk_menu_shell_append (GTK_MENU_SHELL (deskmenu->current_menu), + item); + break; + + case DESKMENU_ELEMENT_ITEM: + + g_return_if_fail (deskmenu->current_item != NULL); + + /* finally make the item ^_^ */ + deskmenu_construct_item (deskmenu); + + /* free data used to make it */ + if (deskmenu->current_item->name) + g_string_free (deskmenu->current_item->name, TRUE); + if (deskmenu->current_item->icon) + g_string_free (deskmenu->current_item->icon, TRUE); + if (deskmenu->current_item->command) + g_string_free (deskmenu->current_item->command, TRUE); + if (deskmenu->current_item->wrap) + g_string_free (deskmenu->current_item->wrap, TRUE); + g_slice_free (DeskmenuItem, deskmenu->current_item); + deskmenu->current_item = NULL; + break; + + default: + break; + } +} + +/* The list of what handler does what. */ +static GMarkupParser parser = { + start_element, + end_element, + text, + NULL, + NULL +}; + + +/* Class init */ +static void +deskmenu_class_init (DeskmenuClass *deskmenu_class) +{ + dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (deskmenu_class), + &dbus_glib_deskmenu_object_info); +} + +/* Instance init */ +static void +deskmenu_init (Deskmenu *deskmenu) +{ + + deskmenu->show_hooks = g_slice_new0 (GHookList); + + g_hook_list_init (deskmenu->show_hooks, sizeof (GHook)); + + + deskmenu->menu = NULL; + deskmenu->current_menu = NULL; + deskmenu->current_item = NULL; + + deskmenu->envp = NULL; + + deskmenu->item_hash = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (deskmenu->item_hash, "launcher", + GINT_TO_POINTER (DESKMENU_ITEM_LAUNCHER)); +#if HAVE_WNCK + g_hash_table_insert (deskmenu->item_hash, "windowlist", + GINT_TO_POINTER (DESKMENU_ITEM_WINDOWLIST)); + g_hash_table_insert (deskmenu->item_hash, "viewportlist", + GINT_TO_POINTER (DESKMENU_ITEM_VIEWPORTLIST)); +#endif + g_hash_table_insert (deskmenu->item_hash, "reload", + GINT_TO_POINTER (DESKMENU_ITEM_RELOAD)); + + deskmenu->element_hash = g_hash_table_new (g_str_hash, g_str_equal); + + g_hash_table_insert (deskmenu->element_hash, "menu", + GINT_TO_POINTER (DESKMENU_ELEMENT_MENU)); + g_hash_table_insert (deskmenu->element_hash, "separator", + GINT_TO_POINTER (DESKMENU_ELEMENT_SEPARATOR)); + g_hash_table_insert (deskmenu->element_hash, "item", + GINT_TO_POINTER (DESKMENU_ELEMENT_ITEM)); + g_hash_table_insert (deskmenu->element_hash, "name", + GINT_TO_POINTER (DESKMENU_ELEMENT_NAME)); + g_hash_table_insert (deskmenu->element_hash, "icon", + GINT_TO_POINTER (DESKMENU_ELEMENT_ICON)); + g_hash_table_insert (deskmenu->element_hash, "command", + GINT_TO_POINTER (DESKMENU_ELEMENT_COMMAND)); + g_hash_table_insert (deskmenu->element_hash, "wrap", + GINT_TO_POINTER (DESKMENU_ELEMENT_WRAP)); + +} + +static void +deskmenu_parse_file (Deskmenu *deskmenu, + gchar *configpath) +{ + GError *error = NULL; + gboolean success = FALSE; + gchar *text; + gsize length; + + if (!configpath) + configpath = g_build_path (G_DIR_SEPARATOR_S, + g_get_user_config_dir (), + "compiz", + "deskmenu", + "menu.xml", + NULL); + + GMarkupParseContext *context = g_markup_parse_context_new (&parser, + 0, deskmenu, NULL); + + if (!g_file_get_contents (configpath, &text, &length, NULL)) + { + const gchar* const *cursor = g_get_system_config_dirs (); + gchar *path = NULL; + while (*cursor) + { + g_free (configpath); + g_free (path); + path = g_strdup (*cursor); + configpath = g_build_path (G_DIR_SEPARATOR_S, + path, + "compiz", + "deskmenu", + "menu.xml", + NULL); + + if (g_file_get_contents (configpath, &text, &length, NULL)) + { + success = TRUE; + g_free (path); + break; + } + cursor++; + } + } + else + { + success = TRUE; + } + + if (!success) + { + g_printerr ("Couldn't find a menu file\n"); + exit (1); + } + + if (!g_markup_parse_context_parse (context, text, length, &error) + || !g_markup_parse_context_end_parse (context, &error)) + { + g_printerr ("Parse of %s failed with message: %s \n", + configpath, error->message); + g_error_free (error); + exit (1); + } + + g_free(text); + g_free (configpath); + g_markup_parse_context_free (context); + + gtk_widget_show_all (deskmenu->menu); +} + +/* The show method */ +gboolean +deskmenu_show (Deskmenu *deskmenu, + gchar **env, + GError **error) +{ + g_hook_list_invoke (deskmenu->show_hooks, FALSE); + + if (deskmenu->envp) + g_strfreev (deskmenu->envp); + + deskmenu->envp = g_strdupv (env); + + gtk_menu_popup (GTK_MENU (deskmenu->menu), + NULL, NULL, NULL, NULL, + 0, 0); + return TRUE; +} + +/* The reload method */ +gboolean +deskmenu_reload (Deskmenu *deskmenu, + GError **error) +{ + gtk_main_quit (); + return TRUE; +} + +int +main (int argc, + char **argv) +{ + DBusGConnection *connection; + GError *error = NULL; + GObject *deskmenu; + + g_type_init (); + + gchar *filename = NULL; + GOptionContext *context; + GOptionEntry entries[] = + { + { "menu", 'm', 0, G_OPTION_ARG_FILENAME, &filename, + "Use FILE instead of the default menu file", "FILE" }, + { NULL, 0, 0, 0, NULL, NULL, NULL } + }; + + context = g_option_context_new (NULL); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_add_group (context, gtk_get_option_group (TRUE)); + if (!g_option_context_parse (context, &argc, &argv, &error)) + { + g_printerr ("option parsing failed: %s", error->message); + g_error_free (error); + exit (1); + } + g_option_context_free (context); + + /* Obtain a connection to the session bus */ + connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (connection == NULL) + { + g_printerr ("Failed to open connection to bus: %s", error->message); + g_error_free (error); + exit (1); + } + +#if HAVE_WNCK + wnck_set_client_type (WNCK_CLIENT_TYPE_PAGER); +#endif + + gtk_init (&argc, &argv); + + deskmenu = g_object_new (DESKMENU_TYPE, NULL); + + deskmenu_parse_file (DESKMENU (deskmenu), filename); + + dbus_g_connection_register_g_object (connection, + DESKMENU_PATH_DBUS, + deskmenu); + + if (!dbus_bus_request_name (dbus_g_connection_get_connection (connection), + DESKMENU_SERVICE_DBUS, + DBUS_NAME_FLAG_REPLACE_EXISTING, + NULL)) + return 1; + + gtk_main (); + + return 0; +} +