summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Zeuthen <davidz@redhat.com>2009-04-11 17:19:59 -0400
committerDavid Zeuthen <davidz@redhat.com>2009-04-11 17:19:59 -0400
commitf5c0e92affa5ec515d03eb1d8abfcccd2b4fe3eb (patch)
tree1b1e44099a98d64c4d3d199ace1fbe7e778711d2
parent01b800c41e0684d50ac60b3a54766f1e2cb4814c (diff)
downloadgnome-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.ac2
-rw-r--r--data/Makefile.am19
-rw-r--r--data/gdu-notification-daemon.desktop.in.in.in15
-rw-r--r--po/POTFILES.in4
-rw-r--r--po/POTFILES.skip5
-rw-r--r--src/Makefile.am2
-rw-r--r--src/notification/Makefile.am44
-rw-r--r--src/notification/gdu-slow-unmount-dialog.c295
-rw-r--r--src/notification/gdu-slow-unmount-dialog.h60
-rw-r--r--src/notification/notification-main.c275
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;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */