diff options
Diffstat (limited to 'mate-panel/panel-menu-button.c')
-rw-r--r-- | mate-panel/panel-menu-button.c | 1194 |
1 files changed, 1194 insertions, 0 deletions
diff --git a/mate-panel/panel-menu-button.c b/mate-panel/panel-menu-button.c new file mode 100644 index 00000000..f173d0b5 --- /dev/null +++ b/mate-panel/panel-menu-button.c @@ -0,0 +1,1194 @@ +/* + * panel-menu-button.c: panel menu button + * + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Authors: + * Mark McLoughlin <[email protected]> + */ + +#include <config.h> + +#include "panel-menu-button.h" + +#include <string.h> +#include <glib/gi18n.h> + +#include <matemenu-tree.h> + +#include <libpanel-util/panel-error.h> +#include <libpanel-util/panel-launch.h> +#include <libpanel-util/panel-show.h> + +#include "applet.h" +#include "panel-widget.h" +#include "panel-util.h" +#include "panel-profile.h" +#include "panel-globals.h" +#include "menu.h" +#include "panel-lockdown.h" +#include "panel-a11y.h" +#include "panel-icon-names.h" + +G_DEFINE_TYPE (PanelMenuButton, panel_menu_button, BUTTON_TYPE_WIDGET) + +#define PANEL_MENU_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PANEL_TYPE_MENU_BUTTON, PanelMenuButtonPrivate)) + +enum { + PROP_0, + PROP_MENU_PATH, + PROP_CUSTOM_ICON, + PROP_TOOLTIP, + PROP_USE_MENU_PATH, + PROP_USE_CUSTOM_ICON, + PROP_DND_ENABLED +}; + +typedef enum { + FIRST_MENU, + APPLICATIONS_MENU, +#define DEFAULT_MENU APPLICATIONS_MENU + SETTINGS_MENU, + LAST_MENU +} MenuPathRoot; + +typedef struct { + MenuPathRoot root_id; + char* scheme; + char* filename; +} MenuPathRootItem; + +static MenuPathRootItem root_items[] = { + {APPLICATIONS_MENU, "mate-applications", "mate-applications.menu"}, + {SETTINGS_MENU, "mate-settings", "mate-settings.menu"} +}; + +struct _PanelMenuButtonPrivate { + PanelToplevel *toplevel; + guint mateconf_notify; + char *applet_id; + + GtkWidget *menu; + + char *menu_path; + char *custom_icon; + char *tooltip; + + MenuPathRoot path_root; + guint use_menu_path : 1; + guint use_custom_icon : 1; + guint dnd_enabled : 1; +}; + +static void panel_menu_button_disconnect_from_mateconf (PanelMenuButton *button); +static void panel_menu_button_recreate_menu (PanelMenuButton *button); +static void panel_menu_button_set_icon (PanelMenuButton *button); + +static AtkObject *panel_menu_button_get_accessible (GtkWidget *widget); + +static const char * +panel_menu_path_root_to_filename (MenuPathRoot path_root) +{ + const char *retval; + int i; + + retval = NULL; + + for (i = 0; i < G_N_ELEMENTS (root_items); i++) { + if (root_items [i].root_id == path_root) { + retval = root_items [i].filename; + break; + } + } + + return retval; +} + +static const char * +panel_menu_filename_to_scheme (const char *filename) +{ + const char *retval; + int i; + + retval = NULL; + + if (!filename) + return retval; + + for (i = 0; i < G_N_ELEMENTS (root_items); i++) { + if (root_items [i].filename && + !strncmp (filename, root_items [i].filename, + strlen (root_items [i].filename))) { + retval = root_items [i].scheme; + break; + } + } + + return retval; +} + +static MenuPathRoot +panel_menu_scheme_to_path_root (const char *scheme) +{ + MenuPathRoot retval; + int i; + + retval = LAST_MENU; + + if (!scheme) + return retval; + + for (i = 0; i < G_N_ELEMENTS (root_items); i++) { + if (root_items [i].scheme && + !strncmp (scheme, root_items [i].scheme, + strlen (root_items [i].scheme))) { + retval = root_items [i].root_id; + break; + } + } + + return retval; +} + +static void +panel_menu_button_init (PanelMenuButton *button) +{ + button->priv = PANEL_MENU_BUTTON_GET_PRIVATE (button); + + button->priv->applet_id = NULL; + button->priv->toplevel = NULL; + button->priv->mateconf_notify = 0; + + button->priv->menu_path = NULL; + button->priv->custom_icon = NULL; + button->priv->tooltip = NULL; + + button->priv->path_root = LAST_MENU; + button->priv->use_menu_path = FALSE; + button->priv->use_custom_icon = FALSE; +} + +static void +panel_menu_button_finalize (GObject *object) +{ + PanelMenuButton *button = PANEL_MENU_BUTTON (object); + + panel_lockdown_notify_remove (G_CALLBACK (panel_menu_button_recreate_menu), + button); + + panel_menu_button_disconnect_from_mateconf (button); + + if (button->priv->menu) { + /* detaching the menu will kill our reference */ + gtk_menu_detach (GTK_MENU (button->priv->menu)); + button->priv->menu = NULL; + } + + g_free (button->priv->applet_id); + button->priv->applet_id = NULL; + + g_free (button->priv->menu_path); + button->priv->menu_path = NULL; + + g_free (button->priv->custom_icon); + button->priv->custom_icon = NULL; + + g_free (button->priv->tooltip); + button->priv->tooltip = NULL; + + G_OBJECT_CLASS (panel_menu_button_parent_class)->finalize (object); +} + +static void +panel_menu_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + PanelMenuButton *button; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (object)); + + button = PANEL_MENU_BUTTON (object); + + switch (prop_id) { + case PROP_MENU_PATH: + g_value_set_string (value, button->priv->menu_path); + break; + case PROP_CUSTOM_ICON: + g_value_set_string (value, button->priv->custom_icon); + break; + case PROP_TOOLTIP: + g_value_set_string (value, button->priv->tooltip); + break; + case PROP_USE_MENU_PATH: + g_value_set_boolean (value, button->priv->use_menu_path); + break; + case PROP_USE_CUSTOM_ICON: + g_value_set_boolean (value, button->priv->use_custom_icon); + break; + case PROP_DND_ENABLED: + g_value_set_boolean (value, button->priv->dnd_enabled); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +panel_menu_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + PanelMenuButton *button; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (object)); + + button = PANEL_MENU_BUTTON (object); + + switch (prop_id) { + case PROP_MENU_PATH: + panel_menu_button_set_menu_path (button, g_value_get_string (value)); + break; + case PROP_CUSTOM_ICON: + panel_menu_button_set_custom_icon (button, g_value_get_string (value)); + break; + case PROP_TOOLTIP: + panel_menu_button_set_tooltip (button, g_value_get_string (value)); + break; + case PROP_USE_MENU_PATH: + panel_menu_button_set_use_menu_path (button, g_value_get_boolean (value)); + break; + case PROP_USE_CUSTOM_ICON: + panel_menu_button_set_use_custom_icon (button, g_value_get_boolean (value)); + break; + case PROP_DND_ENABLED: + panel_menu_button_set_dnd_enabled (button, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +panel_menu_button_associate_panel (PanelMenuButton *button) +{ + PanelWidget *panel_widget = NULL; + + if (!button->priv->menu) + return; + + if (button->priv->toplevel) + panel_widget = panel_toplevel_get_panel_widget (button->priv->toplevel); + + mate_panel_applet_menu_set_recurse (GTK_MENU (button->priv->menu), "menu_panel", panel_widget); +} + +static void +panel_menu_button_parent_set (GtkWidget *widget, + GtkWidget *previous_parent) +{ + PanelMenuButton *button = PANEL_MENU_BUTTON (widget); + GtkWidget *parent; + + parent = gtk_widget_get_parent (widget); + g_return_if_fail (!parent || PANEL_IS_WIDGET (parent)); + + if (parent) + button->priv->toplevel = PANEL_WIDGET (parent)->toplevel; + else + button->priv->toplevel = NULL; + + panel_menu_button_associate_panel (button); + panel_menu_button_set_icon (button); + + if (GTK_WIDGET_CLASS (panel_menu_button_parent_class)->parent_set) + GTK_WIDGET_CLASS (panel_menu_button_parent_class)->parent_set (widget, previous_parent); +} + +static void +panel_menu_button_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time) +{ + char *drag_data; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (widget)); + + drag_data = g_strdup_printf ("MENU:%d", panel_find_applet_index (widget)); + + gtk_selection_data_set ( + selection_data, gtk_selection_data_get_target (selection_data), + 8, (guchar *) drag_data, strlen (drag_data)); + + g_free (drag_data); +} + +static void +panel_menu_button_menu_deactivated (PanelMenuButton *button) +{ + panel_toplevel_pop_autohide_disabler (button->priv->toplevel); + + GTK_BUTTON (button)->in_button = FALSE; + button_widget_set_ignore_leave (BUTTON_WIDGET (button), FALSE); +} + +static void +panel_menu_button_menu_detacher (PanelMenuButton *button) +{ + /* + * just in case someone still owns a reference to the + * menu (the menu may be up or some such other nonsense) + */ + g_signal_handlers_disconnect_by_func (button->priv->menu, + G_CALLBACK (panel_menu_button_menu_deactivated), + button); + + button->priv->menu = NULL; +} + +static GtkWidget * +panel_menu_button_create_menu (PanelMenuButton *button) +{ + PanelWidget *panel_widget; + + if (button->priv->menu) + return button->priv->menu; + + if (!button->priv->toplevel) + return NULL; + + panel_widget = panel_toplevel_get_panel_widget (button->priv->toplevel); + + if (button->priv->use_menu_path && + button->priv->path_root > FIRST_MENU && + button->priv->path_root < LAST_MENU) { + const char *filename; + + filename = panel_menu_path_root_to_filename (button->priv->path_root); + button->priv->menu = create_applications_menu (filename, + button->priv->menu_path, + TRUE); + } else + button->priv->menu = create_main_menu (panel_widget); + + gtk_menu_attach_to_widget (GTK_MENU (button->priv->menu), + GTK_WIDGET (button), + (GtkMenuDetachFunc) panel_menu_button_menu_detacher); + + panel_menu_button_associate_panel (button); + + g_signal_connect_swapped (button->priv->menu, "deactivate", + G_CALLBACK (panel_menu_button_menu_deactivated), + button); + + return button->priv->menu; +} + +static void +panel_menu_button_recreate_menu (PanelMenuButton *button) +{ + if (button->priv->menu) + gtk_widget_destroy (button->priv->menu); + button->priv->menu = NULL; +} + +void +panel_menu_button_popup_menu (PanelMenuButton *button, + guint n_button, + guint32 activate_time) +{ + GdkScreen *screen; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + panel_menu_button_create_menu (button); + + panel_toplevel_push_autohide_disabler (button->priv->toplevel); + + button_widget_set_ignore_leave (BUTTON_WIDGET (button), TRUE); + + screen = gtk_window_get_screen (GTK_WINDOW (button->priv->toplevel)); + gtk_menu_set_screen (GTK_MENU (button->priv->menu), screen); + + gtk_menu_popup (GTK_MENU (button->priv->menu), + NULL, + NULL, + (GtkMenuPositionFunc) mate_panel_applet_position_menu, + GTK_WIDGET (button), + n_button, + activate_time); +} + +static void +panel_menu_button_pressed (GtkButton *gtk_button) +{ + PanelMenuButton *button; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (gtk_button)); + + button = PANEL_MENU_BUTTON (gtk_button); + + if (GTK_BUTTON_CLASS (panel_menu_button_parent_class)->pressed) + GTK_BUTTON_CLASS (panel_menu_button_parent_class)->pressed (gtk_button); + + panel_menu_button_popup_menu (button, 0, gtk_get_current_event_time()); +} + +static void +panel_menu_button_clicked (GtkButton *gtk_button) +{ + PanelMenuButton *button; + GdkEvent *event; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (gtk_button)); + + button = PANEL_MENU_BUTTON (gtk_button); + + if (GTK_BUTTON_CLASS (panel_menu_button_parent_class)->clicked) + GTK_BUTTON_CLASS (panel_menu_button_parent_class)->clicked (gtk_button); + + if ((event = gtk_get_current_event ())) { + panel_menu_button_popup_menu (button, + event->button.button, + event->button.time); + gdk_event_free (event); + } else { + panel_menu_button_popup_menu (button, 1, GDK_CURRENT_TIME); + } +} + +static void +panel_menu_button_class_init (PanelMenuButtonClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GtkWidgetClass *widget_class = (GtkWidgetClass *) klass; + GtkButtonClass *button_class = (GtkButtonClass *) klass; + + gobject_class->finalize = panel_menu_button_finalize; + gobject_class->get_property = panel_menu_button_get_property; + gobject_class->set_property = panel_menu_button_set_property; + + widget_class->parent_set = panel_menu_button_parent_set; + widget_class->drag_data_get = panel_menu_button_drag_data_get; + widget_class->get_accessible = panel_menu_button_get_accessible; + + button_class->clicked = panel_menu_button_clicked; + button_class->pressed = panel_menu_button_pressed; + + g_type_class_add_private (klass, sizeof (PanelMenuButtonPrivate)); + + g_object_class_install_property ( + gobject_class, + PROP_MENU_PATH, + g_param_spec_string ("menu-path", + "Menu Path", + "The path from which to construct the menu", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_CUSTOM_ICON, + g_param_spec_string ("custom-icon", + "Custom Icon", + "The custom icon for the menu", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_TOOLTIP, + g_param_spec_string ("tooltip", + "Tooltip", + "Tooltip displayed for the menu", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_USE_MENU_PATH, + g_param_spec_boolean ("use-menu-path", + "Use Menu Path", + "Use the path specified by the menu-path property", + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_USE_CUSTOM_ICON, + g_param_spec_boolean ("use-custom-icon", + "Use Custom Icon", + "Use the icon specified by the custom-icon property", + FALSE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + gobject_class, + PROP_DND_ENABLED, + g_param_spec_boolean ("dnd-enabled", + "Drag and drop enabled", + "Whether or not drag and drop is enabled on the widget", + FALSE, + G_PARAM_READWRITE)); +} + +static void +panel_menu_button_mateconf_notify (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + PanelMenuButton *button) +{ + MateConfValue *value; + const char *key; + + key = panel_mateconf_basename (mateconf_entry_get_key (entry)); + + value = entry->value; + + if (!strcmp (key, "menu_path")) { + if (value && value->type == MATECONF_VALUE_STRING) + panel_menu_button_set_menu_path (button, + mateconf_value_get_string (value)); + } else if (!strcmp (key, "custom_icon")) { + if (value && value->type == MATECONF_VALUE_STRING) + panel_menu_button_set_custom_icon (button, + mateconf_value_get_string (value)); + } else if (!strcmp (key, "tooltip")) { + if (value && value->type == MATECONF_VALUE_STRING) + panel_menu_button_set_tooltip (button, + mateconf_value_get_string (value)); + } else if (!strcmp (key, "use_menu_path")) { + if (value && value->type == MATECONF_VALUE_BOOL) + panel_menu_button_set_use_menu_path (button, + mateconf_value_get_bool (value)); + } else if (!strcmp (key, "use_custom_icon")) { + if (value && value->type == MATECONF_VALUE_BOOL) + panel_menu_button_set_use_custom_icon (button, + mateconf_value_get_bool (value)); + } +} + +static void +panel_menu_button_connect_to_mateconf (PanelMenuButton *button) +{ + MateConfClient *client; + const char *key; + + client = panel_mateconf_get_client (); + + key = panel_mateconf_sprintf (PANEL_CONFIG_DIR "/objects/%s", + button->priv->applet_id); + mateconf_client_add_dir (client, key, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + button->priv->mateconf_notify = + mateconf_client_notify_add (client, key, + (MateConfClientNotifyFunc) panel_menu_button_mateconf_notify, + button, NULL, NULL); +} + +static void +panel_menu_button_disconnect_from_mateconf (PanelMenuButton *button) +{ + MateConfClient *client; + const char *key; + + if (!button->priv->mateconf_notify) + return; + + client = panel_mateconf_get_client (); + + key = panel_mateconf_sprintf (PANEL_CONFIG_DIR "/objects/%s", + button->priv->applet_id); + + mateconf_client_notify_remove (client, button->priv->mateconf_notify); + button->priv->mateconf_notify = 0; + + mateconf_client_remove_dir (client, key, NULL); +} + +static void +panel_menu_button_load (const char *menu_path, + gboolean use_menu_path, + const char *custom_icon, + gboolean use_custom_icon, + const char *tooltip, + PanelWidget *panel, + gboolean locked, + int position, + gboolean exactpos, + const char *id) +{ + PanelMenuButton *button; + AppletInfo *info; + + g_return_if_fail (panel != NULL); + + button = g_object_new (PANEL_TYPE_MENU_BUTTON, + "menu-path", menu_path, + "custom-icon", custom_icon, + "tooltip", tooltip, + "use-menu-path", use_menu_path, + "use-custom-icon", use_custom_icon, + "has-arrow", TRUE, + NULL); + + info = mate_panel_applet_register (GTK_WIDGET (button), NULL, NULL, + panel, locked, position, exactpos, + PANEL_OBJECT_MENU, id); + if (!info) { + gtk_widget_destroy (GTK_WIDGET (button)); + return; + } + + button->priv->applet_id = g_strdup (info->id); + + mate_panel_applet_add_callback (info, "help", GTK_STOCK_HELP, _("_Help"), NULL); + + if (panel_is_program_in_path ("alacarte") || + panel_is_program_in_path ("matemenu-simple-editor")) + mate_panel_applet_add_callback (info, "edit", NULL, + _("_Edit Menus"), NULL); + + panel_widget_set_applet_expandable (panel, GTK_WIDGET (button), FALSE, TRUE); + panel_widget_set_applet_size_constrained (panel, GTK_WIDGET (button), TRUE); + + panel_menu_button_connect_to_mateconf (button); + + panel_lockdown_notify_add (G_CALLBACK (panel_menu_button_recreate_menu), + button); +} + +static char * +panel_menu_button_get_icon (PanelMenuButton *button) +{ + MateMenuTreeDirectory *directory; + char *retval; + + retval = NULL; + + if (button->priv->use_custom_icon && + button->priv->custom_icon) + retval = g_strdup (button->priv->custom_icon); + + if (!retval && + button->priv->use_menu_path && + button->priv->menu_path && + panel_menu_button_create_menu (button)) { + directory = g_object_get_data (G_OBJECT (button->priv->menu), + "panel-menu-tree-directory"); + + if (!directory) { + MateMenuTree *tree; + + if ((tree = g_object_get_data (G_OBJECT (button->priv->menu), + "panel-menu-tree"))) { + directory = matemenu_tree_get_directory_from_path (tree, + button->priv->menu_path); + g_object_set_data_full (G_OBJECT (button->priv->menu), + "panel-menu-tree-directory", + directory, + (GDestroyNotify) matemenu_tree_item_unref); + } + } + + if (directory) + retval = g_strdup (matemenu_tree_directory_get_icon (directory)); + } + + if (!retval) + retval = g_strdup (PANEL_ICON_MAIN_MENU); + + return retval; +} + +static void +panel_menu_button_set_icon (PanelMenuButton *button) +{ + char *icon_path; + + icon_path = panel_menu_button_get_icon (button); + button_widget_set_icon_name (BUTTON_WIDGET (button), icon_path); + + g_free (icon_path); +} + +static const char * +split_menu_uri (const char *menu_uri, + char **menu_scheme) +{ + char *p; + + if (!menu_uri) + return NULL; + + p = strchr (menu_uri, ':'); + + if (!p || p == menu_uri) + return NULL; + + if (menu_scheme) + *menu_scheme = g_strndup (menu_uri, p - menu_uri); + + if (*(++p) != '/') + return NULL; + + while (*p != '\0' && *(p + 1) == '/') p++; + + return p; +} + +void +panel_menu_button_set_menu_path (PanelMenuButton *button, + const char *menu_uri) +{ + const char *menu_path; + char *scheme; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + scheme = NULL; + menu_path = split_menu_uri (menu_uri, &scheme); + + if (!scheme) + return; + + button->priv->path_root = panel_menu_scheme_to_path_root (scheme); + g_free (scheme); + + if (!button->priv->menu_path && (!menu_path || !menu_path [0])) + return; + + if (button->priv->menu_path && menu_path && + !strcmp (button->priv->menu_path, menu_path)) + return; + + g_free (button->priv->menu_path); + button->priv->menu_path = NULL; + + button->priv->menu_path = g_strdup (menu_path); + + if (button->priv->menu) + gtk_menu_detach (GTK_MENU (button->priv->menu)); + button->priv->menu = NULL; + + panel_menu_button_set_icon (button); +} + +void +panel_menu_button_set_custom_icon (PanelMenuButton *button, + const char *custom_icon) +{ + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + g_free (button->priv->custom_icon); + button->priv->custom_icon = NULL; + + if (custom_icon && custom_icon [0]) + button->priv->custom_icon = g_strdup (custom_icon); + + panel_menu_button_set_icon (button); +} + +void +panel_menu_button_set_tooltip (PanelMenuButton *button, + const char *tooltip) +{ + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + g_free (button->priv->tooltip); + button->priv->tooltip = NULL; + + if (tooltip && tooltip [0]) { + button->priv->tooltip = g_strdup (tooltip); + panel_util_set_tooltip_text (GTK_WIDGET (button), tooltip); + } +} + +void +panel_menu_button_set_use_menu_path (PanelMenuButton *button, + gboolean use_menu_path) +{ + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + use_menu_path = use_menu_path != FALSE; + + if (button->priv->use_menu_path == use_menu_path) + return; + + button->priv->use_menu_path = use_menu_path; + + if (button->priv->menu) + gtk_menu_detach (GTK_MENU (button->priv->menu)); + button->priv->menu = NULL; + + panel_menu_button_set_icon (button); +} + +gboolean +panel_menu_button_get_use_menu_path (PanelMenuButton *button) +{ + g_return_val_if_fail (PANEL_IS_MENU_BUTTON (button), FALSE); + + return button->priv->use_menu_path; +} + +void +panel_menu_button_set_use_custom_icon (PanelMenuButton *button, + gboolean use_custom_icon) +{ + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + button->priv->use_custom_icon = use_custom_icon != FALSE; + + panel_menu_button_set_icon (button); +} + +void +panel_menu_button_load_from_mateconf (PanelWidget *panel, + gboolean locked, + int position, + gboolean exactpos, + const char *id) +{ + MateConfClient *client; + const char *key; + char *menu_path; + char *custom_icon; + char *tooltip; + gboolean use_menu_path; + gboolean use_custom_icon; + + client = panel_mateconf_get_client (); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "menu_path"); + menu_path = mateconf_client_get_string (client, key, NULL); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "custom_icon"); + custom_icon = mateconf_client_get_string (client, key, NULL); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "tooltip"); + tooltip = mateconf_client_get_string (client, key, NULL); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "use_menu_path"); + use_menu_path = mateconf_client_get_bool (client, key, NULL); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "use_custom_icon"); + use_custom_icon = mateconf_client_get_bool (client, key, NULL); + + panel_menu_button_load (menu_path, + use_menu_path, + custom_icon, + use_custom_icon, + tooltip, + panel, + locked, + position, + exactpos, + id); + + g_free (menu_path); + g_free (custom_icon); + g_free (tooltip); +} + +gboolean +panel_menu_button_create (PanelToplevel *toplevel, + int position, + const char *filename, + const char *menu_path, + gboolean use_menu_path, + const char *tooltip) +{ + MateConfClient *client; + const char *scheme; + const char *key; + char *id; + + client = panel_mateconf_get_client (); + + id = panel_profile_prepare_object (PANEL_OBJECT_MENU, toplevel, position, FALSE); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "use_menu_path"); + mateconf_client_set_bool (client, key, use_menu_path, NULL); + + scheme = panel_menu_filename_to_scheme (filename); + + if (filename && !scheme) { + g_warning ("Failed to find menu scheme for %s\n", filename); + g_free (id); + return FALSE; + } + + if (use_menu_path && menu_path && menu_path [0] && scheme) { + char *menu_uri; + + menu_uri = g_strconcat (scheme, ":", menu_path, NULL); + + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "menu_path"); + mateconf_client_set_string (client, key, menu_uri, NULL); + + g_free (menu_uri); + } + + if (tooltip && tooltip [0]) { + key = panel_mateconf_full_key (PANEL_MATECONF_OBJECTS, id, "tooltip"); + mateconf_client_set_string (client, key, tooltip, NULL); + } + + panel_profile_add_to_list (PANEL_MATECONF_OBJECTS, id); + g_free (id); + + return TRUE; +} + +void +panel_menu_button_invoke_menu (PanelMenuButton *button, + const char *callback_name) +{ + GdkScreen *screen; + + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + g_return_if_fail (callback_name != NULL); + + screen = gtk_widget_get_screen (GTK_WIDGET (button)); + + if (!strcmp (callback_name, "help")) { + panel_show_help (screen, "user-guide", "gospanel-37", NULL); + + } else if (!strcmp (callback_name, "edit")) { + GError *error = NULL; + + panel_launch_desktop_file_with_fallback ("alacarte.desktop", + "alacarte", + screen, &error); + if (error) { + g_error_free (error); + panel_launch_desktop_file_with_fallback ( + "matemenu-simple-editor.desktop", + "matemenu-simple-editor", + screen, NULL); + } + } +} + +void +panel_menu_button_set_dnd_enabled (PanelMenuButton *button, + gboolean dnd_enabled) +{ + g_return_if_fail (PANEL_IS_MENU_BUTTON (button)); + + dnd_enabled = dnd_enabled != FALSE; + + if (button->priv->dnd_enabled == dnd_enabled) + return; + + if (dnd_enabled) { + static GtkTargetEntry dnd_targets [] = { + { "application/x-mate-panel-applet-internal", 0, 0 } + }; + char *icon; + + gtk_widget_set_has_window (GTK_WIDGET (button), TRUE); + gtk_drag_source_set (GTK_WIDGET (button), GDK_BUTTON1_MASK, + dnd_targets, 1, + GDK_ACTION_COPY | GDK_ACTION_MOVE); + + icon = panel_menu_button_get_icon (button); + if (icon != NULL) { + gtk_drag_source_set_icon_name (GTK_WIDGET (button), + icon); + g_free (icon); + } + + gtk_widget_set_has_window (GTK_WIDGET (button), FALSE); + } else + gtk_drag_source_unset (GTK_WIDGET (button)); +} + +/* + * An AtkObject implementation for PanelMenuButton. + * We need all this just so we can create the menu in ref_child() + * + * See http://bugzilla.gnome.org/show_bug.cgi?id=138535 for details + * + * If we ever remove the on-demand creation of the menu, we should + * can just remove all this again + */ + +#define PANEL_IS_MENU_BUTTON_ACCESSIBLE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), panel_menu_button_accessible_get_type ())) + +static GType panel_menu_button_accessible_get_type (void); +static gpointer parent_accessible_class = NULL; + +static int +panel_menu_button_accessible_get_n_children (AtkObject *obj) +{ + g_return_val_if_fail (PANEL_IS_MENU_BUTTON_ACCESSIBLE (obj), 0); + +#if GTK_CHECK_VERSION (2, 21, 0) + return gtk_accessible_get_widget (GTK_ACCESSIBLE (obj)) ? 1 : 0; +#else + return GTK_ACCESSIBLE (obj)->widget ? 1 : 0; +#endif +} + +static AtkObject * +panel_menu_button_accessible_ref_child (AtkObject *obj, + int index) +{ + PanelMenuButton *button; + GtkWidget *menu; + + g_return_val_if_fail (PANEL_IS_MENU_BUTTON_ACCESSIBLE (obj), NULL); + + if (index != 0) + return NULL; + +#if GTK_CHECK_VERSION (2, 21, 0) + if (!(button = PANEL_MENU_BUTTON (gtk_accessible_get_widget (GTK_ACCESSIBLE (obj))))) +#else + if (!(button = PANEL_MENU_BUTTON (GTK_ACCESSIBLE (obj)->widget))) +#endif + return NULL; + + if (!(menu = panel_menu_button_create_menu (button))) + return NULL; + /* + * This ensures that the menu is populated with all menu items + */ + g_signal_emit_by_name (menu, "show", NULL); + + return g_object_ref (gtk_widget_get_accessible (menu)); +} + +static const gchar* panel_menu_button_accessible_get_name(AtkObject* obj) +{ + const char* name; + + name = ATK_OBJECT_CLASS(parent_accessible_class)->get_name(obj); + + if (name == NULL) + { + name = _("Main Menu"); + } + + return name; +} + +static void +panel_menu_button_accessible_class_init (AtkObjectClass *klass) +{ + klass->get_n_children = panel_menu_button_accessible_get_n_children; + klass->ref_child = panel_menu_button_accessible_ref_child; + klass->get_name = panel_menu_button_accessible_get_name; + + parent_accessible_class = g_type_class_peek_parent (klass); +} + +static GType +panel_menu_button_accessible_get_type (void) +{ + static GType type = 0; + + if (!type) { + GTypeInfo type_info = { 0 }; + GType accessible_parent_type; + + type_info.class_init = + (GClassInitFunc) panel_menu_button_accessible_class_init; + + accessible_parent_type = + panel_a11y_query_accessible_parent_type (PANEL_TYPE_MENU_BUTTON, + &type_info); + + type = g_type_register_static (accessible_parent_type, + "PanelMenuButtonAccessible", + &type_info, 0); + } + + return type; +} + +static AtkObject * +panel_menu_button_accessible_new (GObject *obj) +{ + AtkObject *accessible; + + g_return_val_if_fail (PANEL_IS_MENU_BUTTON (obj), NULL); + + accessible = g_object_new (panel_menu_button_accessible_get_type (), NULL); + atk_object_initialize (accessible, obj); + + return accessible; +} + +static void +panel_menu_button_accessible_factory_class_init (AtkObjectFactoryClass *klass) +{ + klass->create_accessible = panel_menu_button_accessible_new; + klass->get_accessible_type = panel_menu_button_accessible_get_type; +} + +static GType +panel_menu_button_accessible_factory_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (AtkObjectFactoryClass), + NULL, + NULL, + (GClassInitFunc) panel_menu_button_accessible_factory_class_init, + NULL, + NULL, + sizeof (AtkObjectFactory), + 0, + NULL, + NULL + }; + + type = g_type_register_static (ATK_TYPE_OBJECT_FACTORY, + "PanelMenuButtonAccessibleFactory", + &info, 0); + } + + return type; +} + +static AtkObject * +panel_menu_button_get_accessible (GtkWidget *widget) +{ + static gboolean first_time = TRUE; + + g_return_val_if_fail (widget != NULL, NULL); + + if (first_time && panel_a11y_get_is_a11y_enabled (widget)) + atk_registry_set_factory_type (atk_get_default_registry (), + PANEL_TYPE_MENU_BUTTON, + panel_menu_button_accessible_factory_get_type ()); + + first_time = FALSE; + + return GTK_WIDGET_CLASS (panel_menu_button_parent_class)->get_accessible (widget); +} |