summaryrefslogtreecommitdiff
path: root/drivemount/src
diff options
context:
space:
mode:
Diffstat (limited to 'drivemount/src')
-rw-r--r--drivemount/src/Makefile.am43
-rw-r--r--drivemount/src/drive-button.c982
-rw-r--r--drivemount/src/drive-button.h71
-rw-r--r--drivemount/src/drive-list.c522
-rw-r--r--drivemount/src/drive-list.h73
-rw-r--r--drivemount/src/drivemount.c230
6 files changed, 1921 insertions, 0 deletions
diff --git a/drivemount/src/Makefile.am b/drivemount/src/Makefile.am
new file mode 100644
index 00000000..28b273c0
--- /dev/null
+++ b/drivemount/src/Makefile.am
@@ -0,0 +1,43 @@
+NULL =
+
+AM_CPPFLAGS = \
+ -I. \
+ -I$(srcdir) \
+ -DDRIVEMOUNT_RESOURCE_PATH=\""/org/mate/mate-applets/drivemount/"\" \
+ ${WARN_CFLAGS} \
+ $(MATE_APPLETS4_CFLAGS) \
+ $(NULL)
+
+libexec_PROGRAMS = mate-drivemount-applet
+
+BUILT_SOURCES = \
+ drivemount-resources.c \
+ drivemount-resources.h \
+ $(NULL)
+
+nodist_mate_drivemount_applet_SOURCES = \
+ $(BUILT_SOURCES) \
+ $(NULL)
+
+mate_drivemount_applet_SOURCES = \
+ drivemount.c \
+ drive-list.c \
+ drive-list.h \
+ drive-button.c \
+ drive-button.h \
+ $(NULL)
+
+mate_drivemount_applet_LDADD = \
+ $(MATE_APPLETS4_LIBS)
+
+drivemount-resources.c: ../data/drivemount-resources.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir)/../data/ --generate-dependencies $(srcdir)/../data/drivemount-resources.gresource.xml)
+ $(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir)/../data/ --generate --c-name drivemount $<
+
+drivemount-resources.h: ../data/drivemount-resources.gresource.xml $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir)/../data/ --generate-dependencies $(srcdir)/../data/drivemount-resources.gresource.xml)
+ $(AM_V_GEN)$(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir)/../data/ --generate --c-name drivemount $<
+
+CLEANFILES = \
+ $(BUILT_SOURCES) \
+ $(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/drivemount/src/drive-button.c b/drivemount/src/drive-button.c
new file mode 100644
index 00000000..f64f38c4
--- /dev/null
+++ b/drivemount/src/drive-button.c
@@ -0,0 +1,982 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * Drive Mount Applet
+ * Copyright (c) 2004 Canonical Ltd
+ * Copyright 2008 Pierre Ossman
+ *
+ * This library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * James Henstridge <[email protected]>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include "drive-button.h"
+#include <glib/gi18n.h>
+#include <gdk/gdkkeysyms.h>
+#include <gio/gdesktopappinfo.h>
+
+#include <string.h>
+
+enum {
+ CMD_NONE,
+ CMD_MOUNT_OR_PLAY,
+ CMD_UNMOUNT,
+ CMD_EJECT
+};
+
+/* type registration boilerplate code */
+G_DEFINE_TYPE (DriveButton, drive_button, GTK_TYPE_BUTTON)
+
+static void drive_button_set_volume (DriveButton *self,
+ GVolume *volume);
+static void drive_button_set_mount (DriveButton *self,
+ GMount *mount);
+static void drive_button_reset_popup (DriveButton *self);
+static void drive_button_ensure_popup (DriveButton *self);
+
+static void drive_button_dispose (GObject *object);
+#if 0
+static void drive_button_unrealize (GtkWidget *widget);
+#endif /* 0 */
+static gboolean drive_button_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean drive_button_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+static void drive_button_theme_change (GtkIconTheme *icon_theme,
+ gpointer data);
+
+static void
+drive_button_class_init (DriveButtonClass *class)
+{
+ G_OBJECT_CLASS (class)->dispose = drive_button_dispose;
+ GTK_WIDGET_CLASS (class)->button_press_event = drive_button_button_press;
+ GTK_WIDGET_CLASS (class)->key_press_event = drive_button_key_press;
+
+ GtkCssProvider *provider;
+
+ provider = gtk_css_provider_new ();
+
+ gtk_css_provider_load_from_data (provider,
+ "#drive-button {\n"
+ " border-width: 0px;\n"
+ " padding: 0px;\n"
+ " margin: 0px;\n"
+ "}",
+ -1, NULL);
+
+ gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
+ GTK_STYLE_PROVIDER (provider),
+ GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+ g_object_unref (provider);
+}
+
+static void
+drive_button_init (DriveButton *self)
+{
+ GtkWidget *image;
+
+ image = gtk_image_new ();
+ gtk_container_add (GTK_CONTAINER (self), image);
+ gtk_widget_show (image);
+
+ self->volume = NULL;
+ self->mount = NULL;
+ self->icon_size = 24;
+ self->update_tag = 0;
+
+ self->popup_menu = NULL;
+
+ gtk_widget_set_name (GTK_WIDGET (self), "drive-button");
+}
+
+GtkWidget *
+drive_button_new (GVolume *volume)
+{
+ DriveButton *self;
+
+ self = g_object_new (DRIVE_TYPE_BUTTON, NULL);
+ if (volume != NULL) {
+ drive_button_set_volume (self, volume);
+ g_signal_connect (gtk_icon_theme_get_default (),
+ "changed", G_CALLBACK (drive_button_theme_change),
+ self);
+ }
+
+ return (GtkWidget *)self;
+}
+
+GtkWidget *
+drive_button_new_from_mount (GMount *mount)
+{
+ DriveButton *self;
+
+ self = g_object_new (DRIVE_TYPE_BUTTON, NULL);
+ drive_button_set_mount (self, mount);
+
+ g_signal_connect (gtk_icon_theme_get_default (),
+ "changed", G_CALLBACK (drive_button_theme_change),
+ self);
+
+ return (GtkWidget *)self;
+}
+
+static void
+drive_button_dispose (GObject *object)
+{
+ DriveButton *self = DRIVE_BUTTON (object);
+
+ drive_button_set_volume (self, NULL);
+
+ if (self->update_tag)
+ g_source_remove (self->update_tag);
+ self->update_tag = 0;
+
+ drive_button_reset_popup (self);
+
+ if (G_OBJECT_CLASS (drive_button_parent_class)->dispose)
+ (* G_OBJECT_CLASS (drive_button_parent_class)->dispose) (object);
+}
+
+static gboolean
+drive_button_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ DriveButton *self = DRIVE_BUTTON (widget);
+
+ /* don't consume non-button1 presses */
+ if (event->button == 1) {
+ drive_button_ensure_popup (self);
+ if (self->popup_menu) {
+ gtk_menu_popup_at_widget (GTK_MENU (self->popup_menu),
+ widget,
+ GDK_GRAVITY_SOUTH_WEST,
+ GDK_GRAVITY_NORTH_WEST,
+ (const GdkEvent*) event);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+drive_button_key_press (GtkWidget *widget,
+ GdkEventKey *event)
+{
+ DriveButton *self = DRIVE_BUTTON (widget);
+
+ switch (event->keyval) {
+ case GDK_KEY_KP_Space:
+ case GDK_KEY_space:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_Return:
+ drive_button_ensure_popup (self);
+ if (self->popup_menu) {
+ gtk_menu_popup_at_widget (GTK_MENU (self->popup_menu),
+ widget,
+ GDK_GRAVITY_SOUTH_WEST,
+ GDK_GRAVITY_NORTH_WEST,
+ (const GdkEvent*) event);
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+drive_button_theme_change (GtkIconTheme *icon_theme,
+ gpointer data)
+{
+ drive_button_queue_update (data);
+}
+
+static void
+drive_button_set_volume (DriveButton *self,
+ GVolume *volume)
+{
+ g_return_if_fail (DRIVE_IS_BUTTON (self));
+
+ if (self->volume) {
+ g_object_unref (self->volume);
+ }
+ self->volume = NULL;
+ if (self->mount) {
+ g_object_unref (self->mount);
+ }
+ self->mount = NULL;
+
+ if (volume) {
+ self->volume = g_object_ref (volume);
+ }
+ drive_button_queue_update (self);
+}
+
+static void
+drive_button_set_mount (DriveButton *self,
+ GMount *mount)
+{
+ g_return_if_fail (DRIVE_IS_BUTTON (self));
+
+ if (self->volume) {
+ g_object_unref (self->volume);
+ }
+ self->volume = NULL;
+ if (self->mount) {
+ g_object_unref (self->mount);
+ }
+ self->mount = NULL;
+
+ if (mount) {
+ self->mount = g_object_ref (mount);
+ }
+ drive_button_queue_update (self);
+}
+
+static gboolean
+drive_button_update (gpointer user_data)
+{
+ DriveButton *self;
+ GdkScreen *screen;
+ GtkIconTheme *icon_theme;
+ GtkIconInfo *icon_info;
+ GIcon *icon;
+ int width, height, scale;
+ cairo_t *cr;
+ cairo_surface_t *surface = NULL;
+ cairo_surface_t *tmp_surface = NULL;
+ GtkRequisition button_req, image_req;
+ char *display_name, *tip;
+
+ g_return_val_if_fail (DRIVE_IS_BUTTON (user_data), FALSE);
+ self = DRIVE_BUTTON (user_data);
+ self->update_tag = 0;
+
+ /* base the icon size on the desired button size */
+ drive_button_reset_popup (self);
+ scale = gtk_widget_get_scale_factor (GTK_WIDGET (self));
+ gtk_widget_get_preferred_size (GTK_WIDGET (self), NULL, &button_req);
+ gtk_widget_get_preferred_size (gtk_bin_get_child (GTK_BIN (self)), NULL, &image_req);
+ width = (self->icon_size - (button_req.width - image_req.width)) / scale;
+ height = (self->icon_size - (button_req.height - image_req.height)) / scale;
+
+ /* if no volume or mount, display general image */
+ if (!self->volume && !self->mount)
+ {
+ gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("nothing to mount"));
+ screen = gtk_widget_get_screen (GTK_WIDGET (self));
+ icon_theme = gtk_icon_theme_get_for_screen (screen); //m
+ // note - other good icon would be emblem-unreadable
+ icon_info = gtk_icon_theme_lookup_icon_for_scale (icon_theme, "media-floppy",
+ MIN (width, height), scale,
+ GTK_ICON_LOOKUP_USE_BUILTIN);
+ if (icon_info) {
+ surface = gtk_icon_info_load_surface (icon_info, NULL, NULL);
+ g_object_unref (icon_info);
+ }
+
+ if (!surface)
+ return FALSE;
+
+ if (gtk_bin_get_child (GTK_BIN (self)) != NULL)
+ gtk_image_set_from_surface (GTK_IMAGE (gtk_bin_get_child (GTK_BIN (self))), surface);
+
+ return FALSE;
+ }
+
+ gboolean is_mounted = FALSE;
+
+ if (self->volume)
+ {
+ GMount *mount;
+
+ display_name = g_volume_get_name (self->volume);
+ mount = g_volume_get_mount (self->volume);
+
+ if (mount)
+ {
+ is_mounted = TRUE;
+ tip = g_strdup_printf ("%s\n%s", display_name, _("(mounted)"));
+ icon = g_mount_get_icon (mount);
+ g_object_unref (mount);
+ }
+ else
+ {
+ is_mounted = FALSE;
+ tip = g_strdup_printf ("%s\n%s", display_name, _("(not mounted)"));
+ icon = g_volume_get_icon (self->volume);
+ }
+ } else
+ {
+ is_mounted = TRUE;
+ display_name = g_mount_get_name (self->mount);
+ tip = g_strdup_printf ("%s\n%s", display_name, _("(mounted)"));
+ icon = g_mount_get_icon (self->mount);
+ }
+
+ gtk_widget_set_tooltip_text (GTK_WIDGET (self), tip);
+ g_free (tip);
+ g_free (display_name);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (self));
+ icon_theme = gtk_icon_theme_get_for_screen (screen);
+ icon_info = gtk_icon_theme_lookup_by_gicon_for_scale (icon_theme, icon,
+ MIN (width, height), scale,
+ GTK_ICON_LOOKUP_USE_BUILTIN);
+ if (icon_info)
+ {
+ surface = gtk_icon_info_load_surface (icon_info, NULL, NULL);
+ g_object_unref (icon_info);
+ }
+
+ g_object_unref (icon);
+
+ if (!surface)
+ return FALSE;
+
+ // create a new surface because icon image can be shared by system
+ tmp_surface = cairo_surface_create_similar (surface,
+ cairo_surface_get_content (surface),
+ cairo_image_surface_get_width (surface) / scale,
+ cairo_image_surface_get_height (surface) / scale);
+
+ // if mounted, change icon
+ if (is_mounted)
+ {
+ int icon_width, icon_height, rowstride, n_channels, x, y;
+ guchar *pixels, *p;
+ gboolean has_alpha;
+
+ has_alpha = cairo_surface_get_content (tmp_surface) != CAIRO_CONTENT_COLOR;
+ n_channels = 3;
+ if (has_alpha)
+ n_channels++;
+
+ icon_width = cairo_image_surface_get_width (tmp_surface);
+ icon_height = cairo_image_surface_get_height (tmp_surface);
+
+ rowstride = cairo_image_surface_get_stride (tmp_surface);
+ pixels = cairo_image_surface_get_data (tmp_surface);
+
+ GdkRGBA color;
+ GSettings *settings;
+ settings = g_settings_new ("org.mate.drivemount");
+ gchar *color_string = g_settings_get_string (settings, "drivemount-checkmark-color");
+ if (!color_string)
+ color_string = g_strdup ("#00ff00");
+ gdk_rgba_parse (&color, color_string);
+ g_free (color_string);
+ g_object_unref (settings);
+
+ guchar red = color.red*255;
+ guchar green = color.green*255;
+ guchar blue = color.blue*255;
+
+ const gdouble ratio = 0.65;
+ gdouble y_start = icon_height * ratio;
+ gdouble x_start = icon_height * (1 + ratio);
+
+ for (y = y_start; y < icon_height; y++)
+ for (x = x_start - y; x < icon_width; x++)
+ {
+ p = pixels + y * rowstride + x * n_channels;
+ p[0] = red;
+ p[1] = green;
+ p[2] = blue;
+ if (has_alpha)
+ p[3] = 255;
+ }
+ }
+
+ cr = cairo_create (tmp_surface);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVERLAY);
+ cairo_set_source_surface (cr, surface, 0, 0);
+ cairo_paint (cr);
+
+ gtk_image_set_from_surface (GTK_IMAGE (gtk_bin_get_child (GTK_BIN (self))), tmp_surface);
+
+ cairo_surface_destroy (surface);
+ cairo_surface_destroy (tmp_surface);
+
+ gtk_widget_get_preferred_size (GTK_WIDGET (self), NULL, &button_req);
+
+ return FALSE;
+}
+
+void
+drive_button_queue_update (DriveButton *self)
+{
+ if (!self->update_tag) {
+ self->update_tag = g_idle_add (drive_button_update, self);
+ }
+}
+
+void
+drive_button_set_size (DriveButton *self,
+ int icon_size)
+{
+ g_return_if_fail (DRIVE_IS_BUTTON (self));
+
+ if (self->icon_size != icon_size) {
+ self->icon_size = icon_size;
+ drive_button_queue_update (self);
+ }
+}
+
+void
+drive_button_redraw (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ DriveButton *button = value;
+ drive_button_queue_update (button);
+}
+
+int
+drive_button_compare (DriveButton *button,
+ DriveButton *other_button)
+{
+ /* sort drives before driveless volumes volumes */
+ if (button->volume) {
+ if (other_button->volume) {
+ int cmp;
+ gchar *str1, *str2;
+
+ str1 = g_volume_get_name (button->volume);
+ str2 = g_volume_get_name (other_button->volume);
+ cmp = g_utf8_collate (str1, str2);
+ g_free (str2);
+ g_free (str1);
+
+ return cmp;
+ } else {
+ return -1;
+ }
+ } else {
+ if (other_button->volume) {
+ return 1;
+ } else {
+ int cmp;
+ gchar *str1, *str2;
+
+ str1 = g_mount_get_name (button->mount);
+ str2 = g_mount_get_name (other_button->mount);
+ cmp = g_utf8_collate (str1, str2);
+ g_free (str2);
+ g_free (str1);
+
+ return cmp;
+ }
+ }
+}
+
+static void
+drive_button_reset_popup (DriveButton *self)
+{
+ if (self->popup_menu)
+ gtk_widget_destroy (GTK_WIDGET (self->popup_menu));
+ self->popup_menu = NULL;
+}
+
+#if 0
+static void
+popup_menu_detach (GtkWidget *attach_widget, GtkMenu *menu)
+{
+ DRIVE_BUTTON (attach_widget)->popup_menu = NULL;
+}
+#endif /* 0 */
+
+static char *
+escape_underscores (const char *str)
+{
+ char *new_str;
+ int i, j, count;
+
+ /* count up how many underscores are in the string */
+ count = 0;
+ for (i = 0; str[i] != '\0'; i++) {
+ if (str[i] == '_')
+ count++;
+ }
+ /* copy to new string, doubling up underscores */
+ new_str = g_new (char, i + count + 1);
+ for (i = j = 0; str[i] != '\0'; i++, j++) {
+ new_str[j] = str[i];
+ if (str[i] == '_')
+ new_str[++j] = '_';
+ }
+ new_str[j] = '\0';
+ return new_str;
+}
+static GtkWidget *
+create_menu_item (DriveButton *self,
+ const gchar *icon_name,
+ const gchar *label,
+ GCallback callback,
+ gboolean sensitive)
+{
+ GtkWidget *item, *image;
+
+ item = gtk_image_menu_item_new_with_mnemonic (label);
+ if (icon_name) {
+ image = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
+ gtk_widget_show (image);
+ }
+ if (callback)
+ g_signal_connect_object (item, "activate", callback, self,
+ G_CONNECT_SWAPPED);
+ gtk_widget_set_sensitive (item, sensitive);
+ gtk_widget_show (item);
+ return item;
+}
+
+static void
+open_drive (DriveButton *self,
+ GtkWidget *item)
+{
+ GdkScreen *screen;
+ GtkWidget *dialog;
+ GError *error = NULL;
+ GFile *file = NULL;
+ GList *files = NULL;
+ GdkAppLaunchContext *launch_context;
+ GAppInfo *app_info;
+
+ if (self->volume) {
+ GMount *mount;
+
+ mount = g_volume_get_mount (self->volume);
+ if (mount) {
+ file = g_mount_get_root (mount);
+ g_object_unref (mount);
+ }
+ } else if (self->mount) {
+ file = g_mount_get_root (self->mount);
+ } else
+ g_return_if_reached ();
+
+ app_info = g_app_info_get_default_for_type ("inode/directory", FALSE);
+ if (!app_info)
+ app_info = G_APP_INFO (g_desktop_app_info_new ("caja.desktop"));
+
+ if (app_info) {
+ GdkDisplay *display = gtk_widget_get_display (item);
+ launch_context = gdk_display_get_app_launch_context (display);
+ screen = gtk_widget_get_screen (GTK_WIDGET (self));
+ gdk_app_launch_context_set_screen (launch_context, screen);
+ files = g_list_prepend (files, file);
+ g_app_info_launch (app_info,
+ files,
+ G_APP_LAUNCH_CONTEXT (launch_context),
+ &error);
+
+ g_object_unref (launch_context);
+ g_list_free (files);
+ }
+
+ if (!app_info || error) {
+ dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (self))),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Cannot execute Caja"));
+ if (error)
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+ else
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "Could not find Caja");
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_widget_show (dialog);
+ g_error_free (error);
+ }
+
+ g_object_unref (file);
+}
+
+/* copied from mate-volume-manager/src/manager.c maybe there is a better way than
+ * duplicating this code? */
+
+/*
+ * gvm_run_command - run the given command, replacing %d with the device node
+ * and %m with the given path
+ */
+static void
+gvm_run_command (const char *device,
+ const char *command,
+ const char *path)
+{
+ char *argv[4];
+ gchar *new_command;
+ GError *error = NULL;
+ GString *exec = g_string_new (NULL);
+ char *p, *q;
+
+ /* perform s/%d/device/ and s/%m/path/ */
+ new_command = g_strdup (command);
+ q = new_command;
+ p = new_command;
+ while ((p = strchr (p, '%')) != NULL) {
+ if (*(p + 1) == 'd') {
+ *p = '\0';
+ g_string_append (exec, q);
+ g_string_append (exec, device);
+ q = p + 2;
+ p = p + 2;
+ } else if (*(p + 1) == 'm') {
+ *p = '\0';
+ g_string_append (exec, q);
+ g_string_append (exec, path);
+ q = p + 2;
+ p = p + 2;
+ } else {
+ /* Ignore anything else. */
+ p++;
+ }
+ }
+ g_string_append (exec, q);
+
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = exec->str;
+ argv[3] = NULL;
+
+ g_spawn_async (g_get_home_dir (), argv, NULL, 0, NULL, NULL,
+ NULL, &error);
+ if (error) {
+ g_warning ("failed to exec %s: %s\n", exec->str, error->message);
+ g_error_free (error);
+ }
+
+ g_string_free (exec, TRUE);
+ g_free (new_command);
+}
+
+/*
+ * gvm_check_dvd_only - is this a Video DVD?
+ *
+ * Returns TRUE if this was a Video DVD and FALSE otherwise.
+ * (the original in gvm was also running the autoplay action,
+ * I removed that code, so I renamed from gvm_check_dvd to
+ * gvm_check_dvd_only)
+ */
+static gboolean
+gvm_check_dvd_only (const char *udi,
+ const char *device,
+ const char *mount_point)
+{
+ char *path;
+ gboolean retval;
+
+ path = g_build_path (G_DIR_SEPARATOR_S, mount_point, "video_ts", NULL);
+ retval = g_file_test (path, G_FILE_TEST_IS_DIR);
+ g_free (path);
+
+ /* try the other name, if needed */
+ if (retval == FALSE) {
+ path = g_build_path (G_DIR_SEPARATOR_S, mount_point,
+ "VIDEO_TS", NULL);
+ retval = g_file_test (path, G_FILE_TEST_IS_DIR);
+ g_free (path);
+ }
+
+ return retval;
+}
+/* END copied from mate-volume-manager/src/manager.c */
+
+static gboolean
+check_dvd_video (DriveButton *self)
+{
+ GFile *file;
+ char *udi, *device_path, *mount_path;
+ gboolean result;
+ GMount *mount;
+
+ if (!self->volume)
+ return FALSE;
+
+ mount = g_volume_get_mount (self->volume);
+ if (!mount)
+ return FALSE;
+
+ file = g_mount_get_root (mount);
+ g_object_unref (mount);
+
+ if (!file)
+ return FALSE;
+
+ mount_path = g_file_get_path (file);
+
+ g_object_unref (file);
+
+ device_path = g_volume_get_identifier (self->volume,
+ G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+ udi = g_volume_get_identifier (self->volume,
+ G_VOLUME_IDENTIFIER_KIND_HAL_UDI);
+
+ result = gvm_check_dvd_only (udi, device_path, mount_path);
+
+ g_free (device_path);
+ g_free (udi);
+ g_free (mount_path);
+
+ return result;
+}
+
+static gboolean
+check_audio_cd (DriveButton *self)
+{
+ GFile *file;
+ char *activation_uri;
+ GMount *mount;
+
+ if (!self->volume)
+ return FALSE;
+
+ mount = g_volume_get_mount (self->volume);
+ if (!mount)
+ return FALSE;
+
+ file = g_mount_get_root (mount);
+ g_object_unref (mount);
+
+ if (!file)
+ return FALSE;
+
+ activation_uri = g_file_get_uri (file);
+
+ g_object_unref (file);
+
+ /* we have an audioCD if the activation URI starts by 'cdda://' */
+ gboolean result = (strncmp ("cdda://", activation_uri, 7) == 0);
+ g_free (activation_uri);
+ return result;
+}
+
+static void
+run_command (DriveButton *self,
+ const char *command)
+{
+ GFile *file;
+ char *mount_path, *device_path;
+ GMount *mount;
+
+ if (!self->volume)
+ return;
+
+ mount = g_volume_get_mount (self->volume);
+ if (!mount)
+ return;
+
+ file = g_mount_get_root (mount);
+ g_object_unref (mount);
+
+ g_assert (file);
+
+ mount_path = g_file_get_path (file);
+
+ g_object_unref (file);
+
+ device_path = g_volume_get_identifier (self->volume,
+ G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+
+ gvm_run_command (device_path, command, mount_path);
+
+ g_free (mount_path);
+ g_free (device_path);
+}
+
+static void dummy_async_ready_callback (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ /* do nothing */
+}
+
+static void
+mount_drive (DriveButton *self,
+ GtkWidget *item)
+{
+ if (self->volume) {
+ GMountOperation *mount_op = gtk_mount_operation_new (NULL);
+ g_volume_mount (self->volume, G_MOUNT_MOUNT_NONE,
+ mount_op, NULL, dummy_async_ready_callback, NULL);
+ g_object_unref (mount_op);
+ } else {
+ g_return_if_reached ();
+ }
+}
+
+static void
+unmount_drive (DriveButton *self,
+ GtkWidget *item)
+{
+ if (self->volume) {
+ GMount *mount;
+
+ mount = g_volume_get_mount (self->volume);
+ if (mount) {
+ g_mount_unmount_with_operation (mount, G_MOUNT_UNMOUNT_NONE,
+ NULL, NULL, dummy_async_ready_callback, NULL);
+ g_object_unref (mount);
+ }
+ } else if (self->mount) {
+ g_mount_unmount_with_operation (self->mount, G_MOUNT_UNMOUNT_NONE,
+ NULL, NULL, dummy_async_ready_callback, NULL);
+ } else {
+ g_return_if_reached ();
+ }
+}
+
+static void eject_finish (DriveButton *self,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ /* Do nothing. We shouldn't need this according to the GIO
+ * docs, but the applet crashes without it using glib 2.18.0 */
+}
+
+static void
+eject_drive (DriveButton *self,
+ GtkWidget *item)
+{
+ if (self->volume) {
+ g_volume_eject_with_operation (self->volume, G_MOUNT_UNMOUNT_NONE,
+ NULL, NULL,
+ (GAsyncReadyCallback) eject_finish,
+ NULL);
+ } else if (self->mount) {
+ g_mount_eject_with_operation (self->mount, G_MOUNT_UNMOUNT_NONE,
+ NULL, NULL,
+ (GAsyncReadyCallback) eject_finish,
+ NULL);
+ } else {
+ g_return_if_reached ();
+ }
+}
+static void
+play_autoplay_media (DriveButton *self,
+ const char *dflt)
+{
+ run_command (self, dflt);
+}
+
+static void
+play_dvd (DriveButton *self,
+ GtkWidget *item)
+{
+ /* FIXME add an option to set this */
+ play_autoplay_media (self, "totem %d");
+}
+
+static void
+play_cda (DriveButton *self,
+ GtkWidget *item)
+{
+ /* FIXME add an option to set this */
+ play_autoplay_media (self, "sound-juicer -d %d");
+}
+
+static void
+drive_button_ensure_popup (DriveButton *self)
+{
+ char *display_name, *tmp, *label;
+ GtkWidget *item;
+ gboolean mounted, ejectable;
+
+ if (self->popup_menu) return;
+
+ mounted = FALSE;
+
+ if (self->volume) {
+ GMount *mount = NULL;
+
+ display_name = g_volume_get_name (self->volume);
+ ejectable = g_volume_can_eject (self->volume);
+
+ mount = g_volume_get_mount (self->volume);
+ if (mount) {
+ mounted = TRUE;
+ g_object_unref (mount);
+ }
+ } else {
+ if (!G_IS_MOUNT (self->volume))
+ return;
+ else {
+ display_name = g_mount_get_name (self->mount);
+ ejectable = g_mount_can_eject (self->mount);
+ mounted = TRUE;
+ }
+ }
+
+ self->popup_menu = gtk_menu_new ();
+
+ /* make sure the display name doesn't look like a mnemonic */
+ tmp = escape_underscores (display_name ? display_name : "(none)");
+ g_free (display_name);
+ display_name = tmp;
+
+ if (check_dvd_video (self)) {
+ item = create_menu_item (self, "media-playback-start",
+ _("_Play DVD"), G_CALLBACK (play_dvd),
+ TRUE);
+ } else if (check_audio_cd (self)) {
+ item = create_menu_item (self, "media-playback-start",
+ _("_Play CD"), G_CALLBACK (play_cda),
+ TRUE);
+ } else {
+ label = g_strdup_printf (_("_Open %s"), display_name);
+ item = create_menu_item (self, "document-open", label,
+ G_CALLBACK (open_drive), mounted);
+ g_free (label);
+ }
+ gtk_container_add (GTK_CONTAINER (self->popup_menu), item);
+
+ if (mounted) {
+ label = g_strdup_printf (_("Un_mount %s"), display_name);
+ item = create_menu_item (self, NULL, label,
+ G_CALLBACK (unmount_drive), TRUE);
+ g_free (label);
+ gtk_container_add (GTK_CONTAINER (self->popup_menu), item);
+ } else {
+ label = g_strdup_printf (_("_Mount %s"), display_name);
+ item = create_menu_item (self, NULL, label,
+ G_CALLBACK (mount_drive), TRUE);
+ g_free (label);
+ gtk_container_add (GTK_CONTAINER (self->popup_menu), item);
+ }
+
+ if (ejectable) {
+ label = g_strdup_printf (_("_Eject %s"), display_name);
+ item = create_menu_item (self, "media-eject", label,
+ G_CALLBACK (eject_drive), TRUE);
+ g_free (label);
+ gtk_container_add (GTK_CONTAINER (self->popup_menu), item);
+ }
+
+ /*Set up custom theme and transparency support */
+ GtkWidget *toplevel = gtk_widget_get_toplevel (self->popup_menu);
+ /* Fix any failures of compiz/other wm's to communicate with gtk for transparency */
+ GdkScreen *screen2 = gtk_widget_get_screen (GTK_WIDGET (toplevel));
+ GdkVisual *visual = gdk_screen_get_rgba_visual (screen2);
+ gtk_widget_set_visual (GTK_WIDGET (toplevel), visual);
+ /*set menu and it's toplevel window to follow panel theme */
+ GtkStyleContext *context;
+ context = gtk_widget_get_style_context (GTK_WIDGET (toplevel));
+ gtk_style_context_add_class (context,"gnome-panel-menu-bar");
+ gtk_style_context_add_class (context,"mate-panel-menu-bar");
+}
diff --git a/drivemount/src/drive-button.h b/drivemount/src/drive-button.h
new file mode 100644
index 00000000..fdab2f16
--- /dev/null
+++ b/drivemount/src/drive-button.h
@@ -0,0 +1,71 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * Drive Mount Applet
+ * Copyright (c) 2004 Canonical Ltd
+ *
+ * This library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * James Henstridge <[email protected]>
+ */
+
+#ifndef DRIVE_BUTTON_H
+#define DRIVE_BUTTON_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DRIVE_TYPE_BUTTON (drive_button_get_type ())
+#define DRIVE_BUTTON(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DRIVE_TYPE_BUTTON, DriveButton))
+#define DRIVE_BUTTON_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), DRIVE_TYPE_BUTTON, DriveButtonClass))
+#define DRIVE_IS_BUTTON(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DRIVE_TYPE_BUTTON))
+#define DRIVE_IS_BUTTON_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DRIVE_TYPE_BUTTON))
+#define DRIVE_BUTTON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DRIVE_TYPE_BUTTON, DriveButtonClass))
+
+typedef struct _DriveButton DriveButton;
+typedef struct _DriveButtonClass DriveButtonClass;
+
+struct _DriveButton
+{
+ GtkButton parent;
+
+ GVolume *volume;
+ GMount *mount;
+ int icon_size;
+ guint update_tag;
+
+ GtkWidget *popup_menu;
+};
+
+struct _DriveButtonClass
+{
+ GtkButtonClass parent;
+};
+
+GType drive_button_get_type (void);
+GtkWidget *drive_button_new (GVolume *volume);
+GtkWidget *drive_button_new_from_mount (GMount *mount);
+void drive_button_queue_update (DriveButton *button);
+void drive_button_set_size (DriveButton *button,
+ int icon_size);
+
+int drive_button_compare (DriveButton *button,
+ DriveButton *other_button);
+
+void drive_button_redraw (gpointer key, gpointer value, gpointer user_data);
+
+G_END_DECLS
+
+#endif /* DRIVE_BUTTON_H */
diff --git a/drivemount/src/drive-list.c b/drivemount/src/drive-list.c
new file mode 100644
index 00000000..b1840051
--- /dev/null
+++ b/drivemount/src/drive-list.c
@@ -0,0 +1,522 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * Drive Mount Applet
+ * Copyright (c) 2004 Canonical Ltd
+ * Copyright 2008 Pierre Ossman
+ *
+ * This library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * James Henstridge <[email protected]>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gio/gio.h>
+#include "drive-list.h"
+#include "drive-button.h"
+#include <glib/gi18n.h>
+
+G_DEFINE_TYPE (DriveList, drive_list, GTK_TYPE_GRID);
+
+static GVolumeMonitor *volume_monitor = NULL;
+
+static void drive_list_finalize (GObject *object);
+static void drive_list_dispose (GObject *object);
+static void drive_list_add (GtkContainer *container, GtkWidget *child);
+static void drive_list_remove (GtkContainer *container, GtkWidget *child);
+
+static void mount_added (GVolumeMonitor *monitor, GMount *mount, DriveList *self);
+static void mount_changed (GVolumeMonitor *monitor, GMount *mount, DriveList *self);
+static void mount_removed (GVolumeMonitor *monitor, GMount *mount, DriveList *self);
+static void volume_added (GVolumeMonitor *monitor, GVolume *volume, DriveList *self);
+static void volume_changed (GVolumeMonitor *monitor, GVolume *volume, DriveList *self);
+static void volume_removed (GVolumeMonitor *monitor, GVolume *volume, DriveList *self);
+static void add_volume (DriveList *self, GVolume *volume);
+static void remove_volume (DriveList *self, GVolume *volume);
+static void add_mount (DriveList *self, GMount *mount);
+static void remove_mount (DriveList *self, GMount *mount);
+static void queue_relayout (DriveList *self);
+
+static void
+drive_list_class_init (DriveListClass *class)
+{
+ G_OBJECT_CLASS (class)->finalize = drive_list_finalize;
+ G_OBJECT_CLASS (class)->dispose = drive_list_dispose;
+ GTK_CONTAINER_CLASS (class)->add = drive_list_add;
+ GTK_CONTAINER_CLASS (class)->remove = drive_list_remove;
+}
+
+static void
+drive_list_init (DriveList *self)
+{
+ GList *volumes, *mounts, *tmp;
+
+ gtk_grid_set_column_homogeneous (GTK_GRID (self), TRUE);
+ gtk_grid_set_row_homogeneous (GTK_GRID (self), TRUE);
+
+ self->volumes = g_hash_table_new (NULL, NULL);
+ self->mounts = g_hash_table_new (NULL, NULL);
+ self->orientation = GTK_ORIENTATION_HORIZONTAL;
+ self->layout_tag = 0;
+ self->settings = g_settings_new ("org.mate.drivemount");
+ self->icon_size = 24;
+ self->relief = GTK_RELIEF_NORMAL;
+
+ g_signal_connect (self->settings,
+ "changed::drivemount-checkmark-color",
+ G_CALLBACK (settings_color_changed),
+ self);
+
+ /* listen for drive connects/disconnects, and add
+ * currently connected drives. */
+ self->count = 0;
+ if (!volume_monitor)
+ volume_monitor = g_volume_monitor_get ();
+
+ g_signal_connect_object (volume_monitor, "mount_added",
+ G_CALLBACK (mount_added), self, 0);
+ g_signal_connect_object (volume_monitor, "mount_changed",
+ G_CALLBACK (mount_changed), self, 0);
+ g_signal_connect_object (volume_monitor, "mount_removed",
+ G_CALLBACK (mount_removed), self, 0);
+ g_signal_connect_object (volume_monitor, "volume_added",
+ G_CALLBACK (volume_added), self, 0);
+ g_signal_connect_object (volume_monitor, "volume_changed",
+ G_CALLBACK (volume_changed), self, 0);
+ g_signal_connect_object (volume_monitor, "volume_removed",
+ G_CALLBACK (volume_removed), self, 0);
+ volumes = g_volume_monitor_get_volumes (volume_monitor);
+ for (tmp = volumes; tmp != NULL; tmp = tmp->next) {
+ GVolume *volume = tmp->data;
+ add_volume (self, volume);
+ g_object_unref (volume);
+ self->count++;
+ }
+ g_list_free (volumes);
+
+ mounts = g_volume_monitor_get_mounts (volume_monitor);
+ for (tmp = mounts; tmp != NULL; tmp = tmp->next) {
+ GMount *mount = tmp->data;
+ add_mount (self, mount);
+ g_object_unref (mount);
+ self->count++;
+ }
+ self->dummy = drive_button_new (NULL);
+ gtk_button_set_relief (GTK_BUTTON (self->dummy), self->relief);
+ drive_button_set_size (DRIVE_BUTTON (self->dummy), self->icon_size);
+
+ if (self->count == 0) {
+ gtk_container_add (GTK_CONTAINER (self), self->dummy);
+ queue_relayout (self);
+ drive_button_queue_update (DRIVE_BUTTON (self->dummy));
+ }
+ g_list_free (mounts);
+}
+
+GtkWidget *
+drive_list_new (void)
+{
+ return g_object_new (DRIVE_TYPE_LIST, NULL);
+}
+
+static void
+drive_list_finalize (GObject *object)
+{
+ DriveList *self = DRIVE_LIST (object);
+
+ g_hash_table_destroy (self->volumes);
+ g_hash_table_destroy (self->mounts);
+ g_object_unref (self->settings);
+
+ if (G_OBJECT_CLASS (drive_list_parent_class)->finalize)
+ (* G_OBJECT_CLASS (drive_list_parent_class)->finalize) (object);
+}
+
+static void
+drive_list_dispose (GObject *object)
+{
+ DriveList *self = DRIVE_LIST (object);
+
+ g_signal_handlers_disconnect_by_func (volume_monitor,
+ G_CALLBACK (mount_added), self);
+ g_signal_handlers_disconnect_by_func (volume_monitor,
+ G_CALLBACK (mount_changed), self);
+ g_signal_handlers_disconnect_by_func (volume_monitor,
+ G_CALLBACK (mount_removed), self);
+ g_signal_handlers_disconnect_by_func (volume_monitor,
+ G_CALLBACK (volume_added), self);
+ g_signal_handlers_disconnect_by_func (volume_monitor,
+ G_CALLBACK (volume_changed), self);
+ g_signal_handlers_disconnect_by_func (volume_monitor,
+ G_CALLBACK (volume_removed), self);
+
+ if (self->layout_tag)
+ g_source_remove (self->layout_tag);
+ self->layout_tag = 0;
+
+ if (G_OBJECT_CLASS (drive_list_parent_class)->dispose)
+ (* G_OBJECT_CLASS (drive_list_parent_class)->dispose) (object);
+}
+
+static void
+drive_list_add (GtkContainer *container,
+ GtkWidget *child)
+{
+ DriveList *self;
+ DriveButton *button;
+
+ g_return_if_fail (DRIVE_IS_LIST (container));
+ g_return_if_fail (DRIVE_IS_BUTTON (child));
+
+ if (GTK_CONTAINER_CLASS (drive_list_parent_class)->add)
+ (* GTK_CONTAINER_CLASS (drive_list_parent_class)->add) (container, child);
+
+ self = DRIVE_LIST (container);
+ button = DRIVE_BUTTON (child);
+ if (button->volume)
+ g_hash_table_insert (self->volumes, button->volume, button);
+ else
+ g_hash_table_insert (self->mounts, button->mount, button);
+}
+
+static void
+drive_list_remove (GtkContainer *container,
+ GtkWidget *child)
+{
+ DriveList *self;
+ DriveButton *button;
+
+ g_return_if_fail (DRIVE_IS_LIST (container));
+ g_return_if_fail (DRIVE_IS_BUTTON (child));
+
+ self = DRIVE_LIST (container);
+ button = DRIVE_BUTTON (child);
+ if (button->volume)
+ g_hash_table_remove (self->volumes, button->volume);
+ else
+ g_hash_table_remove (self->mounts, button->mount);
+
+ if (GTK_CONTAINER_CLASS (drive_list_parent_class)->remove)
+ (* GTK_CONTAINER_CLASS (drive_list_parent_class)->remove) (container, child);
+}
+
+static void
+list_buttons (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GtkWidget *button = value;
+ GList **sorted_buttons = user_data;
+
+ *sorted_buttons = g_list_insert_sorted (*sorted_buttons, button,
+ (GCompareFunc)drive_button_compare);
+}
+
+static gboolean
+relayout_buttons (gpointer data)
+{
+ DriveList *self = DRIVE_LIST (data);
+ GList *sorted_buttons = NULL, *tmp;
+ int i = 0;
+
+ self->layout_tag = 0;
+ if ( self->count > 0 ) {
+ g_hash_table_foreach (self->volumes, list_buttons, &sorted_buttons);
+ g_hash_table_foreach (self->mounts, list_buttons, &sorted_buttons);
+
+ /* position buttons in the table according to their sorted order */
+ for (tmp = sorted_buttons, i = 0; tmp != NULL; tmp = tmp->next, i++) {
+ GtkWidget *button = tmp->data;
+
+ if (self->orientation == GTK_ORIENTATION_HORIZONTAL) {
+ gtk_container_child_set (GTK_CONTAINER (self), button,
+ "left-attach", i + 1, "top-attach", 0,
+ "width", 1, "height", 1,
+ NULL);
+ }
+ else {
+ gtk_container_child_set (GTK_CONTAINER (self), button,
+ "left-attach", 0, "top-attach", i + 1,
+ "width", 1, "height", 1,
+ NULL);
+ }
+ }
+ }
+ else {
+ gtk_widget_show (self->dummy);
+ if (self->orientation == GTK_ORIENTATION_HORIZONTAL) {
+ gtk_container_child_set (GTK_CONTAINER (self), self->dummy,
+ "left-attach", i + 1, "top-attach", 0,
+ "width", 1, "height", 1,
+ NULL);
+ }
+ else {
+ gtk_container_child_set (GTK_CONTAINER (self), self->dummy,
+ "left-attach", 0, "top-attach", i + 1,
+ "width", 1, "height", 1,
+ NULL);
+ }
+ }
+ return FALSE;
+}
+
+static void
+queue_relayout (DriveList *self)
+{
+ if (!self->layout_tag) {
+ self->layout_tag = g_idle_add (relayout_buttons, self);
+ }
+}
+
+static void
+mount_added (GVolumeMonitor *monitor,
+ GMount *mount,
+ DriveList *self)
+{
+ add_mount (self, mount);
+ self->count++;
+ if (self->count == 1)
+ gtk_container_remove (GTK_CONTAINER (self), g_object_ref (self->dummy));
+
+ mount_changed (monitor, mount, self);
+}
+
+static void
+mount_changed (GVolumeMonitor *monitor,
+ GMount *mount,
+ DriveList *self)
+{
+ GVolume *volume;
+ DriveButton *button = NULL;;
+
+ volume = g_mount_get_volume (mount);
+ if (volume) {
+ button = g_hash_table_lookup (self->volumes, volume);
+ g_object_unref (volume);
+ } else {
+ button = g_hash_table_lookup (self->mounts, mount);
+ }
+ if (button)
+ drive_button_queue_update (button);
+}
+
+static void
+mount_removed (GVolumeMonitor *monitor,
+ GMount *mount,
+ DriveList *self)
+{
+ remove_mount (self, mount);
+ mount_changed (monitor, mount, self);
+ self->count--;
+ if (self->count == 0) {
+ gtk_container_add (GTK_CONTAINER (self), self->dummy);
+ queue_relayout (self);
+ }
+}
+
+static void
+volume_added (GVolumeMonitor *monitor,
+ GVolume *volume,
+ DriveList *self)
+{
+ add_volume (self, volume);
+ self->count++;
+ if (self->count == 1)
+ gtk_container_remove (GTK_CONTAINER (self), g_object_ref (self->dummy));
+}
+
+static void
+volume_changed (GVolumeMonitor *monitor,
+ GVolume *volume,
+ DriveList *self)
+{
+ DriveButton *button = NULL;;
+
+ button = g_hash_table_lookup (self->volumes, volume);
+ if (button)
+ drive_button_queue_update (button);
+}
+
+static void
+volume_removed (GVolumeMonitor *monitor,
+ GVolume *volume,
+ DriveList *self)
+{
+ remove_volume (self, volume);
+ self->count--;
+ if (self->count == 0) {
+ gtk_container_add (GTK_CONTAINER (self), self->dummy);
+ queue_relayout (self);
+ }
+}
+
+static void
+add_volume (DriveList *self,
+ GVolume *volume)
+{
+ GtkWidget *button;
+
+ /* if the volume has already been added, return */
+ if (g_hash_table_lookup (self->volumes, volume) != NULL)
+ return;
+
+ button = drive_button_new (volume);
+ gtk_button_set_relief (GTK_BUTTON (button), self->relief);
+ drive_button_set_size (DRIVE_BUTTON (button), self->icon_size);
+ gtk_container_add (GTK_CONTAINER (self), button);
+ gtk_widget_show (button);
+ queue_relayout (self);
+}
+
+static void
+remove_volume (DriveList *self,
+ GVolume *volume)
+{
+ GtkWidget *button;
+
+ /* if the volume has already been added, return */
+ button = g_hash_table_lookup (self->volumes, volume);
+ if (button) {
+ gtk_container_remove (GTK_CONTAINER (self), button);
+ queue_relayout (self);
+ }
+}
+
+static void
+add_mount (DriveList *self,
+ GMount *mount)
+{
+ GtkWidget *button;
+ GVolume *volume;
+
+ /* ignore mounts reported as shadowed */
+ if (g_mount_is_shadowed (mount)) {
+ return;
+ }
+
+ /* ignore mounts attached to a volume */
+ volume = g_mount_get_volume (mount);
+ if (volume) {
+ g_object_unref (volume);
+ return;
+ }
+
+ /* if the mount has already been added, return */
+ if (g_hash_table_lookup (self->mounts, mount) != NULL)
+ return;
+
+ button = drive_button_new_from_mount (mount);
+ gtk_button_set_relief (GTK_BUTTON (button), self->relief);
+ drive_button_set_size (DRIVE_BUTTON (button), self->icon_size);
+ gtk_container_add (GTK_CONTAINER (self), button);
+ gtk_widget_show (button);
+ queue_relayout (self);
+}
+
+static void
+remove_mount (DriveList *self,
+ GMount *mount)
+{
+ GtkWidget *button;
+
+ /* if the mount has already been added, return */
+ button = g_hash_table_lookup (self->mounts, mount);
+ if (button) {
+ gtk_container_remove (GTK_CONTAINER (self), button);
+ queue_relayout (self);
+ }
+}
+
+void
+drive_list_set_orientation (DriveList *self,
+ GtkOrientation orientation)
+{
+ g_return_if_fail (DRIVE_IS_LIST (self));
+
+ if (orientation != self->orientation) {
+ self->orientation = orientation;
+ queue_relayout (self);
+ }
+}
+
+static void
+set_icon_size (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ DriveButton *button = value;
+ DriveList *self = user_data;
+
+ drive_button_set_size (button, self->icon_size);
+}
+
+
+void
+drive_list_set_panel_size (DriveList *self,
+ int panel_size)
+{
+ g_return_if_fail (DRIVE_IS_LIST (self));
+
+ if (self->icon_size != panel_size) {
+ self->icon_size = panel_size;
+ g_hash_table_foreach (self->volumes, set_icon_size, self);
+ g_hash_table_foreach (self->mounts, set_icon_size, self);
+ }
+}
+
+void
+drive_list_redraw (DriveList *self)
+{
+ g_hash_table_foreach (self->volumes, drive_button_redraw, self);
+ g_hash_table_foreach (self->mounts, drive_button_redraw, self);
+}
+
+void
+settings_color_changed (GSettings *settings,
+ gchar *key,
+ DriveList *drive_list)
+{
+ g_return_if_fail (DRIVE_IS_LIST (drive_list));
+ drive_list_redraw (drive_list);
+}
+
+static void
+set_button_relief (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GtkButton *button = value;
+ DriveList *self = user_data;
+
+ gtk_button_set_relief (button, self->relief);
+}
+
+void
+drive_list_set_transparent (DriveList *self,
+ gboolean transparent)
+{
+ GtkReliefStyle relief;
+
+ relief = transparent ? GTK_RELIEF_NONE : GTK_RELIEF_NORMAL;
+
+ if (relief == self->relief)
+ return;
+
+ self->relief = relief;
+ g_hash_table_foreach (self->volumes, set_button_relief, self);
+ g_hash_table_foreach (self->mounts, set_button_relief, self);
+}
diff --git a/drivemount/src/drive-list.h b/drivemount/src/drive-list.h
new file mode 100644
index 00000000..e2da7333
--- /dev/null
+++ b/drivemount/src/drive-list.h
@@ -0,0 +1,73 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * Drive Mount Applet
+ * Copyright (c) 2004 Canonical Ltd
+ *
+ * This library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * James Henstridge <[email protected]>
+ */
+
+#ifndef DRIVE_LIST_H
+#define DRIVE_LIST_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define DRIVE_TYPE_LIST (drive_list_get_type ())
+#define DRIVE_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), DRIVE_TYPE_LIST, DriveList))
+#define DRIVE_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), DRIVE_TYPE_LIST, DriveListClass))
+#define DRIVE_IS_LIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), DRIVE_TYPE_LIST))
+#define DRIVE_IS_LIST_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), DRIVE_TYPE_LIST))
+#define DRIVE_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DRIVE_TYPE_LIST, DriveListClass))
+
+typedef struct _DriveList DriveList;
+typedef struct _DriveListClass DriveListClass;
+
+struct _DriveList
+{
+ GtkGrid parent;
+
+ GHashTable *volumes;
+ GHashTable *mounts;
+ GtkOrientation orientation;
+ guint layout_tag;
+ GtkReliefStyle relief;
+ GtkWidget *dummy;
+ gint count;
+
+ GSettings *settings;
+
+ int icon_size;
+};
+
+struct _DriveListClass
+{
+ GtkGridClass parent_class;
+};
+
+GType drive_list_get_type (void);
+GtkWidget *drive_list_new (void);
+void drive_list_set_orientation (DriveList *list,
+ GtkOrientation orientation);
+void drive_list_set_panel_size (DriveList *list,
+ int panel_size);
+void drive_list_set_transparent (DriveList *self,
+ gboolean transparent);
+void drive_list_redraw (DriveList *self);
+void settings_color_changed (GSettings *settings, gchar *key, DriveList *drive_list);
+
+#endif /* DRIVE_LIST_H */
diff --git a/drivemount/src/drivemount.c b/drivemount/src/drivemount.c
new file mode 100644
index 00000000..d6a77f20
--- /dev/null
+++ b/drivemount/src/drivemount.c
@@ -0,0 +1,230 @@
+/* -*- mode: C; c-basic-offset: 4 -*-
+ * Drive Mount Applet
+ * Copyright (c) 2004 Canonical Ltd
+ *
+ * This library 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Author:
+ * James Henstridge <[email protected]>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <mate-panel-applet.h>
+
+#include "drive-list.h"
+
+static const char drivemount_iid[] = "DriveMountApplet";
+static const char factory_iid[] = "DriveMountAppletFactory";
+
+static void
+change_orient (MatePanelApplet *applet,
+ MatePanelAppletOrient o,
+ DriveList *drive_list)
+{
+ GtkOrientation orientation;
+
+ switch (o) {
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ orientation = GTK_ORIENTATION_VERTICAL;
+ break;
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ default:
+ orientation = GTK_ORIENTATION_HORIZONTAL;
+ }
+ drive_list_set_orientation (drive_list, orientation);
+}
+
+static void
+size_allocate (MatePanelApplet *applet,
+ GdkRectangle *allocation,
+ DriveList *drive_list)
+{
+ int size;
+
+ switch (mate_panel_applet_get_orient (applet)) {
+ case MATE_PANEL_APPLET_ORIENT_LEFT:
+ case MATE_PANEL_APPLET_ORIENT_RIGHT:
+ size = allocation->width;
+ break;
+ case MATE_PANEL_APPLET_ORIENT_UP:
+ case MATE_PANEL_APPLET_ORIENT_DOWN:
+ default:
+ size = allocation->height;
+ }
+ drive_list_set_panel_size (drive_list, size);
+}
+
+static void
+change_background (MatePanelApplet *applet,
+ MatePanelAppletBackgroundType type,
+ GdkColor *colour,
+ cairo_pattern_t *pattern,
+ DriveList *drivelist)
+{
+ switch (type) {
+ case PANEL_NO_BACKGROUND:
+ drive_list_set_transparent (drivelist, FALSE);
+ break;
+ case PANEL_COLOR_BACKGROUND:
+ case PANEL_PIXMAP_BACKGROUND:
+ drive_list_set_transparent (drivelist, TRUE);
+ }
+}
+
+static void
+display_about_dialog (GtkAction *action,
+ DriveList *drive_list)
+{
+ const gchar *authors[] = {
+ "James Henstridge <[email protected]>",
+ NULL
+ };
+
+ const gchar *documenters[] = {
+ "Dan Mueth <[email protected]>",
+ "John Fleck <[email protected]>",
+ N_("MATE Documentation Team"),
+ NULL
+ };
+
+#ifdef ENABLE_NLS
+ const char **p;
+ for (p = documenters; *p; ++p)
+ *p = _(*p);
+#endif
+
+ gtk_show_about_dialog (NULL,
+ "title", _("About Disk Mounter"),
+ "version", VERSION,
+ "copyright", _("Copyright \xC2\xA9 2004 Canonical Ltd\n"
+ "Copyright \xc2\xa9 2012-2020 MATE developers"),
+ "comments", _("Applet for mounting and unmounting block volumes."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo_icon_name", "media-floppy",
+ NULL);
+}
+
+static void
+display_help (GtkAction *action,
+ DriveList *drive_list)
+{
+ GdkScreen *screen;
+ GError *error = NULL;
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (drive_list));
+
+ gtk_show_uri_on_window (NULL,
+ "help:mate-drivemount",
+ gtk_get_current_event_time (),
+ &error);
+
+ if (error) {
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("There was an error displaying help: %s"),
+ error->message);
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_window_set_screen (GTK_WINDOW (dialog), screen);
+ gtk_widget_show (dialog);
+ g_error_free (error);
+ }
+}
+
+static const GtkActionEntry applet_menu_actions[] = {
+ { "Help", "help-browser", N_("_Help"),
+ NULL, NULL,
+ G_CALLBACK (display_help) },
+ { "About", "help-about", N_("_About"),
+ NULL, NULL,
+ G_CALLBACK (display_about_dialog) }
+};
+
+static gboolean
+applet_factory (MatePanelApplet *applet,
+ const char *iid,
+ gpointer user_data)
+{
+ gboolean ret = FALSE;
+ GtkWidget *drive_list;
+ AtkObject *ao;
+ GtkActionGroup *action_group;
+
+ if (!strcmp (iid, drivemount_iid)) {
+ g_set_application_name (_("Disk Mounter"));
+
+ gtk_window_set_default_icon_name ("media-floppy");
+
+ mate_panel_applet_set_flags (applet, MATE_PANEL_APPLET_EXPAND_MINOR);
+
+ drive_list = drive_list_new ();
+
+ gtk_container_add (GTK_CONTAINER (applet), drive_list);
+
+ g_signal_connect_object (applet, "change_orient",
+ G_CALLBACK (change_orient), drive_list, 0);
+ g_signal_connect_object (applet, "size_allocate",
+ G_CALLBACK (size_allocate), drive_list, 0);
+ g_signal_connect (applet, "change_background",
+ G_CALLBACK (change_background), drive_list);
+
+ /* set initial state */
+ change_orient (applet,
+ mate_panel_applet_get_orient (applet),
+ DRIVE_LIST (drive_list));
+
+ action_group = gtk_action_group_new ("DriveMount Applet Actions");
+ gtk_action_group_set_translation_domain (action_group, GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (action_group,
+ applet_menu_actions,
+ G_N_ELEMENTS (applet_menu_actions),
+ drive_list);
+ mate_panel_applet_setup_menu_from_resource (applet,
+ DRIVEMOUNT_RESOURCE_PATH "drivemount-applet-menu.xml",
+ action_group);
+ g_object_unref (action_group);
+
+ ao = gtk_widget_get_accessible (GTK_WIDGET (applet));
+ atk_object_set_name (ao, _("Disk Mounter"));
+
+ gtk_widget_show_all (GTK_WIDGET (applet));
+
+ ret = TRUE;
+ }
+
+ return ret;
+}
+
+MATE_PANEL_APPLET_OUT_PROCESS_FACTORY (factory_iid,
+ PANEL_TYPE_APPLET,
+ "Drive-Mount-Applet",
+ applet_factory, NULL)
+