rendered paste body/* * 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 <http://www.gnu.org/licenses/>. * * Copyright 2008 Christopher Williams <christopherw@verizon.net> */ /*Roadmap:Necessary: TODO: Add a viewport # indicator for the window list for accesiblity reasons difficulty: hard TODO: Add configuration for menu icon size difficulty: easy TODO: Add toggle of tearables difficulty: easy TODO: Add a sane icon dialog difficulty: medium-hardFor fun, might not implement:TODO: Add ability to call up menus from the menu.xml file by name, if this is really, really needed or requested */#include <stdlib.h>#include <string.h>#include <dbus/dbus-glib-lowlevel.h>#include <gtk/gtk.h>#define HAVE_WNCK 1#if HAVE_WNCK#include "deskmenu-wnck.h"#endif#include "deskmenu-utils.h"#include "deskmenu-menu.h"#include "deskmenu-glue.h"G_DEFINE_TYPE(Deskmenu, deskmenu, G_TYPE_OBJECT) //this is calling deskmenu_class_initstatic voidstart_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer user_data, GError **error);static voidtext (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error);static voidend_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error);static GMarkupParser parser = { start_element, end_element, text, NULL, NULL};static GHashTable *item_hash;static GHashTable *element_hash;static voiddeskmenu_free_item (DeskmenuObject *dm_object);GQuarkdeskmenu_error_quark (void){ static GQuark quark = 0; if (!quark) quark = g_quark_from_static_string ("deskmenu_error"); return quark;}static voidquit (GtkWidget *widget, gpointer data){ gtk_main_quit ();}//This is how menu command is launchedstatic voidlauncher_activated (GtkWidget *widget, gchar *command){ GError *error = NULL; if (!gdk_spawn_command_line_on_screen (gdk_screen_get_default (), parse_expand_tilde(command), &error)) { deskmenu_widget_error(error); }}//This is how a recent document is openedstatic voidrecent_activated (GtkRecentChooser *chooser, gchar *command){ GError *error = NULL; gchar *full_command, *file; file = gtk_recent_chooser_get_current_uri (chooser); full_command = get_full_command(command, file); if (!gdk_spawn_command_line_on_screen (gdk_screen_get_default (), parse_expand_tilde(full_command), &error)) { deskmenu_widget_error(error); }}/* prototype code for pipemenu */static voidpipe_menu_recreate (GtkWidget *item, gchar *command) { gchar *stdout; gchar *cache_entries = g_object_get_data (G_OBJECT(item), "cached"); GtkWidget *submenu; submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM(item)); if (submenu) { if (strcmp (cache_entries, "yes") != 0) { gtk_widget_destroy (submenu); submenu = NULL; } } if (!submenu) { submenu = gtk_menu_new(); if (g_spawn_command_line_sync (parse_expand_tilde(command), &stdout, NULL, NULL, NULL)) { GError *error = NULL; DeskmenuObject *dm_object = g_object_get_data (G_OBJECT(item), "menu"); dm_object->current_menu = submenu; dm_object->make_from_pipe = TRUE; if (gtk_menu_get_tearoff_state (GTK_MENU(dm_object->menu))) { gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), gtk_tearoff_menu_item_new()); } GMarkupParseContext *context = g_markup_parse_context_new (&parser, 0, dm_object, NULL); g_markup_parse_context_parse (context, stdout, strlen(stdout), &error); g_markup_parse_context_free (context); if (error) { g_print(error->message); //spit out the message manually if (dm_object->current_item) { //force reset deskmenu_free_item(dm_object); } g_error_free (error); } g_free(stdout); dm_object->make_from_pipe = FALSE; } else { GtkWidget *empty_item = gtk_menu_item_new_with_label ("Unable to get output"); gtk_widget_set_sensitive (empty_item, FALSE); gtk_menu_shell_append (GTK_MENU_SHELL (submenu), empty_item); } gtk_widget_show_all(submenu); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); }}static voidlauncher_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 GtkWidget *make_recent_documents_list (gboolean images, gchar *command, int limit, int age, gchar *sort_type){ GtkWidget *widget = gtk_recent_chooser_menu_new (); if (images) { gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER(widget), TRUE); } else { gtk_recent_chooser_set_show_icons (GTK_RECENT_CHOOSER(widget), FALSE); } g_signal_connect (G_OBJECT (widget), "item-activated", G_CALLBACK (recent_activated), g_strdup (command)); if (age) { GtkRecentFilter *filter = gtk_recent_filter_new (); gtk_recent_filter_add_pattern (filter, "*"); gtk_recent_filter_add_age (filter, age); gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER(widget), filter); } if (sort_type) { if (strcmp (sort_type, "most used") == 0) { gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER(widget), GTK_RECENT_SORT_MRU); } else { gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER(widget), GTK_RECENT_SORT_LRU); } } gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER(widget), limit); return widget;}static voiddeskmenu_construct_item (DeskmenuObject *dm_object){ DeskmenuItem *item = dm_object->current_item; GtkWidget *menu_item, *submenu; gchar *name, *icon, *command, *vpicon; gboolean images; gint w, h;//constructs the items in menu 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 (dm_object->show_hooks); hook->data = (gpointer) label; hook->func = (GHookFunc *) launcher_name_exec_update; g_hook_append (dm_object->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); if (item->icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL))); } else { 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_pipe) { command = g_strstrip (item->command->str); if (item->cache_output) { g_object_set_data(G_OBJECT(menu_item), "cached", g_strdup("yes")); } else { g_object_set_data(G_OBJECT(menu_item), "cached", g_strdup("no")); } g_object_set_data(G_OBJECT(menu_item), "menu", dm_object); g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (pipe_menu_recreate), g_strdup(command)); submenu = gtk_menu_new(); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), submenu); } else { if (item->command) { command = g_strstrip (item->command->str); g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (launcher_activated), g_strdup (command)); } } gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), menu_item); break;#if HAVE_WNCK case DESKMENU_ITEM_WINDOWLIST: menu_item = gtk_image_menu_item_new_with_mnemonic ("_Windows"); images = FALSE; gboolean this_vp = FALSE; gboolean mini_only = FALSE; if (item->icon) { images = TRUE; icon = g_strstrip (item->icon->str); if (item->icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL))); } else { 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->thisvp && strcmp (g_strstrip (item->thisvp->str), "true") == 0) this_vp = TRUE; if (item->mini_only && strcmp (g_strstrip (item->mini_only->str), "true") == 0) mini_only = TRUE; g_object_set_data(G_OBJECT(menu_item), "windowlist", deskmenu_windowlist_initialize (images, this_vp, mini_only)); g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (refresh_windowlist_item), NULL); submenu = gtk_menu_new(); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), submenu); gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), menu_item); break; case DESKMENU_ITEM_VIEWPORTLIST: menu_item = gtk_image_menu_item_new_with_mnemonic ("_Viewports"); gboolean wrap, file; wrap = FALSE; images = FALSE; file = FALSE; if (item->wrap && strcmp (g_strstrip (item->wrap->str), "true") == 0) wrap = TRUE; if (item->icon) { images = TRUE; icon = g_strstrip (item->icon->str); if (item->icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL))); } else { 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->vpicon) { vpicon = g_strstrip (parse_expand_tilde(item->vpicon->str)); if (item->vpicon_file) { file = TRUE; } } g_object_set_data(G_OBJECT(menu_item), "vplist", deskmenu_vplist_initialize (wrap, images, file, vpicon)); g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (refresh_viewportlist_item), NULL); submenu = gtk_menu_new(); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), submenu); gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), menu_item); break;#endif case DESKMENU_ITEM_RELOAD: menu_item = gtk_image_menu_item_new_with_mnemonic ("Reload"); if (item->icon) { icon = g_strstrip (item->icon->str); if (item->icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL))); } else { gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU)); } } g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (quit), NULL); gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), menu_item); break; case DESKMENU_ITEM_DOCUMENTS: menu_item = gtk_image_menu_item_new_with_mnemonic ("Recent Doc_uments"); gint limit, age; gchar *sort_type; images = FALSE; if (item->icon) { images = TRUE; icon = g_strstrip (item->icon->str); if (item->icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL))); } else { 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->age) { age = atoi(g_strstrip (item->age->str)); } if (item->sort_type) { sort_type = g_strstrip (item->sort_type->str); } if (item->quantity) { limit = atoi(g_strstrip (item->quantity->str)); } else { limit = -1; } if (item->command) { command = g_strstrip (item->command->str); } else { command = g_strdup ("xdg-open"); } GtkWidget *docs = make_recent_documents_list(images, command, limit, age, sort_type); gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), docs); gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), menu_item); break; default: break; }}static voiddeskmenu_free_item (DeskmenuObject *dm_object) { /* free data used to make it */ if (dm_object->current_item->name) g_string_free (dm_object->current_item->name, TRUE); if (dm_object->current_item->icon) g_string_free (dm_object->current_item->icon, TRUE); if (dm_object->current_item->command) g_string_free (dm_object->current_item->command, TRUE); if (dm_object->current_item->wrap) g_string_free (dm_object->current_item->wrap, TRUE); if (dm_object->current_item->vpicon) g_string_free (dm_object->current_item->vpicon, TRUE); if (dm_object->current_item->mini_only) g_string_free (dm_object->current_item->mini_only, TRUE); if (dm_object->current_item->thisvp) g_string_free (dm_object->current_item->thisvp, TRUE); if (dm_object->current_item->sort_type) g_string_free (dm_object->current_item->sort_type, TRUE); if (dm_object->current_item->quantity) g_string_free (dm_object->current_item->quantity, TRUE); if (dm_object->current_item->age) g_string_free (dm_object->current_item->age, TRUE); dm_object->current_item = NULL;}/* The handler functions. */static voidstart_element (GMarkupParseContext *context, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer user_data, GError **error){ DeskmenuObject *dm_object = user_data; DeskmenuElementType element_type; const gchar **ncursor = attr_names, **vcursor = attr_values; GtkWidget *item, *menu; gint w, h; element_type = GPOINTER_TO_INT (g_hash_table_lookup (element_hash, element_name)); if ((dm_object->menu && !dm_object->current_menu) || (!dm_object->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 (dm_object->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 (!dm_object->menu) { /*if (strcmp (*ncursor, "size") == 0) { deskmenu->w = g_strdup (*vcursor); deskmenu->h = g_strdup (*vcursor); } else { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, deskmenu->w, deskmenu->h); }*/ dm_object->menu = gtk_menu_new (); g_object_set_data (G_OBJECT (dm_object->menu), "parent menu", NULL); dm_object->current_menu = dm_object->menu; } else { gchar *name = NULL; gchar *icon = NULL; gboolean name_exec = FALSE; gboolean icon_file = FALSE; while (*ncursor) { if (strcmp (*ncursor, "name") == 0) name = g_strdup (*vcursor); else if (strcmp (*ncursor, "icon") == 0) icon = g_strdup (*vcursor); else if ((strcmp (*ncursor, "mode") == 0) && (strcmp (*vcursor, "exec") == 0)) name_exec = TRUE; else if ((strcmp (*ncursor, "mode1") == 0) && (strcmp (*vcursor, "file") == 0)) icon_file = TRUE; else g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute: %s", *ncursor); ncursor++; vcursor++; } if (name_exec) { GtkWidget *label; GHook *hook; 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 (item), label); hook = g_hook_alloc (dm_object->show_hooks); hook->data = (gpointer) label; hook->func = (GHookFunc *) launcher_name_exec_update; g_hook_append (dm_object->show_hooks, hook); } else { if (name) item = gtk_image_menu_item_new_with_mnemonic (name); else item = gtk_image_menu_item_new_with_mnemonic (""); } if (icon) { if (icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL))); } else { gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU)); } } gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), item); menu = gtk_menu_new (); g_object_set_data (G_OBJECT (menu), "parent menu", dm_object->current_menu); dm_object->current_menu = menu; gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dm_object->current_menu); if (!dm_object->make_from_pipe) { GtkWidget *pin = gtk_tearoff_menu_item_new(); gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), pin); //add a pin menu item dm_object->pin_items = g_slist_prepend (dm_object->pin_items, pin); } else { if (gtk_menu_get_tearoff_state (GTK_MENU(dm_object->menu))) { GtkWidget *pin = gtk_tearoff_menu_item_new(); gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), pin); //add a pin menu item } } g_free (name); g_free (icon); } break; case DESKMENU_ELEMENT_SEPARATOR: if (dm_object->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; } else { gchar *name = NULL; gchar *icon = NULL; gboolean name_exec = FALSE; gboolean icon_file = FALSE; gboolean decorate = FALSE; gint w, h; item = gtk_separator_menu_item_new(); while (*ncursor) { if (strcmp (*ncursor, "name") == 0) { name = g_strdup (*vcursor); if (!decorate) { decorate = TRUE; } } else if (strcmp (*ncursor, "icon") == 0) { icon = g_strdup (*vcursor); if (!decorate) { decorate = TRUE; } } else if ((strcmp (*ncursor, "mode") == 0) && (strcmp (*vcursor, "exec") == 0)) name_exec = TRUE; else if ((strcmp (*ncursor, "mode1") == 0) && (strcmp (*vcursor, "file") == 0)) icon_file = TRUE; else g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute: %s", *ncursor); ncursor++; vcursor++; } if (decorate) { GtkHBox *box = gtk_hbox_new (FALSE, 3); gtk_container_add (GTK_CONTAINER(item), GTK_WIDGET(box)); if (name_exec) { GtkWidget *label; GHook *hook; label = gtk_label_new_with_mnemonic (NULL); g_object_set_data (G_OBJECT (label), "exec", g_strdup (name)); gtk_box_pack_end (GTK_BOX(box), label, TRUE, FALSE, 0); hook = g_hook_alloc (dm_object->show_hooks); hook->data = (gpointer) label; hook->func = (GHookFunc *) launcher_name_exec_update; g_hook_append (dm_object->show_hooks, hook); } else { gtk_box_pack_end (GTK_BOX(box), gtk_label_new_with_mnemonic (name), TRUE, FALSE, 0); } if (icon) { GtkWidget *image; if (icon_file) { gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &w, &h); image = gtk_image_new_from_pixbuf (gdk_pixbuf_new_from_file_at_size (parse_expand_tilde(icon), w, h, NULL)); } else { image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); } gtk_box_pack_start (GTK_BOX(box), image, FALSE, FALSE, 0); } gtk_widget_set_state (item, GTK_STATE_PRELIGHT); /*derive colors from menu hover*/ g_free (name); g_free (icon); } gtk_menu_shell_append (GTK_MENU_SHELL (dm_object->current_menu), item); } break; case DESKMENU_ELEMENT_ITEM: if (dm_object->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; } dm_object->current_item = g_slice_new0 (DeskmenuItem); while (*ncursor) { if (strcmp (*ncursor, "type") == 0) dm_object->current_item->type = GPOINTER_TO_INT (g_hash_table_lookup (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)) dm_object->current_item->name_exec = TRUE; ncursor++; vcursor++; } /* no break here to let it fall through */ case DESKMENU_ELEMENT_ICON: while (*ncursor) { if ((strcmp (*ncursor, "mode1") == 0) && (strcmp (*vcursor, "file") == 0)) dm_object->current_item->icon_file = TRUE; ncursor++; vcursor++; } /* no break here to let it fall through */ case DESKMENU_ELEMENT_VPICON: while (*ncursor) { if ((strcmp (*ncursor, "mode1") == 0) && (strcmp (*vcursor, "file") == 0)) dm_object->current_item->vpicon_file = TRUE; ncursor++; vcursor++; } /* no break here to let it fall through */ case DESKMENU_ELEMENT_COMMAND: while (*ncursor) { if ((strcmp (*ncursor, "mode2") == 0) && (strcmp (*vcursor, "pipe") == 0)) dm_object->current_item->command_pipe = TRUE; if (dm_object->current_item->command_pipe == TRUE && (strcmp (*ncursor, "cache") == 0) && (strcmp (*vcursor, "true") == 0)) dm_object->current_item->cache_output = TRUE; ncursor++; vcursor++; } /* no break here to let it fall through */ case DESKMENU_ELEMENT_WRAP: if (dm_object->current_item) dm_object->current_item->current_element = element_type; break; case DESKMENU_ELEMENT_THISVP: if (dm_object->current_item) dm_object->current_item->current_element = element_type; break; case DESKMENU_ELEMENT_MINIONLY: if (dm_object->current_item) dm_object->current_item->current_element = element_type; break; case DESKMENU_ELEMENT_QUANTITY: if (dm_object->current_item) dm_object->current_item->current_element = element_type; break; case DESKMENU_ELEMENT_SORT: if (dm_object->current_item) dm_object->current_item->current_element = element_type; break; case DESKMENU_ELEMENT_AGE: if (dm_object->current_item) dm_object->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; }}//dealing with empty attributesstatic voidtext (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error){ DeskmenuObject *dm_object = user_data; DeskmenuItem *item = dm_object->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_VPICON: if (!item->vpicon) item->vpicon = g_string_new_len (text, text_len); else g_string_append_len (item->vpicon, 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; case DESKMENU_ELEMENT_THISVP: if (!item->thisvp) item->thisvp = g_string_new_len (text, text_len); else g_string_append_len (item->thisvp, text, text_len); break; case DESKMENU_ELEMENT_MINIONLY: if (!item->mini_only) item->mini_only = g_string_new_len (text, text_len); else g_string_append_len (item->mini_only, text, text_len); break; case DESKMENU_ELEMENT_AGE: if (!item->age) item->age = g_string_new_len (text, text_len); else g_string_append_len (item->age, text, text_len); break; case DESKMENU_ELEMENT_QUANTITY: if (!item->quantity) item->quantity = g_string_new_len (text, text_len); else g_string_append_len (item->quantity, text, text_len); break; case DESKMENU_ELEMENT_SORT: if (!item->sort_type) item->sort_type = g_string_new_len (text, text_len); else g_string_append_len (item->sort_type, text, text_len); break; default: break; }}static voidend_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error){ DeskmenuElementType element_type; DeskmenuObject *dm_object = user_data; GtkWidget *parent; element_type = GPOINTER_TO_INT (g_hash_table_lookup (element_hash, element_name)); switch (element_type) { case DESKMENU_ELEMENT_MENU: g_return_if_fail (dm_object->current_item == NULL); parent = g_object_get_data (G_OBJECT (dm_object->current_menu), "parent menu"); dm_object->current_menu = parent; break;/* separator building is now dealt with in the beginning */ case DESKMENU_ELEMENT_ITEM: g_return_if_fail (dm_object->current_item != NULL); /* finally make the item ^_^ */ deskmenu_construct_item (dm_object); deskmenu_free_item(dm_object); break; default: break; }}/* 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, matches up words to types, note how there's no handler for pipe since it'sreplaced in its own chunk */static void deskmenu_init (Deskmenu *deskmenu){ deskmenu->pinnable = FALSE; deskmenu->file_cache = g_hash_table_new (g_str_hash, g_str_equal);}static voidset_up_item_hash (void) { item_hash= g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (item_hash, "launcher", GINT_TO_POINTER (DESKMENU_ITEM_LAUNCHER));#if HAVE_WNCK g_hash_table_insert (item_hash, "windowlist", GINT_TO_POINTER (DESKMENU_ITEM_WINDOWLIST)); g_hash_table_insert (item_hash, "viewportlist", GINT_TO_POINTER (DESKMENU_ITEM_VIEWPORTLIST));#endif g_hash_table_insert (item_hash, "documents", GINT_TO_POINTER (DESKMENU_ITEM_DOCUMENTS)); g_hash_table_insert (item_hash, "reload", GINT_TO_POINTER (DESKMENU_ITEM_RELOAD));}static voidset_up_element_hash (void) { element_hash = g_hash_table_new (g_str_hash, g_str_equal); g_hash_table_insert (element_hash, "menu", GINT_TO_POINTER (DESKMENU_ELEMENT_MENU)); g_hash_table_insert (element_hash, "separator", GINT_TO_POINTER (DESKMENU_ELEMENT_SEPARATOR)); g_hash_table_insert (element_hash, "item", GINT_TO_POINTER (DESKMENU_ELEMENT_ITEM)); g_hash_table_insert (element_hash, "name", GINT_TO_POINTER (DESKMENU_ELEMENT_NAME)); g_hash_table_insert (element_hash, "icon", GINT_TO_POINTER (DESKMENU_ELEMENT_ICON)); g_hash_table_insert (element_hash, "vpicon", GINT_TO_POINTER (DESKMENU_ELEMENT_VPICON)); g_hash_table_insert (element_hash, "command", GINT_TO_POINTER (DESKMENU_ELEMENT_COMMAND)); g_hash_table_insert (element_hash, "thisvp", GINT_TO_POINTER (DESKMENU_ELEMENT_THISVP)); g_hash_table_insert (element_hash, "minionly", GINT_TO_POINTER (DESKMENU_ELEMENT_MINIONLY)); g_hash_table_insert (element_hash, "wrap", GINT_TO_POINTER (DESKMENU_ELEMENT_WRAP)); g_hash_table_insert (element_hash, "sort", GINT_TO_POINTER (DESKMENU_ELEMENT_SORT)); g_hash_table_insert (element_hash, "quantity", GINT_TO_POINTER (DESKMENU_ELEMENT_QUANTITY)); g_hash_table_insert (element_hash, "age", GINT_TO_POINTER (DESKMENU_ELEMENT_AGE));}static DeskmenuObject*deskmenu_object_init (void) { DeskmenuObject *dm_object = g_slice_new0 (DeskmenuObject); dm_object->menu = NULL; dm_object->current_menu = NULL; dm_object->current_item = NULL; dm_object->make_from_pipe = FALSE; dm_object->pin_items = NULL; dm_object->show_hooks = g_slice_new0 (GHookList); g_hook_list_init (dm_object->show_hooks, sizeof (GHook)); return dm_object;}static DeskmenuObject*deskmenu_parse_file (gchar *filename){ GError *error = NULL; DeskmenuObject *dm_object = deskmenu_object_init(); GMarkupParseContext *context = g_markup_parse_context_new (&parser, 0, dm_object, NULL); gchar *text; gsize length; g_file_get_contents (filename, &text, &length, NULL); //cache already handled file existence check if (!g_markup_parse_context_parse (context, text, strlen(text), &error) || !g_markup_parse_context_end_parse (context, &error)) { g_printerr ("Parse failed with message: %s \n", error->message); g_error_free (error); exit (1); } g_free(text); //free the joined array g_markup_parse_context_free (context); //free the parser gtk_widget_show_all (dm_object->menu); return dm_object;}static DeskmenuObject *check_file_cache (Deskmenu *deskmenu, gchar *filename) { DeskmenuObject *dm_object; gchar *user_default = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (), "compiz", "boxmenu", "menu.xml", NULL); //TODO: add a size column to cache for possible autorefresh g_print("Checking cache...\n"); if (strlen(filename) == 0) filename = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (), "compiz", "boxmenu", "menu.xml", NULL); if (strcmp(filename, user_default) == 0) { g_print("Looking up default menu...\n"); /* set default filename to be [configdir]/compiz/boxmenu/menu.xml */ gboolean success = FALSE; if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { g_print("Getting default system menu...\n"); const gchar* const *cursor = g_get_system_config_dirs (); gchar *path = NULL; while (*cursor) { g_free (path); path = g_strdup (*cursor); filename = g_build_path (G_DIR_SEPARATOR_S, path, "compiz", "boxmenu", "menu.xml", NULL); if (g_file_test(filename, G_FILE_TEST_EXISTS)) { if (g_hash_table_lookup(deskmenu->file_cache, filename) == NULL) { g_hash_table_insert (deskmenu->file_cache, g_strdup(filename), deskmenu_parse_file(filename)); g_print("Prepared default system menu!\n"); } else { g_print("Retrieving default system menu!\n"); } g_free (path); success = TRUE; break; } cursor++; } } else { if (g_hash_table_lookup(deskmenu->file_cache, user_default) == NULL) { if (g_file_test(filename, G_FILE_TEST_EXISTS)) { g_print("Preparing default menu!\n"); g_hash_table_insert (deskmenu->file_cache, g_strdup(filename), deskmenu_parse_file(filename)); success = TRUE; } } else { g_print("Retrieving cached default user menu...\n"); success = TRUE; } } if (!success) { g_printerr ("Couldn't find a menu file...\n"); exit (1); } } else { if (g_hash_table_lookup(deskmenu->file_cache, filename) == NULL) { if (g_file_test(filename, G_FILE_TEST_EXISTS)) { g_print("Preparing new non-default menu...\n"); g_hash_table_insert (deskmenu->file_cache, g_strdup(filename), deskmenu_parse_file(filename)); } else { if (g_hash_table_lookup(deskmenu->file_cache, user_default) != NULL) { g_print("Couldn't find specified file, loading default...\n"); filename = user_default; } else { g_printerr ("Couldn't find a menu file...\n"); exit (1); } } } } dm_object = g_hash_table_lookup (deskmenu->file_cache, filename); g_printf("Done loading %s!\n", filename); return dm_object;}#if HAVE_WNCKgbooleandeskmenu_vplist (Deskmenu *deskmenu, gboolean toggle_wrap, gboolean toggle_images, gboolean toggle_file, gchar *viewport_icon) { DeskmenuVplist *vplist = deskmenu_vplist_initialize(toggle_wrap, toggle_images, toggle_file, g_strstrip(viewport_icon)); deskmenu_vplist_new (vplist); gtk_menu_popup (GTK_MENU (vplist->menu), NULL, NULL, NULL, NULL, 0, 0); return TRUE;}gbooleandeskmenu_windowlist (Deskmenu *deskmenu, gboolean images, gboolean thisvp, gboolean mini_only) { DeskmenuWindowlist *windowlist = deskmenu_windowlist_initialize (images, thisvp, mini_only); deskmenu_windowlist_new(windowlist); gtk_menu_popup (GTK_MENU (windowlist->menu), NULL, NULL, NULL, NULL, 0, 0); return TRUE;}#endifgbooleandeskmenu_documentlist (Deskmenu *deskmenu, gboolean images, gchar *command, int limit, int age, gchar *sort_type) { GtkWidget *menu = make_recent_documents_list (images, g_strdup(command), limit, age, g_strstrip(sort_type)); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, 0); return TRUE;}/* The show method */static voiddeskmenu_show (DeskmenuObject *dm_object, Deskmenu *deskmenu, GError **error){ GSList *list = NULL, *iterator = NULL; list = dm_object->pin_items; g_hook_list_invoke (dm_object->show_hooks, FALSE); if (deskmenu->pinnable) { gtk_menu_set_tearoff_state (GTK_MENU (dm_object->menu), TRUE); for (iterator = list; iterator; iterator = iterator->next) { gtk_widget_show (iterator->data); gtk_widget_set_no_show_all (iterator->data, FALSE); } } else { gtk_menu_popup (GTK_MENU (dm_object->menu), NULL, NULL, NULL, NULL, 0, 0); for (iterator = list; iterator; iterator = iterator->next) { gtk_widget_hide (iterator->data); gtk_widget_set_no_show_all (iterator->data, TRUE); } }}gbooleandeskmenu_pin (Deskmenu *deskmenu, gboolean pin){ deskmenu->pinnable = pin; return TRUE;}gbooleandeskmenu_reload (Deskmenu *deskmenu, GError **error){ gtk_main_quit (); return TRUE;}/* The dbus method for binary client */gbooleandeskmenu_control (Deskmenu *deskmenu, gchar *filename, gchar *workingd, GError **error){ chdir (workingd); DeskmenuObject *dm_object = check_file_cache (deskmenu, g_strstrip(filename)); deskmenu_show(dm_object, deskmenu, error); return TRUE;}//precache backend, currently needs GUIstatic voiddeskmenu_precache (Deskmenu *deskmenu, gchar *filename){ GError *error = NULL; GKeyFile *config = g_key_file_new (); int i; (void *)check_file_cache(deskmenu, ""); //always cache default menu g_print("Attempting to precache files in config..."); if (!filename) { filename = g_build_path (G_DIR_SEPARATOR_S, g_get_user_config_dir (), "compiz", "boxmenu", "precache.ini", NULL); } if (!g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, NULL)) { g_print("Configuration not found, will not precache files..."); } else { g_print("Configuration found! Starting precache..."); gchar **files = g_key_file_get_keys (config, "Files", NULL, &error); gchar *feed; while (files[i]) { feed = g_key_file_get_string (config, "Files", files[i], &error); (void *)check_file_cache(deskmenu, parse_expand_tilde(feed)); i++; } g_strfreev(files); g_free(feed); } g_key_file_free (config);}intmain (int argc, char **argv){ DBusGConnection *connection; GError *error = NULL; GObject *deskmenu; g_type_init (); 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); } g_print ("Starting the daemon...\n"); GOptionContext *context; gchar *file = NULL; GOptionEntry entries[] = { { "config", 'c', 0, G_OPTION_ARG_FILENAME, &file, "Use FILE instead of the default daemon configuration", "FILE" }, { NULL, 0, 0, 0, NULL, NULL, NULL } }; context = g_option_context_new (NULL); g_option_context_add_main_entries (context, entries, NULL); error = NULL; if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("option parsing failed: %s", error->message); g_error_free (error); return 1; } g_set_prgname ("Compiz Boxmenu"); g_option_context_free (context);#if HAVE_WNCK wnck_set_client_type (WNCK_CLIENT_TYPE_PAGER);#endif gtk_init (&argc, &argv); deskmenu = g_object_new (DESKMENU_TYPE, NULL); 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; set_up_element_hash(); set_up_item_hash(); deskmenu_precache(DESKMENU(deskmenu), file); gtk_main (); return 0;}