From c51ef797a707f4e2c6f9688d4378f2b0e9898a66 Mon Sep 17 00:00:00 2001 From: Perberos Date: Thu, 1 Dec 2011 22:56:10 -0300 Subject: moving from https://github.com/perberos/mate-desktop-environment --- mate-panel/panel-menu-items.c | 1618 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1618 insertions(+) create mode 100644 mate-panel/panel-menu-items.c (limited to 'mate-panel/panel-menu-items.c') diff --git a/mate-panel/panel-menu-items.c b/mate-panel/panel-menu-items.c new file mode 100644 index 00000000..7f790676 --- /dev/null +++ b/mate-panel/panel-menu-items.c @@ -0,0 +1,1618 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2005 Vincent Untz + * + * 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: + * Vincent Untz + * + * Based on code from panel-menu-bar.c + */ + +/* + * TODO: + * + drag and drop loses icon for URIs + * + drag and drop of bookmarks/network places/removable media should create + * a menu button + * + if a menu is open and gets updated, it should reappear and not just + * disappear + */ + +#include + +#include "panel-menu-items.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "menu.h" +#include "panel-action-button.h" +#include "panel-globals.h" +#include "panel-icon-names.h" +#include "panel-lockdown.h" +#include "panel-recent.h" +#include "panel-stock-icons.h" +#include "panel-util.h" + +#define BOOKMARKS_FILENAME ".gtk-bookmarks" +#define DESKTOP_IS_HOME_DIR_DIR "/apps/caja/preferences" +#define DESKTOP_IS_HOME_DIR_KEY "/apps/caja/preferences/desktop_is_home_dir" +#define NAMES_DIR "/apps/caja/desktop" +#define HOME_NAME_KEY "/apps/caja/desktop/home_icon_name" +#define COMPUTER_NAME_KEY "/apps/caja/desktop/computer_icon_name" +#define MAX_ITEMS_OR_SUBMENU 5 +#define MAX_BOOKMARK_ITEMS 100 + +G_DEFINE_TYPE(PanelPlaceMenuItem, panel_place_menu_item, GTK_TYPE_IMAGE_MENU_ITEM) +G_DEFINE_TYPE(PanelDesktopMenuItem, panel_desktop_menu_item, GTK_TYPE_IMAGE_MENU_ITEM) + +#define PANEL_PLACE_MENU_ITEM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), PANEL_TYPE_PLACE_MENU_ITEM, PanelPlaceMenuItemPrivate)) +#define PANEL_DESKTOP_MENU_ITEM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), PANEL_TYPE_DESKTOP_MENU_ITEM, PanelDesktopMenuItemPrivate)) + +struct _PanelPlaceMenuItemPrivate { + GtkWidget *menu; + PanelWidget *panel; + + GtkRecentManager *recent_manager; + + GFileMonitor *bookmarks_monitor; + + GVolumeMonitor *volume_monitor; + gulong drive_changed_id; + gulong drive_connected_id; + gulong drive_disconnected_id; + gulong volume_added_id; + gulong volume_changed_id; + gulong volume_removed_id; + gulong mount_added_id; + gulong mount_changed_id; + gulong mount_removed_id; + + guint use_image : 1; +}; + +struct _PanelDesktopMenuItemPrivate { + GtkWidget *menu; + PanelWidget *panel; + + guint use_image : 1; + guint append_lock_logout : 1; +}; + +static void activate_uri_on_screen(const char* uri, GdkScreen* screen) +{ + panel_show_uri(screen, uri, gtk_get_current_event_time(), NULL); +} + +static void +activate_uri (GtkWidget *menuitem, + const char *uri) +{ + activate_uri_on_screen (uri, menuitem_to_screen (menuitem)); +} + +static void +activate_path (GtkWidget *menuitem, + const char *path) +{ + char *uri; + + uri = g_filename_to_uri (path, NULL, NULL); + activate_uri_on_screen (uri, menuitem_to_screen (menuitem)); + g_free (uri); +} + +static void +activate_home_uri (GtkWidget *menuitem, + gpointer data) +{ + activate_path (menuitem, g_get_home_dir ()); +} + +static void +activate_desktop_uri (GtkWidget *menuitem, + gpointer data) +{ + activate_path (menuitem, + g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)); +} + +static void +panel_menu_items_append_from_desktop (GtkWidget *menu, + char *path, + char *force_name, + gboolean use_icon) +{ + GKeyFile *key_file; + gboolean loaded; + GtkWidget *item; + char *path_freeme; + char *full_path; + char *uri; + char *type; + gboolean is_application; + char *tryexec; + char *icon; + char *name; + char *comment; + + path_freeme = NULL; + + key_file = g_key_file_new (); + + if (g_path_is_absolute (path)) { + loaded = g_key_file_load_from_file (key_file, path, + G_KEY_FILE_NONE, NULL); + full_path = path; + } else { + char *lookup_file; + char *desktop_path; + + if (!g_str_has_suffix (path, ".desktop")) { + desktop_path = g_strconcat (path, ".desktop", NULL); + } else { + desktop_path = path; + } + + lookup_file = g_strconcat ("applications", G_DIR_SEPARATOR_S, + desktop_path, NULL); + loaded = g_key_file_load_from_data_dirs (key_file, lookup_file, + &path_freeme, + G_KEY_FILE_NONE, + NULL); + full_path = path_freeme; + g_free (lookup_file); + + if (desktop_path != path) + g_free (desktop_path); + } + + if (!loaded) { + g_key_file_free (key_file); + if (path_freeme) + g_free (path_freeme); + return; + } + + /* For Application desktop files, respect TryExec */ + type = panel_key_file_get_string (key_file, "Type"); + if (!type) { + g_key_file_free (key_file); + if (path_freeme) + g_free (path_freeme); + return; + } + is_application = (strcmp (type, "Application") == 0); + g_free (type); + + if (is_application) { + tryexec = panel_key_file_get_string (key_file, "TryExec"); + if (tryexec) { + char *prog; + + prog = g_find_program_in_path (tryexec); + g_free (tryexec); + + if (!prog) { + /* FIXME: we could add some file monitor magic, + * so that the menu items appears when the + * program appears, but that's really complex + * for not a huge benefit */ + g_key_file_free (key_file); + if (path_freeme) + g_free (path_freeme); + return; + } + + g_free (prog); + } + } + + /* Now, simply build the menu item */ + icon = panel_key_file_get_locale_string (key_file, "Icon"); + comment = panel_key_file_get_locale_string (key_file, "Comment"); + + if (PANEL_GLIB_STR_EMPTY (force_name)) + name = panel_key_file_get_locale_string (key_file, "Name"); + else + name = g_strdup (force_name); + + if (use_icon) { + item = panel_image_menu_item_new (); + } else { + item = gtk_image_menu_item_new (); + } + + setup_menu_item_with_icon (item, panel_menu_icon_get_size (), + icon, NULL, NULL, name); + + panel_util_set_tooltip_text (item, comment); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + g_signal_connect_data (item, "activate", + G_CALLBACK (panel_menu_item_activate_desktop_file), + g_strdup (full_path), + (GClosureNotify) g_free, 0); + g_signal_connect (G_OBJECT (item), "button_press_event", + G_CALLBACK (menu_dummy_button_press_event), NULL); + + uri = g_filename_to_uri (full_path, NULL, NULL); + + setup_uri_drag (item, uri, icon); + g_free (uri); + + g_key_file_free (key_file); + + if (icon) + g_free (icon); + + if (name) + g_free (name); + + if (comment) + g_free (comment); + + if (path_freeme) + g_free (path_freeme); +} + +static void +panel_menu_items_append_place_item (const char *icon_name, + GIcon *gicon, + const char *title, + const char *tooltip, + GtkWidget *menu, + GCallback callback, + const char *uri) +{ + GtkWidget *item; + char *user_data; + + item = panel_image_menu_item_new (); + setup_menu_item_with_icon (item, + panel_menu_icon_get_size (), + icon_name, NULL, gicon, + title); + + panel_util_set_tooltip_text (item, tooltip); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + user_data = g_strdup (uri); + g_signal_connect_data (item, "activate", callback, user_data, + (GClosureNotify) g_free, 0); + + g_signal_connect (G_OBJECT (item), "button_press_event", + G_CALLBACK (menu_dummy_button_press_event), NULL); + + setup_uri_drag (item, uri, icon_name); +} + +static GtkWidget * +panel_menu_items_create_action_item_full (PanelActionButtonType action_type, + const char *label, + const char *tooltip) +{ + GtkWidget *item; + + if (panel_action_get_is_disabled (action_type)) + return NULL; + + item = gtk_image_menu_item_new (); + setup_menu_item_with_icon (item, + panel_menu_icon_get_size (), + panel_action_get_icon_name (action_type), + NULL, NULL, + label ? label : panel_action_get_text (action_type)); + + panel_util_set_tooltip_text (item, + tooltip ? + tooltip : + panel_action_get_tooltip (action_type)); + + g_signal_connect (item, "activate", + panel_action_get_invoke (action_type), NULL); + g_signal_connect (G_OBJECT (item), "button_press_event", + G_CALLBACK (menu_dummy_button_press_event), NULL); + setup_internal_applet_drag (item, action_type); + + return item; +} + +static GtkWidget * +panel_menu_items_create_action_item (PanelActionButtonType action_type) +{ + return panel_menu_items_create_action_item_full (action_type, + NULL, NULL); +} + +static void +panel_place_menu_item_append_gtk_bookmarks (GtkWidget *menu) +{ + typedef struct { + char *full_uri; + char *label; + } PanelBookmark; + + GtkWidget *add_menu; + char *filename; + GIOChannel *io_channel; + GHashTable *table; + int i; + GSList *lines = NULL; + GSList *add_bookmarks, *l; + PanelBookmark *bookmark; + + filename = g_build_filename (g_get_home_dir (), + BOOKMARKS_FILENAME, NULL); + + io_channel = g_io_channel_new_file (filename, "r", NULL); + g_free (filename); + + if (!io_channel) + return; + + /* We use a hard limit to avoid having users shooting their + * own feet, and to avoid crashing the system if a misbehaving + * application creates a big bookmars file. + */ + for (i = 0; i < MAX_BOOKMARK_ITEMS; i++) { + char *contents; + gsize length; + gsize terminator_pos; + GIOStatus status; + + status = g_io_channel_read_line (io_channel, &contents, &length, &terminator_pos, NULL); + + if (status != G_IO_STATUS_NORMAL) + break; + + if (length == 0) + break; + + /* Clear the line terminator (\n), if any */ + if (terminator_pos > 0) + contents[terminator_pos] = '\0'; + + lines = g_slist_prepend (lines, contents); + } + + g_io_channel_shutdown (io_channel, FALSE, NULL); + g_io_channel_unref (io_channel); + + if (!lines) + return; + + lines = g_slist_reverse (lines); + + table = g_hash_table_new (g_str_hash, g_str_equal); + add_bookmarks = NULL; + + for (l = lines; l; l = l->next) { + char *line = (char*) l->data; + + if (line[0] && !g_hash_table_lookup (table, line)) { + GFile *file; + char *space; + char *label; + gboolean keep; + + g_hash_table_insert (table, line, line); + + space = strchr (line, ' '); + if (space) { + *space = '\0'; + label = g_strdup (space + 1); + } else { + label = NULL; + } + + keep = FALSE; + + if (g_str_has_prefix (line, "x-caja-search:")) + keep = TRUE; + + if (!keep) { + file = g_file_new_for_uri (line); + keep = !g_file_is_native (file) || + g_file_query_exists (file, NULL); + g_object_unref (file); + } + + if (!keep) { + if (label) + g_free (label); + continue; + } + + bookmark = g_malloc (sizeof (PanelBookmark)); + bookmark->full_uri = g_strdup (line); + bookmark->label = label; + add_bookmarks = g_slist_prepend (add_bookmarks, bookmark); + } + } + + g_hash_table_destroy (table); + g_slist_foreach (lines, (GFunc) g_free, NULL); + g_slist_free (lines); + + add_bookmarks = g_slist_reverse (add_bookmarks); + + if (g_slist_length (add_bookmarks) <= MAX_ITEMS_OR_SUBMENU) { + add_menu = menu; + } else { + GtkWidget *item; + + item = gtk_image_menu_item_new (); + setup_menu_item_with_icon (item, panel_menu_icon_get_size (), + PANEL_ICON_BOOKMARKS, NULL, NULL, + _("Bookmarks")); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + add_menu = create_empty_menu (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), add_menu); + } + + for (l = add_bookmarks; l; l = l->next) { + char *display_name; + char *tooltip; + char *label; + char *icon; + GFile *file; + GIcon *gicon; + + bookmark = l->data; + + file = g_file_new_for_uri (bookmark->full_uri); + display_name = g_file_get_parse_name (file); + g_object_unref (file); + /* Translators: %s is a URI */ + tooltip = g_strdup_printf (_("Open '%s'"), display_name); + g_free (display_name); + + label = NULL; + if (bookmark->label) { + label = g_strdup (g_strstrip (bookmark->label)); + if (!label [0]) { + g_free (label); + label = NULL; + } + } + + if (!label) { + label = panel_util_get_label_for_uri (bookmark->full_uri); + + if (!label) { + g_free (tooltip); + g_free (bookmark->full_uri); + if (bookmark->label) + g_free (bookmark->label); + g_free (bookmark); + continue; + } + } + + icon = panel_util_get_icon_for_uri (bookmark->full_uri); + /*FIXME: we should probably get a GIcon if possible, so that we + * have customized icons for cd-rom, eg */ + if (!icon) + icon = g_strdup (PANEL_ICON_FOLDER); + + gicon = g_themed_icon_new_with_default_fallbacks (icon); + + //FIXME: drag and drop will be broken for x-caja-search uris + panel_menu_items_append_place_item (icon, gicon, + label, + tooltip, + add_menu, + G_CALLBACK (activate_uri), + bookmark->full_uri); + + g_free (icon); + g_object_unref (gicon); + g_free (tooltip); + g_free (label); + g_free (bookmark->full_uri); + if (bookmark->label) + g_free (bookmark->label); + g_free (bookmark); + } + + g_slist_free (add_bookmarks); +} + +static void +drive_poll_for_media_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GdkScreen *screen; + GError *error; + char *primary; + char *name; + + error = NULL; + if (!g_drive_poll_for_media_finish (G_DRIVE (source_object), + res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + screen = GDK_SCREEN (user_data); + + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to scan %s for media changes"), + name); + g_free (name); + panel_error_dialog (NULL, screen, + "cannot_scan_drive", TRUE, + primary, error->message); + g_free (primary); + } + g_error_free (error); + } + + //FIXME: should we mount the volume and activate the root of the new + //mount? +} + +static void +panel_menu_item_rescan_drive (GtkWidget *menuitem, + GDrive *drive) +{ + g_drive_poll_for_media (drive, NULL, + drive_poll_for_media_cb, + menuitem_to_screen (menuitem)); +} + +static void +panel_menu_item_append_drive (GtkWidget *menu, + GDrive *drive) +{ + GtkWidget *item; + GIcon *icon; + char *title; + char *tooltip; + + icon = g_drive_get_icon (drive); + title = g_drive_get_name (drive); + + item = panel_image_menu_item_new (); + setup_menu_item_with_icon (item, + panel_menu_icon_get_size (), + NULL, NULL, icon, + title); + g_object_unref (icon); + + tooltip = g_strdup_printf (_("Rescan %s"), title); + panel_util_set_tooltip_text (item, tooltip); + g_free (tooltip); + + g_free (title); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + g_signal_connect_data (item, "activate", + G_CALLBACK (panel_menu_item_rescan_drive), + g_object_ref (drive), + (GClosureNotify) g_object_unref, 0); + + g_signal_connect (G_OBJECT (item), "button_press_event", + G_CALLBACK (menu_dummy_button_press_event), NULL); +} + +typedef struct { + GdkScreen *screen; + GMountOperation *mount_op; +} PanelVolumeMountData; + +static void +volume_mount_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + PanelVolumeMountData *mount_data = user_data; + GError *error; + + error = NULL; + if (!g_volume_mount_finish (G_VOLUME (source_object), res, &error)) { + char *primary; + char *name; + + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_volume_get_name (G_VOLUME (source_object)); + primary = g_strdup_printf (_("Unable to mount %s"), + name); + g_free (name); + + panel_error_dialog (NULL, mount_data->screen, + "cannot_mount_volume", TRUE, + primary, error->message); + g_free (primary); + } + g_error_free (error); + } else { + GMount *mount; + GFile *root; + char *rooturi; + + mount = g_volume_get_mount (G_VOLUME (source_object)); + root = g_mount_get_root (mount); + rooturi = g_file_get_uri (root); + activate_uri_on_screen (rooturi, mount_data->screen); + g_object_unref (mount); + g_object_unref (root); + g_free (rooturi); + } + + g_object_unref (mount_data->mount_op); + g_slice_free (PanelVolumeMountData, mount_data); +} + +static void +panel_menu_item_mount_volume (GtkWidget *menuitem, + GVolume *volume) +{ + PanelVolumeMountData *mount_data; + + mount_data = g_slice_new (PanelVolumeMountData); + mount_data->screen = menuitem_to_screen (menuitem); + mount_data->mount_op = gtk_mount_operation_new (NULL); + gtk_mount_operation_set_screen (GTK_MOUNT_OPERATION (mount_data->mount_op), + mount_data->screen); + + g_volume_mount (volume, G_MOUNT_MOUNT_NONE, mount_data->mount_op, NULL, + volume_mount_cb, mount_data); +} + +static void +panel_menu_item_append_volume (GtkWidget *menu, + GVolume *volume) +{ + GtkWidget *item; + GIcon *icon; + char *title; + char *tooltip; + + icon = g_volume_get_icon (volume); + title = g_volume_get_name (volume); + + item = panel_image_menu_item_new (); + setup_menu_item_with_icon (item, + panel_menu_icon_get_size (), + NULL, NULL, icon, + title); + g_object_unref (icon); + + tooltip = g_strdup_printf (_("Mount %s"), title); + panel_util_set_tooltip_text (item, tooltip); + g_free (tooltip); + + g_free (title); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + + g_signal_connect_data (item, "activate", + G_CALLBACK (panel_menu_item_mount_volume), + g_object_ref (volume), + (GClosureNotify) g_object_unref, 0); + + g_signal_connect (G_OBJECT (item), "button_press_event", + G_CALLBACK (menu_dummy_button_press_event), NULL); +} + +static void +panel_menu_item_append_mount (GtkWidget *menu, + GMount *mount) +{ + GFile *root; + GIcon *icon; + char *display_name; + char *activation_uri; + + icon = g_mount_get_icon (mount); + display_name = g_mount_get_name (mount); + + root = g_mount_get_root (mount); + activation_uri = g_file_get_uri (root); + g_object_unref (root); + + panel_menu_items_append_place_item (NULL, icon, + display_name, + display_name, //FIXME tooltip + menu, + G_CALLBACK (activate_uri), + activation_uri); + + g_object_unref (icon); + g_free (display_name); + g_free (activation_uri); +} + +typedef enum { + PANEL_GIO_DRIVE, + PANEL_GIO_VOLUME, + PANEL_GIO_MOUNT +} PanelGioItemType; + +typedef struct { + PanelGioItemType type; + union { + GDrive *drive; + GVolume *volume; + GMount *mount; + } u; +} PanelGioItem; + +/* this is loosely based on update_places() from caja-places-sidebar.c */ +static void +panel_place_menu_item_append_local_gio (PanelPlaceMenuItem *place_item, + GtkWidget *menu) +{ + GList *l; + GList *ll; + GList *drives; + GDrive *drive; + GList *volumes; + GVolume *volume; + GList *mounts; + GMount *mount; + GSList *items; + GSList *sl; + PanelGioItem *item; + GtkWidget *add_menu; + + items = NULL; + + /* first go through all connected drives */ + drives = g_volume_monitor_get_connected_drives (place_item->priv->volume_monitor); + for (l = drives; l != NULL; l = l->next) { + drive = l->data; + + volumes = g_drive_get_volumes (drive); + if (volumes != NULL) { + for (ll = volumes; ll != NULL; ll = ll->next) { + volume = ll->data; + mount = g_volume_get_mount (volume); + item = g_slice_new (PanelGioItem); + if (mount != NULL) { + item->type = PANEL_GIO_MOUNT; + item->u.mount = mount; + } else { + /* Do show the unmounted volumes; this + * is so the user can mount it (in case + * automounting is off). + * + * Also, even if automounting is + * enabled, this gives a visual cue + * that the user should remember to + * yank out the media if he just + * unmounted it. + */ + item->type = PANEL_GIO_VOLUME; + item->u.volume = g_object_ref (volume); + } + items = g_slist_prepend (items, item); + g_object_unref (volume); + } + g_list_free (volumes); + } else { + if (g_drive_is_media_removable (drive) && + !g_drive_is_media_check_automatic (drive)) { + /* If the drive has no mountable volumes and we + * cannot detect media change.. we display the + * drive so the user can manually poll the + * drive by clicking on it..." + * + * This is mainly for drives like floppies + * where media detection doesn't work.. but + * it's also for human beings who like to turn + * off media detection in the OS to save + * battery juice. + */ + item = g_slice_new (PanelGioItem); + item->type = PANEL_GIO_DRIVE; + item->u.drive = g_object_ref (drive); + items = g_slist_prepend (items, item); + } + } + g_object_unref (drive); + } + g_list_free (drives); + + /* add all volumes that is not associated with a drive */ + volumes = g_volume_monitor_get_volumes (place_item->priv->volume_monitor); + for (l = volumes; l != NULL; l = l->next) { + volume = l->data; + drive = g_volume_get_drive (volume); + if (drive != NULL) { + g_object_unref (volume); + g_object_unref (drive); + continue; + } + mount = g_volume_get_mount (volume); + item = g_slice_new (PanelGioItem); + if (mount != NULL) { + item->type = PANEL_GIO_MOUNT; + item->u.mount = mount; + } else { + /* see comment above in why we add an icon for an + * unmounted mountable volume */ + item->type = PANEL_GIO_VOLUME; + item->u.volume = g_object_ref (volume); + } + items = g_slist_prepend (items, item); + g_object_unref (volume); + } + g_list_free (volumes); + + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (place_item->priv->volume_monitor); + for (l = mounts; l != NULL; l = l->next) { + GFile *root; + + mount = l->data; + + if (g_mount_is_shadowed (mount)) { + g_object_unref (mount); + continue; + } + + volume = g_mount_get_volume (mount); + if (volume != NULL) { + g_object_unref (volume); + g_object_unref (mount); + continue; + } + + root = g_mount_get_root (mount); + if (!g_file_is_native (root)) { + g_object_unref (root); + g_object_unref (mount); + continue; + } + g_object_unref (root); + + item = g_slice_new (PanelGioItem); + item->type = PANEL_GIO_MOUNT; + item->u.mount = mount; + items = g_slist_prepend (items, item); + } + g_list_free (mounts); + + /* now that we have everything, add the items inline or in a submenu */ + items = g_slist_reverse (items); + + if (g_slist_length (items) <= MAX_ITEMS_OR_SUBMENU) { + add_menu = menu; + } else { + GtkWidget *item; + + item = gtk_image_menu_item_new (); + setup_menu_item_with_icon (item, panel_menu_icon_get_size (), + PANEL_ICON_REMOVABLE_MEDIA, + NULL, NULL, + _("Removable Media")); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + add_menu = create_empty_menu (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), add_menu); + } + + for (sl = items; sl; sl = sl->next) { + item = sl->data; + switch (item->type) { + case PANEL_GIO_DRIVE: + panel_menu_item_append_drive (add_menu, item->u.drive); + g_object_unref (item->u.drive); + break; + case PANEL_GIO_VOLUME: + panel_menu_item_append_volume (add_menu, item->u.volume); + g_object_unref (item->u.volume); + break; + case PANEL_GIO_MOUNT: + panel_menu_item_append_mount (add_menu, item->u.mount); + g_object_unref (item->u.mount); + break; + default: + g_assert_not_reached (); + } + g_slice_free (PanelGioItem, item); + } + + g_slist_free (items); +} + +/* this is loosely based on update_places() from caja-places-sidebar.c */ +static void +panel_place_menu_item_append_remote_gio (PanelPlaceMenuItem *place_item, + GtkWidget *menu) +{ + GtkWidget *add_menu; + GList *mounts, *l; + GMount *mount; + GSList *add_mounts, *sl; + + /* add mounts that has no volume (/etc/mtab mounts, ftp, sftp,...) */ + mounts = g_volume_monitor_get_mounts (place_item->priv->volume_monitor); + add_mounts = NULL; + + for (l = mounts; l; l = l->next) { + GVolume *volume; + GFile *root; + + mount = l->data; + + if (g_mount_is_shadowed (mount)) { + g_object_unref (mount); + continue; + } + + volume = g_mount_get_volume (mount); + if (volume != NULL) { + g_object_unref (volume); + g_object_unref (mount); + continue; + } + + root = g_mount_get_root (mount); + if (g_file_is_native (root)) { + g_object_unref (root); + g_object_unref (mount); + continue; + } + g_object_unref (root); + + + add_mounts = g_slist_prepend (add_mounts, mount); + } + add_mounts = g_slist_reverse (add_mounts); + + if (g_slist_length (add_mounts) <= MAX_ITEMS_OR_SUBMENU) { + add_menu = menu; + } else { + GtkWidget *item; + + item = panel_image_menu_item_new (); + setup_menu_item_with_icon (item, panel_menu_icon_get_size (), + PANEL_ICON_NETWORK_SERVER, + NULL, NULL, + _("Network Places")); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + gtk_widget_show (item); + + add_menu = create_empty_menu (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), add_menu); + } + + for (sl = add_mounts; sl; sl = sl->next) { + mount = sl->data; + panel_menu_item_append_mount (add_menu, mount); + g_object_unref (mount); + } + + g_slist_free (add_mounts); + g_list_free (mounts); +} + + +static GtkWidget * +panel_place_menu_item_create_menu (PanelPlaceMenuItem *place_item) +{ + GtkWidget *places_menu; + GtkWidget *item; + char *mateconf_name; + char *name; + char *uri; + GFile *file; + + places_menu = panel_create_menu (); + + file = g_file_new_for_path (g_get_home_dir ()); + uri = g_file_get_uri (file); + name = panel_util_get_label_for_uri (uri); + g_object_unref (file); + + panel_menu_items_append_place_item (PANEL_ICON_HOME, NULL, + name, + _("Open your personal folder"), + places_menu, + G_CALLBACK (activate_home_uri), + uri); + g_free (name); + g_free (uri); + + if (!mateconf_client_get_bool (panel_mateconf_get_client (), + DESKTOP_IS_HOME_DIR_KEY, + NULL)) { + file = g_file_new_for_path (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)); + uri = g_file_get_uri (file); + g_object_unref (file); + + panel_menu_items_append_place_item ( + PANEL_ICON_DESKTOP, NULL, + /* Translators: Desktop is used here as in + * "Desktop Folder" (this is not the Desktop + * environment). */ + C_("Desktop Folder", "Desktop"), + _("Open the contents of your desktop in a folder"), + places_menu, + G_CALLBACK (activate_desktop_uri), + /* FIXME: if the dir changes, we'd need to update the drag data since the uri is not the same */ + uri); + g_free (uri); + } + + panel_place_menu_item_append_gtk_bookmarks (places_menu); + add_menu_separator (places_menu); + + mateconf_name = mateconf_client_get_string (panel_mateconf_get_client (), + COMPUTER_NAME_KEY, + NULL); + panel_menu_items_append_from_desktop (places_menu, + "caja-computer.desktop", + mateconf_name, + TRUE); + if (mateconf_name) + g_free (mateconf_name); + + panel_place_menu_item_append_local_gio (place_item, places_menu); + add_menu_separator (places_menu); + + panel_menu_items_append_from_desktop (places_menu, + "network-scheme.desktop", + NULL, + TRUE); + panel_place_menu_item_append_remote_gio (place_item, places_menu); + + if (panel_is_program_in_path ("caja-connect-server")) { + item = panel_menu_items_create_action_item (PANEL_ACTION_CONNECT_SERVER); + if (item != NULL) + gtk_menu_shell_append (GTK_MENU_SHELL (places_menu), + item); + } + + add_menu_separator (places_menu); + + panel_menu_items_append_from_desktop (places_menu, + "mate-search-tool.desktop", + NULL, + FALSE); + + panel_recent_append_documents_menu (places_menu, + place_item->priv->recent_manager); + + return places_menu; +} + +static void +panel_place_menu_item_recreate_menu (GtkWidget *widget) +{ + PanelPlaceMenuItem *place_item; + + place_item = PANEL_PLACE_MENU_ITEM (widget); + + if (place_item->priv->menu) { + gtk_widget_destroy (place_item->priv->menu); + place_item->priv->menu = panel_place_menu_item_create_menu (place_item); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (place_item), + place_item->priv->menu); + mate_panel_applet_menu_set_recurse (GTK_MENU (place_item->priv->menu), + "menu_panel", + place_item->priv->panel); + } +} + +static void +panel_place_menu_item_key_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + GtkWidget *place_item) +{ + panel_place_menu_item_recreate_menu (place_item); +} + +static void +panel_place_menu_item_gtk_bookmarks_changed (GFileMonitor *handle, + GFile *file, + GFile *other_file, + GFileMonitorEvent event, + gpointer user_data) +{ + panel_place_menu_item_recreate_menu (GTK_WIDGET (user_data)); +} + +static void +panel_place_menu_item_drives_changed (GVolumeMonitor *monitor, + GDrive *drive, + GtkWidget *place_menu) +{ + panel_place_menu_item_recreate_menu (place_menu); +} + +static void +panel_place_menu_item_volumes_changed (GVolumeMonitor *monitor, + GVolume *volume, + GtkWidget *place_menu) +{ + panel_place_menu_item_recreate_menu (place_menu); +} + +static void +panel_place_menu_item_mounts_changed (GVolumeMonitor *monitor, + GMount *mount, + GtkWidget *place_menu) +{ + panel_place_menu_item_recreate_menu (place_menu); +} + +static void +panel_desktop_menu_item_append_menu (GtkWidget *menu, + gpointer data) +{ + PanelDesktopMenuItem *parent; + gboolean add_separator; + GList *children; + GList *last; + + parent = PANEL_DESKTOP_MENU_ITEM (data); + + add_separator = FALSE; + children = gtk_container_get_children (GTK_CONTAINER (menu)); + last = g_list_last (children); + + if (last != NULL) + add_separator = !GTK_IS_SEPARATOR (GTK_WIDGET (last->data)); + + g_list_free (children); + + if (add_separator) + add_menu_separator (menu); + + panel_menu_items_append_from_desktop (menu, "yelp.desktop", NULL, FALSE); + panel_menu_items_append_from_desktop (menu, "mate-about.desktop", NULL, FALSE); + + if (parent->priv->append_lock_logout) + panel_menu_items_append_lock_logout (menu); +} + +static GtkWidget * +panel_desktop_menu_item_create_menu (PanelDesktopMenuItem *desktop_item) +{ + GtkWidget *desktop_menu; + + desktop_menu = create_applications_menu ("mate-settings.menu", NULL, FALSE); + + g_object_set_data (G_OBJECT (desktop_menu), + "panel-menu-append-callback", + panel_desktop_menu_item_append_menu); + g_object_set_data (G_OBJECT (desktop_menu), + "panel-menu-append-callback-data", + desktop_item); + + return desktop_menu; +} + +static void +panel_desktop_menu_item_recreate_menu (PanelDesktopMenuItem *desktop_item) +{ + if (desktop_item->priv->menu) { + gtk_widget_destroy (desktop_item->priv->menu); + desktop_item->priv->menu = panel_desktop_menu_item_create_menu (desktop_item); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (desktop_item), + desktop_item->priv->menu); + mate_panel_applet_menu_set_recurse (GTK_MENU (desktop_item->priv->menu), + "menu_panel", + desktop_item->priv->panel); + } +} + +static void +panel_place_menu_item_finalize (GObject *object) +{ + PanelPlaceMenuItem *menuitem = (PanelPlaceMenuItem *) object; + + mateconf_client_remove_dir (panel_mateconf_get_client (), + DESKTOP_IS_HOME_DIR_DIR, + NULL); + mateconf_client_remove_dir (panel_mateconf_get_client (), + NAMES_DIR, + NULL); + + if (menuitem->priv->bookmarks_monitor != NULL) { + g_file_monitor_cancel (menuitem->priv->bookmarks_monitor); + g_object_unref (menuitem->priv->bookmarks_monitor); + } + menuitem->priv->bookmarks_monitor = NULL; + + if (menuitem->priv->drive_changed_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->drive_changed_id); + menuitem->priv->drive_changed_id = 0; + + if (menuitem->priv->drive_connected_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->drive_connected_id); + menuitem->priv->drive_connected_id = 0; + + if (menuitem->priv->drive_disconnected_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->drive_disconnected_id); + menuitem->priv->drive_disconnected_id = 0; + + if (menuitem->priv->volume_added_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->volume_added_id); + menuitem->priv->volume_added_id = 0; + + if (menuitem->priv->volume_changed_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->volume_changed_id); + menuitem->priv->volume_changed_id = 0; + + if (menuitem->priv->volume_removed_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->volume_removed_id); + menuitem->priv->volume_removed_id = 0; + + if (menuitem->priv->mount_added_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->mount_added_id); + menuitem->priv->mount_added_id = 0; + + if (menuitem->priv->mount_changed_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->mount_changed_id); + menuitem->priv->mount_changed_id = 0; + + if (menuitem->priv->mount_removed_id) + g_signal_handler_disconnect (menuitem->priv->volume_monitor, + menuitem->priv->mount_removed_id); + menuitem->priv->mount_removed_id = 0; + + if (menuitem->priv->volume_monitor != NULL) + g_object_unref (menuitem->priv->volume_monitor); + menuitem->priv->volume_monitor = NULL; + + G_OBJECT_CLASS (panel_place_menu_item_parent_class)->finalize (object); +} + +static void +panel_desktop_menu_item_finalize (GObject *object) +{ + PanelDesktopMenuItem *menuitem = (PanelDesktopMenuItem *) object; + + if (menuitem->priv->append_lock_logout) + panel_lockdown_notify_remove (G_CALLBACK (panel_desktop_menu_item_recreate_menu), + menuitem); + G_OBJECT_CLASS (panel_desktop_menu_item_parent_class)->finalize (object); +} + +static void +panel_place_menu_item_init (PanelPlaceMenuItem *menuitem) +{ + GFile *bookmark; + char *bookmarks_filename; + GError *error; + + menuitem->priv = PANEL_PLACE_MENU_ITEM_GET_PRIVATE (menuitem); + + mateconf_client_add_dir (panel_mateconf_get_client (), + DESKTOP_IS_HOME_DIR_DIR, + MATECONF_CLIENT_PRELOAD_NONE, + NULL); + mateconf_client_add_dir (panel_mateconf_get_client (), + NAMES_DIR, + MATECONF_CLIENT_PRELOAD_NONE, + NULL); + + panel_mateconf_notify_add_while_alive (HOME_NAME_KEY, + (MateConfClientNotifyFunc) panel_place_menu_item_key_changed, + G_OBJECT (menuitem)); + panel_mateconf_notify_add_while_alive (DESKTOP_IS_HOME_DIR_KEY, + (MateConfClientNotifyFunc) panel_place_menu_item_key_changed, + G_OBJECT (menuitem)); + panel_mateconf_notify_add_while_alive (COMPUTER_NAME_KEY, + (MateConfClientNotifyFunc) panel_place_menu_item_key_changed, + G_OBJECT (menuitem)); + + menuitem->priv->recent_manager = gtk_recent_manager_get_default (); + + bookmarks_filename = g_build_filename (g_get_home_dir (), + BOOKMARKS_FILENAME, NULL); + bookmark = g_file_new_for_path (bookmarks_filename); + + error = NULL; + menuitem->priv->bookmarks_monitor = g_file_monitor_file + (bookmark, + G_FILE_MONITOR_NONE, + NULL, + &error); + if (error) { + g_warning ("Failed to add file monitor for %s: %s\n", + bookmarks_filename, error->message); + g_error_free (error); + } else { + g_signal_connect (G_OBJECT (menuitem->priv->bookmarks_monitor), + "changed", + (GCallback) panel_place_menu_item_gtk_bookmarks_changed, + menuitem); + } + + g_object_unref (bookmark); + g_free (bookmarks_filename); + + menuitem->priv->volume_monitor = g_volume_monitor_get (); + + menuitem->priv->drive_changed_id = g_signal_connect (menuitem->priv->volume_monitor, + "drive-changed", + G_CALLBACK (panel_place_menu_item_drives_changed), + menuitem); + menuitem->priv->drive_connected_id = g_signal_connect (menuitem->priv->volume_monitor, + "drive-connected", + G_CALLBACK (panel_place_menu_item_drives_changed), + menuitem); + menuitem->priv->drive_disconnected_id = g_signal_connect (menuitem->priv->volume_monitor, + "drive-disconnected", + G_CALLBACK (panel_place_menu_item_drives_changed), + menuitem); + menuitem->priv->volume_added_id = g_signal_connect (menuitem->priv->volume_monitor, + "volume-added", + G_CALLBACK (panel_place_menu_item_volumes_changed), + menuitem); + menuitem->priv->volume_changed_id = g_signal_connect (menuitem->priv->volume_monitor, + "volume-changed", + G_CALLBACK (panel_place_menu_item_volumes_changed), + menuitem); + menuitem->priv->volume_removed_id = g_signal_connect (menuitem->priv->volume_monitor, + "volume-removed", + G_CALLBACK (panel_place_menu_item_volumes_changed), + menuitem); + menuitem->priv->mount_added_id = g_signal_connect (menuitem->priv->volume_monitor, + "mount-added", + G_CALLBACK (panel_place_menu_item_mounts_changed), + menuitem); + menuitem->priv->mount_changed_id = g_signal_connect (menuitem->priv->volume_monitor, + "mount-changed", + G_CALLBACK (panel_place_menu_item_mounts_changed), + menuitem); + menuitem->priv->mount_removed_id = g_signal_connect (menuitem->priv->volume_monitor, + "mount-removed", + G_CALLBACK (panel_place_menu_item_mounts_changed), + menuitem); + +} + +static void +panel_desktop_menu_item_init (PanelDesktopMenuItem *menuitem) +{ + menuitem->priv = PANEL_DESKTOP_MENU_ITEM_GET_PRIVATE (menuitem); +} + +static void +panel_place_menu_item_class_init (PanelPlaceMenuItemClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = panel_place_menu_item_finalize; + + g_type_class_add_private (klass, sizeof (PanelPlaceMenuItemPrivate)); +} + +static void +panel_desktop_menu_item_class_init (PanelDesktopMenuItemClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = panel_desktop_menu_item_finalize; + + g_type_class_add_private (klass, sizeof (PanelDesktopMenuItemPrivate)); +} + +GtkWidget* panel_place_menu_item_new(gboolean use_image) +{ + PanelPlaceMenuItem* menuitem; + GtkWidget* image; + + menuitem = g_object_new(PANEL_TYPE_PLACE_MENU_ITEM, NULL); + + if (use_image) + { + image = gtk_image_new_from_icon_name(PANEL_ICON_FOLDER, panel_menu_icon_get_size()); + } + else + { + image = NULL; + } + + setup_menuitem(GTK_WIDGET(menuitem), image ? panel_menu_icon_get_size() : GTK_ICON_SIZE_INVALID, image, _("Places")); + + menuitem->priv->use_image = use_image; + + menuitem->priv->menu = panel_place_menu_item_create_menu(menuitem); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menuitem->priv->menu); + + return GTK_WIDGET(menuitem); +} + +GtkWidget * +panel_desktop_menu_item_new (gboolean use_image, + gboolean append_lock_logout) +{ + PanelDesktopMenuItem *menuitem; + GtkWidget *image; + + menuitem = g_object_new (PANEL_TYPE_DESKTOP_MENU_ITEM, NULL); + + if (use_image) + image = gtk_image_new_from_icon_name ("computer", + panel_menu_icon_get_size ()); + else + image = NULL; + + setup_menuitem (GTK_WIDGET (menuitem), + image ? panel_menu_icon_get_size () : GTK_ICON_SIZE_INVALID, + image, + _("System")); + + menuitem->priv->use_image = use_image; + + menuitem->priv->append_lock_logout = append_lock_logout; + if (append_lock_logout) + panel_lockdown_notify_add (G_CALLBACK (panel_desktop_menu_item_recreate_menu), + menuitem); + + menuitem->priv->menu = panel_desktop_menu_item_create_menu (menuitem); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), + menuitem->priv->menu); + + return GTK_WIDGET (menuitem); +} + +void +panel_place_menu_item_set_panel (GtkWidget *item, + PanelWidget *panel) +{ + PanelPlaceMenuItem *place_item; + + place_item = PANEL_PLACE_MENU_ITEM (item); + + place_item->priv->panel = panel; + mate_panel_applet_menu_set_recurse (GTK_MENU (place_item->priv->menu), + "menu_panel", panel); +} + +void +panel_desktop_menu_item_set_panel (GtkWidget *item, + PanelWidget *panel) +{ + PanelDesktopMenuItem *desktop_item; + + desktop_item = PANEL_DESKTOP_MENU_ITEM (item); + + desktop_item->priv->panel = panel; + mate_panel_applet_menu_set_recurse (GTK_MENU (desktop_item->priv->menu), + "menu_panel", panel); +} + +void +panel_menu_items_append_lock_logout (GtkWidget *menu) +{ + gboolean separator_inserted; + GList *children; + GList *last; + GtkWidget *item; + const char *translate; + char *label; + char *tooltip; + + separator_inserted = FALSE; + children = gtk_container_get_children (GTK_CONTAINER (menu)); + last = g_list_last (children); + if (last != NULL) { + separator_inserted = GTK_IS_SEPARATOR (GTK_WIDGET (last->data)); + } + g_list_free (children); + + if (panel_lock_screen_action_available("lock")) + { + item = panel_menu_items_create_action_item(PANEL_ACTION_LOCK); + + if (item != NULL) + { + if (!separator_inserted) + { + add_menu_separator(menu); + separator_inserted = TRUE; + } + + gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + } + } + + if (panel_lockdown_get_disable_log_out ()) + return; + /* Below this, we only have log out/shutdown items */ + + /* Translators: translate "1" (msgctxt: "panel:showusername") to anything + * but "1" if "Log Out %s" doesn't make any sense in your + * language (where %s is a username). + */ + translate = C_("panel:showusername", "1"); + if (strcmp (translate, "1") == 0) { + const char *user_name; + + user_name = g_get_real_name (); + if (!user_name || !user_name [0]) + user_name = g_get_user_name (); + + /* keep those strings in sync with the ones in + * panel-action-button.c */ + /* Translators: this string is used ONLY if you translated + * "1" (msgctxt: "panel:showusername") to "1" */ + label = g_strdup_printf (_("Log Out %s..."), + g_get_user_name ()); + /* Translators: this string is used ONLY if you translated + * "1" (msgctxt: "panel:showusername") to "1" */ + tooltip = g_strdup_printf (_("Log out %s of this session to " + "log in as a different user"), + user_name); + } else { + label = NULL; + tooltip = NULL; + } + + item = panel_menu_items_create_action_item_full (PANEL_ACTION_LOGOUT, + label, tooltip); + g_free (label); + g_free (tooltip); + + if (item != NULL) { + if (!separator_inserted) { + add_menu_separator (menu); + separator_inserted = TRUE; + } + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + } + + item = panel_menu_items_create_action_item (PANEL_ACTION_SHUTDOWN); + if (item != NULL) { + if (!separator_inserted) + add_menu_separator (menu); + + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + } +} + +void +panel_menu_item_activate_desktop_file (GtkWidget *menuitem, + const char *path) +{ + panel_launch_desktop_file (path, menuitem_to_screen (menuitem), NULL); +} -- cgit v1.2.1