summaryrefslogtreecommitdiff
path: root/drivemount/src/drive-list.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivemount/src/drive-list.c')
-rw-r--r--drivemount/src/drive-list.c522
1 files changed, 522 insertions, 0 deletions
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);
+}