[gnome-disk-utility: 3/4] add notification daemon



commit f5c0e92affa5ec515d03eb1d8abfcccd2b4fe3eb
Author: David Zeuthen <davidz redhat com>
Date:   Sat Apr 11 17:19:59 2009 -0400

    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
---
 configure.ac                                  |    2 +
 data/Makefile.am                              |   19 ++-
 data/gdu-notification-daemon.desktop.in.in.in |   15 ++
 po/POTFILES.in                                |    4 +
 po/POTFILES.skip                              |    5 +
 src/Makefile.am                               |    2 +-
 src/notification/Makefile.am                  |   44 ++++
 src/notification/gdu-slow-unmount-dialog.c    |  295 +++++++++++++++++++++++++
 src/notification/gdu-slow-unmount-dialog.h    |   60 +++++
 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;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]