diff options
Diffstat (limited to 'drivemount/src')
-rw-r--r-- | drivemount/src/Makefile.am | 43 | ||||
-rw-r--r-- | drivemount/src/drive-button.c | 982 | ||||
-rw-r--r-- | drivemount/src/drive-button.h | 71 | ||||
-rw-r--r-- | drivemount/src/drive-list.c | 522 | ||||
-rw-r--r-- | drivemount/src/drive-list.h | 73 | ||||
-rw-r--r-- | drivemount/src/drivemount.c | 230 |
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) + |