diff options
author | David Zeuthen <davidz@redhat.com> | 2009-04-11 17:19:59 -0400 |
---|---|---|
committer | David Zeuthen <davidz@redhat.com> | 2009-04-11 17:19:59 -0400 |
commit | f5c0e92affa5ec515d03eb1d8abfcccd2b4fe3eb (patch) | |
tree | 1b1e44099a98d64c4d3d199ace1fbe7e778711d2 | |
parent | 01b800c41e0684d50ac60b3a54766f1e2cb4814c (diff) | |
download | gnome-disk-utility-f5c0e92affa5ec515d03eb1d8abfcccd2b4fe3eb.tar.gz gnome-disk-utility-f5c0e92affa5ec515d03eb1d8abfcccd2b4fe3eb.tar.xz gnome-disk-utility-f5c0e92affa5ec515d03eb1d8abfcccd2b4fe3eb.zip |
add notification daemon
First order of business for this daemon is to pop up dialogs for slow
unmont operations. These notifications look like this
http://people.freedesktop.org/~david/gdu-unmount-busy-1.png
http://people.freedesktop.org/~david/gdu-unmount-busy-2.png
See also
http://bugzilla.gnome.org/show_bug.cgi?id=570499
https://wiki.ubuntu.com/NotifyOSD#gnome-mount
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | data/Makefile.am | 19 | ||||
-rw-r--r-- | data/gdu-notification-daemon.desktop.in.in.in | 15 | ||||
-rw-r--r-- | po/POTFILES.in | 4 | ||||
-rw-r--r-- | po/POTFILES.skip | 5 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/notification/Makefile.am | 44 | ||||
-rw-r--r-- | src/notification/gdu-slow-unmount-dialog.c | 295 | ||||
-rw-r--r-- | src/notification/gdu-slow-unmount-dialog.h | 60 | ||||
-rw-r--r-- | src/notification/notification-main.c | 275 |
10 files changed, 718 insertions, 3 deletions
diff --git a/configure.ac b/configure.ac index 6fe573f..5450819 100644 --- a/configure.ac +++ b/configure.ac @@ -179,10 +179,12 @@ src/gdu/gdu.pc src/gdu-gtk/Makefile src/gdu-gtk/gdu-gtk.pc src/palimpsest/Makefile +src/notification/Makefile src/playground/Makefile src/playground/grid/Makefile po/Makefile.in data/Makefile +data/gdu-notification-daemon.desktop.in.in data/icons/Makefile data/icons/16x16/Makefile data/icons/22x22/Makefile diff --git a/data/Makefile.am b/data/Makefile.am index a45d814..785368d 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -1,18 +1,33 @@ +NULL = + SUBDIRS = icons desktopdir = $(datadir)/applications desktop_in_files = palimpsest.desktop.in desktop_DATA = $(desktop_in_files:.desktop.in=.desktop) +autostartdir = $(sysconfdir)/xdg/autostart +autostart_in_files = gdu-notification-daemon.desktop.in +autostart_DATA = $(autostart_in_files:.desktop.in=.desktop) + +gdu-notification-daemon.desktop.in: gdu-notification-daemon.desktop.in.in + @sed -e "s|\@LIBEXECDIR\@|$(libexecdir)|" $< > $@ + @INTLTOOL_DESKTOP_RULE@ distuninstallcheck_listfiles = find . -type f -print | grep -v scrollkeeper EXTRA_DIST = \ - $(desktop_in_files) + $(desktop_in_files) \ + $(autostart_in_files) \ + $(NULL) CLEANFILES = \ - $(desktop_DATA) + $(desktop_DATA) \ + $(autostart_DATA) \ + gdu-notification-daemon.desktop.in \ + gdu-notification-daemon.desktop.in.in \ + $(NULL) clean-local : rm -f *~ diff --git a/data/gdu-notification-daemon.desktop.in.in.in b/data/gdu-notification-daemon.desktop.in.in.in new file mode 100644 index 0000000..990cf61 --- /dev/null +++ b/data/gdu-notification-daemon.desktop.in.in.in @@ -0,0 +1,15 @@ +[Desktop Entry] +Encoding=UTF-8 +_Name=Disk Notifications +_Comment=Provides notifications related to disks +Icon=gdu-notification-daemon +Exec=@LIBEXECDIR@/gdu-notification-daemon +Terminal=false +Type=Application +Categories= +OnlyShowIn=GNOME; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=gnome-disk-utility +X-GNOME-Bugzilla-Component=notifications +X-GNOME-Bugzilla-Version=@VERSION@ +#X-GNOME-AutoRestart=true diff --git a/po/POTFILES.in b/po/POTFILES.in index 3a99d43..0af2f3c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -2,6 +2,7 @@ # List of source files containing translatable strings. # Please keep this file sorted alphabetically. data/palimpsest.desktop.in +data/gdu-notification-daemon.desktop.in.in.in src/gdu/gdu-ata-smart-attribute.c src/gdu/gdu-ata-smart-attribute.h src/gdu/gdu-ata-smart-historical-data.c @@ -39,6 +40,9 @@ src/gdu-gtk/gdu-gtk.h src/gdu-gtk/gdu-gtk-types.h src/gdu-gtk/gdu-time-label.c src/gdu-gtk/gdu-time-label.h +src/notification/gdu-slow-unmount-dialog.c +src/notification/gdu-slow-unmount-dialog.h +src/notification/notification-main.c src/palimpsest/gdu-main.c src/palimpsest/gdu-section.c src/palimpsest/gdu-section-create-partition-table.c diff --git a/po/POTFILES.skip b/po/POTFILES.skip new file mode 100644 index 0000000..e881904 --- /dev/null +++ b/po/POTFILES.skip @@ -0,0 +1,5 @@ +[encoding: UTF-8] +# List of files where to skip translations. +# Please keep this file sorted alphabetically. +data/gdu-notification-daemon.desktop.in +data/gdu-notification-daemon.desktop.in.in diff --git a/src/Makefile.am b/src/Makefile.am index 181303f..6a4453c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = gdu gdu-gtk palimpsest playground +SUBDIRS = gdu gdu-gtk palimpsest notification playground clean-local : rm -f *~ diff --git a/src/notification/Makefile.am b/src/notification/Makefile.am new file mode 100644 index 0000000..4a30c21 --- /dev/null +++ b/src/notification/Makefile.am @@ -0,0 +1,44 @@ + +NULL = + +libexec_PROGRAMS = gdu-notification-daemon + +gdu_notification_daemon_SOURCES = \ + notification-main.c \ + gdu-slow-unmount-dialog.h gdu-slow-unmount-dialog.c \ + $(NULL) + +gdu_notification_daemon_CPPFLAGS = \ + -I$(top_srcdir)/src/ \ + -I$(top_builddir)/src/ \ + -DG_LOG_DOMAIN=\"Palimpsest\" \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ + $(DISABLE_DEPRECATED) \ + -DGDU_API_IS_SUBJECT_TO_CHANGE \ + -DGDU_GTK_API_IS_SUBJECT_TO_CHANGE \ + $(AM_CPPFLAGS) + +gdu_notification_daemon_CFLAGS = \ + $(GLIB2_CFLAGS) \ + $(GOBJECT2_CFLAGS) \ + $(GIO2_CFLAGS) \ + $(GIO_UNIX2_CFLAGS) \ + $(GTK2_CFLAGS) \ + $(WARN_CFLAGS) \ + $(POLKIT_DBUS_CFLAGS) \ + $(POLKIT_GNOME_CFLAGS) \ + $(AM_CFLAGS) + +gdu_notification_daemon_LDFLAGS = \ + $(AM_LDFLAGS) + +gdu_notification_daemon_LDADD = \ + $(GLIB2_LIBS) \ + $(GOBJECT2_LIBS) \ + $(GIO2_LIBS) \ + $(GTK2_LIBS) \ + $(top_builddir)/src/gdu/libgdu.la \ + $(top_builddir)/src/gdu-gtk/libgdu-gtk.la + +clean-local : + rm -f *~ diff --git a/src/notification/gdu-slow-unmount-dialog.c b/src/notification/gdu-slow-unmount-dialog.c new file mode 100644 index 0000000..dc95be6 --- /dev/null +++ b/src/notification/gdu-slow-unmount-dialog.c @@ -0,0 +1,295 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* gdu-slow-unmount-dialog.c + * + * Copyright (C) 2009 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <config.h> +#include <glib/gi18n.h> + +#include <gdu-gtk/gdu-gtk.h> + +#include "gdu-slow-unmount-dialog.h" + +struct GduSlowUnmountDialogPrivate +{ + GduDevice *device; + GduPool *pool; + GduPresentable *presentable; + gchar *device_name; + + GtkWidget *label; + GtkWidget *progress_bar; + GtkWidget *details_label; + + guint pulse_timer_id; + gboolean still_unmounting; +}; + +enum +{ + PROP_0, + PROP_DEVICE, +}; + +static void on_device_removed (GduDevice *device, + gpointer user_data); +static void on_device_changed (GduDevice *device, + gpointer user_data); +static void on_device_job_changed (GduDevice *device, + gpointer user_data); + +G_DEFINE_TYPE (GduSlowUnmountDialog, gdu_slow_unmount_dialog, GTK_TYPE_DIALOG) + +static void +gdu_slow_unmount_dialog_finalize (GObject *object) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (object); + + g_signal_handlers_disconnect_by_func (dialog->priv->device, on_device_removed, dialog); + g_signal_handlers_disconnect_by_func (dialog->priv->device, on_device_changed, dialog); + g_signal_handlers_disconnect_by_func (dialog->priv->device, on_device_job_changed, dialog); + g_object_unref (dialog->priv->device); + g_object_unref (dialog->priv->pool); + g_object_unref (dialog->priv->presentable); + if (dialog->priv->pulse_timer_id > 0) + g_source_remove (dialog->priv->pulse_timer_id); + g_free (dialog->priv->device_name); + + if (G_OBJECT_CLASS (gdu_slow_unmount_dialog_parent_class)->finalize != NULL) + G_OBJECT_CLASS (gdu_slow_unmount_dialog_parent_class)->finalize (object); +} + +static void +gdu_slow_unmount_dialog_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (object); + + switch (property_id) { + case PROP_DEVICE: + g_value_set_object (value, dialog->priv->device); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +gdu_slow_unmount_dialog_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (object); + + switch (property_id) { + case PROP_DEVICE: + dialog->priv->device = g_value_dup_object (value); + dialog->priv->pool = gdu_device_get_pool (dialog->priv->device); + dialog->priv->presentable = gdu_pool_get_volume_by_device (dialog->priv->pool, + dialog->priv->device); + dialog->priv->device_name = gdu_presentable_get_name (dialog->priv->presentable); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static gboolean +on_pulse_timeout (gpointer user_data) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (user_data); + + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (dialog->priv->progress_bar)); + + /* keep timeout around */ + return TRUE; +} + +static void +check_still_unmounting (GduSlowUnmountDialog *dialog) +{ + gchar *s; + + if (!dialog->priv->still_unmounting) + goto out; + + if (gdu_device_job_in_progress (dialog->priv->device) && + g_strcmp0 (gdu_device_job_get_id (dialog->priv->device), "FilesystemUnmount") == 0) + goto out; + + dialog->priv->still_unmounting = FALSE; + + if (dialog->priv->pulse_timer_id > 0) { + g_source_remove (dialog->priv->pulse_timer_id); + dialog->priv->pulse_timer_id = 0; + } + gtk_widget_hide (dialog->priv->progress_bar); + gtk_widget_hide (dialog->priv->details_label); + + /* Translators: %s is the name of the device */ + s = g_strdup_printf (_("It's now safe to remove \"%s\"."), + dialog->priv->device_name); + gtk_label_set_markup (GTK_LABEL (dialog->priv->label), s); + + out: + ; +} + +static void +on_device_removed (GduDevice *device, + gpointer user_data) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (user_data); + /* when the device is removed, just destroy the dialog */ + gtk_widget_destroy (GTK_WIDGET (dialog)); +} + +static void +on_device_changed (GduDevice *device, + gpointer user_data) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (user_data); + check_still_unmounting (dialog); +} + +static void +on_device_job_changed (GduDevice *device, + gpointer user_data) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (user_data); + check_still_unmounting (dialog); +} + +static void +gdu_slow_unmount_dialog_constructed (GObject *object) +{ + GduSlowUnmountDialog *dialog = GDU_SLOW_UNMOUNT_DIALOG (object); + GtkWidget *content_area; + GtkWidget *align; + GdkPixbuf *pixbuf; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *progress_bar; + gchar *s; + + + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + + gtk_window_set_title (GTK_WINDOW (dialog), dialog->priv->device_name); + pixbuf = gdu_util_get_pixbuf_for_presentable (dialog->priv->presentable, GTK_ICON_SIZE_MENU); + gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf); + g_object_unref (pixbuf); + + content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); + + align = gtk_alignment_new (0.5, 0.5, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 12, 12, 12, 12); + gtk_box_pack_start (GTK_BOX (content_area), align, TRUE, TRUE, 0); + + vbox = gtk_vbox_new (FALSE, 12); + gtk_container_add (GTK_CONTAINER (align), vbox); + + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + /* Translators: %s is the name of the device */ + s = g_strdup_printf (_("Writing data to \"%s\"..."), + dialog->priv->device_name); + gtk_label_set_markup (GTK_LABEL (label), s); + g_free (s); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + dialog->priv->label = label; + + progress_bar = gtk_progress_bar_new (); + gtk_box_pack_start (GTK_BOX (vbox), progress_bar, FALSE, FALSE, 0); + dialog->priv->progress_bar = progress_bar; + + label = gtk_label_new (NULL); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + s = g_strconcat ("<small>", + _("To prevent data loss, wait until this has finished before removing " + "media or disconnecting the device."), + "</small>", + NULL); + gtk_label_set_markup (GTK_LABEL (label), s); + g_free (s); + gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0); + dialog->priv->details_label = label; + + dialog->priv->pulse_timer_id = g_timeout_add (50, + on_pulse_timeout, + dialog); + + g_signal_connect (dialog->priv->device, "removed", G_CALLBACK (on_device_removed), dialog); + g_signal_connect (dialog->priv->device, "changed", G_CALLBACK (on_device_changed), dialog); + g_signal_connect (dialog->priv->device, "job-changed", G_CALLBACK (on_device_job_changed), dialog); + + dialog->priv->still_unmounting = TRUE; + + if (G_OBJECT_CLASS (gdu_slow_unmount_dialog_parent_class)->constructed != NULL) + G_OBJECT_CLASS (gdu_slow_unmount_dialog_parent_class)->constructed (object); +} + +static void +gdu_slow_unmount_dialog_class_init (GduSlowUnmountDialogClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (GduSlowUnmountDialogPrivate)); + + object_class->get_property = gdu_slow_unmount_dialog_get_property; + object_class->set_property = gdu_slow_unmount_dialog_set_property; + object_class->constructed = gdu_slow_unmount_dialog_constructed; + object_class->finalize = gdu_slow_unmount_dialog_finalize; + + g_object_class_install_property (object_class, + PROP_DEVICE, + g_param_spec_object ("device", + _("Device"), + _("The device to show the dialog for"), + GDU_TYPE_DEVICE, + G_PARAM_READABLE | + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +gdu_slow_unmount_dialog_init (GduSlowUnmountDialog *dialog) +{ + dialog->priv = G_TYPE_INSTANCE_GET_PRIVATE (dialog, + GDU_TYPE_SLOW_UNMOUNT_DIALOG, + GduSlowUnmountDialogPrivate); +} + +GtkWidget * +gdu_slow_unmount_dialog_new (GtkWindow *parent, + GduDevice *device) +{ + return GTK_WIDGET (g_object_new (GDU_TYPE_SLOW_UNMOUNT_DIALOG, + "transient-for", parent, + "device", device, + NULL)); +} + diff --git a/src/notification/gdu-slow-unmount-dialog.h b/src/notification/gdu-slow-unmount-dialog.h new file mode 100644 index 0000000..e04c35a --- /dev/null +++ b/src/notification/gdu-slow-unmount-dialog.h @@ -0,0 +1,60 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* gdu-ata-smart-dialog.h + * + * Copyright (C) 2009 David Zeuthen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef __GDU_SLOW_UNMOUNT_DIALOG_H +#define __GDU_SLOW_UNMOUNT_DIALOG_H + +#include <gdu/gdu.h> +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define GDU_TYPE_SLOW_UNMOUNT_DIALOG gdu_slow_unmount_dialog_get_type() +#define GDU_SLOW_UNMOUNT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDU_TYPE_SLOW_UNMOUNT_DIALOG, GduSlowUnmountDialog)) +#define GDU_SLOW_UNMOUNT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GDU_TYPE_SLOW_UNMOUNT_DIALOG, GduSlowUnmountDialogClass)) +#define GDU_IS_SLOW_UNMOUNT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDU_TYPE_SLOW_UNMOUNT_DIALOG)) +#define GDU_IS_SLOW_UNMOUNT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDU_TYPE_SLOW_UNMOUNT_DIALOG)) +#define GDU_SLOW_UNMOUNT_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDU_TYPE_SLOW_UNMOUNT_DIALOG, GduSlowUnmountDialogClass)) + +typedef struct GduSlowUnmountDialog GduSlowUnmountDialog; +typedef struct GduSlowUnmountDialogClass GduSlowUnmountDialogClass; +typedef struct GduSlowUnmountDialogPrivate GduSlowUnmountDialogPrivate; + +struct GduSlowUnmountDialog +{ + GtkDialog parent; + + /*< private >*/ + GduSlowUnmountDialogPrivate *priv; +}; + +struct GduSlowUnmountDialogClass +{ + GtkDialogClass parent_class; +}; + +GType gdu_slow_unmount_dialog_get_type (void) G_GNUC_CONST; +GtkWidget* gdu_slow_unmount_dialog_new (GtkWindow *parent, + GduDevice *device); + +G_END_DECLS + +#endif /* __GDU_SLOW_UNMOUNT_DIALOG_H */ diff --git a/src/notification/notification-main.c b/src/notification/notification-main.c new file mode 100644 index 0000000..a5cb827 --- /dev/null +++ b/src/notification/notification-main.c @@ -0,0 +1,275 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* notification-main.c + * + * Copyright (C) 2009 David Zeuthen <davidz@redhat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <config.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> + +#include <gdu/gdu.h> +#include <gdu-gtk/gdu-gtk.h> + +#include "gdu-slow-unmount-dialog.h" + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +diff_sorted_lists (GList *list1, + GList *list2, + GCompareFunc compare, + GList **added, + GList **removed) +{ + int order; + + *added = *removed = NULL; + + while (list1 != NULL && + list2 != NULL) + { + order = (*compare) (list1->data, list2->data); + if (order < 0) + { + *removed = g_list_prepend (*removed, list1->data); + list1 = list1->next; + } + else if (order > 0) + { + *added = g_list_prepend (*added, list2->data); + list2 = list2->next; + } + else + { /* same item */ + list1 = list1->next; + list2 = list2->next; + } + } + + while (list1 != NULL) + { + *removed = g_list_prepend (*removed, list1->data); + list1 = list1->next; + } + while (list2 != NULL) + { + *added = g_list_prepend (*added, list2->data); + list2 = list2->next; + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +typedef struct +{ + GduPool *pool; + + GList *devices_being_unmounted; +} NotificationData; + +/* ---------------------------------------------------------------------------------------------------- */ + +static gint +ptr_compare (gconstpointer a, gconstpointer b) +{ + if (a > b) + return 1; + else if (a < b) + return -1; + else + return 0; +} + +static gboolean +show_unmount_dialog (gpointer user_data) +{ + GduDevice *device = GDU_DEVICE (user_data); + GtkWidget *dialog; + + g_debug ("show unmount dialog for %s", gdu_device_get_device_file (device)); + + dialog = gdu_slow_unmount_dialog_new (NULL, device); + gtk_widget_show_all (GTK_WIDGET (dialog)); + gtk_window_present (GTK_WINDOW (dialog)); + + /* remove the timeout */ + return FALSE; +} + +static void +update_unmount_dialogs (NotificationData *data) +{ + GList *devices; + GList *currently_being_unmounted; + GList *l; + GList *added; + GList *removed; + + devices = gdu_pool_get_devices (data->pool); + + currently_being_unmounted = NULL; + + for (l = devices; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + + /* TODO: maybe we shouldn't put up a dialog if other bits of the device + * is busy (e.g. another mounted file system) + */ + + if (!(gdu_device_job_in_progress (device) && + g_strcmp0 (gdu_device_job_get_id (device), "FilesystemUnmount") == 0 && + gdu_device_job_get_initiated_by_uid (device) == getuid ())) + continue; + + currently_being_unmounted = g_list_prepend (currently_being_unmounted, device); + } + + currently_being_unmounted = g_list_sort (currently_being_unmounted, ptr_compare); + data->devices_being_unmounted = g_list_sort (data->devices_being_unmounted, ptr_compare); + + added = removed = NULL; + diff_sorted_lists (data->devices_being_unmounted, + currently_being_unmounted, + ptr_compare, + &added, + &removed); + + for (l = removed; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + guint countdown_timer_id; + + /* remove countdown timer if applicable */ + countdown_timer_id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), + "unmount-countdown-timer-id")); + if (countdown_timer_id > 0) + g_source_remove (countdown_timer_id); + + data->devices_being_unmounted = g_list_remove (data->devices_being_unmounted, device); + g_object_unref (device); + } + + for (l = added; l != NULL; l = l->next) { + GduDevice *device = GDU_DEVICE (l->data); + guint countdown_timer_id; + + data->devices_being_unmounted = g_list_prepend (data->devices_being_unmounted, g_object_ref (device)); + + /* start a countdown timer */ + countdown_timer_id = g_timeout_add (750, + show_unmount_dialog, + device); + g_object_set_data (G_OBJECT (device), + "unmount-countdown-timer-id", + GUINT_TO_POINTER (countdown_timer_id)); + } + + g_list_foreach (devices, (GFunc) g_object_unref, NULL); + g_list_free (devices); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +on_device_added (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_unmount_dialogs (data); +} + +static void +on_device_removed (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_unmount_dialogs (data); +} + +static void +on_device_changed (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_unmount_dialogs (data); +} + +static void +on_device_job_changed (GduPool *pool, + GduDevice *device, + gpointer user_data) +{ + NotificationData *data = user_data; + update_unmount_dialogs (data); +} + +static NotificationData * +notification_data_new (void) +{ + NotificationData *data; + data = g_new0 (NotificationData, 1); + data->pool = gdu_pool_new (); + g_signal_connect (data->pool, "device-added", G_CALLBACK (on_device_added), data); + g_signal_connect (data->pool, "device-removed", G_CALLBACK (on_device_removed), data); + g_signal_connect (data->pool, "device-changed", G_CALLBACK (on_device_changed), data); + g_signal_connect (data->pool, "device-job-changed", G_CALLBACK (on_device_job_changed), data); + return data; +} + +static void +notification_data_free (NotificationData *data) +{ + g_signal_handlers_disconnect_by_func (data->pool, on_device_added, data); + g_signal_handlers_disconnect_by_func (data->pool, on_device_removed, data); + g_signal_handlers_disconnect_by_func (data->pool, on_device_changed, data); + g_signal_handlers_disconnect_by_func (data->pool, on_device_job_changed, data); + g_object_unref (data->pool); + g_list_foreach (data->devices_being_unmounted, (GFunc) g_object_unref, NULL); + g_list_free (data->devices_being_unmounted); + g_free (data); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, char **argv) +{ + NotificationData *data; + + gtk_init (&argc, &argv); + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gtk_window_set_default_icon_name ("palimpsest"); + + data = notification_data_new (); + + gtk_main (); + + notification_data_free (data); + + return 0; +} + +/* ---------------------------------------------------------------------------------------------------- */ |