From f634238ad7e356ef60a4ab2dcf53a0121172ee0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ball=C3=B3=20Gy=C3=B6rgy?= Date: Sat, 18 Oct 2014 22:45:08 +0200 Subject: [PATCH 1/1] Add libautomount-manager It was previously in gnome-settings-daemon. --- configure.ac | 5 + data/org.gnome.gnome-flashback.gschema.xml.in.in | 5 + gnome-flashback/Makefile.am | 2 + gnome-flashback/flashback-application.c | 22 +- gnome-flashback/libautomount-manager/Makefile.am | 17 + .../libautomount-manager/gsd-automount-manager.c | 571 ++++++++++++ .../libautomount-manager/gsd-automount-manager.h | 58 ++ gnome-flashback/libautomount-manager/gsd-autorun.c | 975 +++++++++++++++++++++ gnome-flashback/libautomount-manager/gsd-autorun.h | 53 ++ po/POTFILES.in | 2 + 10 files changed, 1709 insertions(+), 1 deletion(-) create mode 100644 gnome-flashback/libautomount-manager/Makefile.am create mode 100644 gnome-flashback/libautomount-manager/gsd-automount-manager.c create mode 100644 gnome-flashback/libautomount-manager/gsd-automount-manager.h create mode 100644 gnome-flashback/libautomount-manager/gsd-autorun.c create mode 100644 gnome-flashback/libautomount-manager/gsd-autorun.h diff --git a/configure.ac b/configure.ac index 1ced253..6f5ee7c 100644 --- a/configure.ac +++ b/configure.ac @@ -44,6 +44,10 @@ PKG_CHECK_MODULES(END_SESSION_DIALOG, gtk+-3.0 >= $GTK_REQUIRED) AC_SUBST(END_SESSION_DIALOG_CFLAGS) AC_SUBST(END_SESSION_DIALOG_LIBS) +PKG_CHECK_MODULES(AUTOMOUNT_MANAGER, gtk+-3.0 >= $GTK_REQUIRED) +AC_SUBST(AUTOMOUNT_MANAGER_CFLAGS) +AC_SUBST(AUTOMOUNT_MANAGER_LIBS) + PKG_CHECK_MODULES(DESKTOP_BACKGROUND, gtk+-3.0 >= $GTK_REQUIRED gnome-desktop-3.0 >= $LIBGNOME_DESKTOP_REQUIRED) AC_SUBST(DESKTOP_BACKGROUND_CFLAGS) AC_SUBST(DESKTOP_BACKGROUND_LIBS) @@ -67,6 +71,7 @@ AC_CONFIG_FILES([ Makefile data/Makefile gnome-flashback/Makefile +gnome-flashback/libautomount-manager/Makefile gnome-flashback/libdesktop-background/Makefile gnome-flashback/libdisplay-config/Makefile gnome-flashback/libend-session-dialog/Makefile diff --git a/data/org.gnome.gnome-flashback.gschema.xml.in.in b/data/org.gnome.gnome-flashback.gschema.xml.in.in index 38ed32b..5c766a2 100644 --- a/data/org.gnome.gnome-flashback.gschema.xml.in.in +++ b/data/org.gnome.gnome-flashback.gschema.xml.in.in @@ -1,5 +1,10 @@ + + true + <_summary>Automount manager + <_description>If set to true, then GNOME Flashback application will be used to automount removable media. + true <_summary>Desktop background diff --git a/gnome-flashback/Makefile.am b/gnome-flashback/Makefile.am index 48f1b50..14f3e08 100644 --- a/gnome-flashback/Makefile.am +++ b/gnome-flashback/Makefile.am @@ -1,4 +1,5 @@ SUBDIRS = \ + libautomount-manager \ libdesktop-background \ libdisplay-config \ libend-session-dialog \ @@ -20,6 +21,7 @@ gnome_flashback_SOURCES = \ gnome_flashback_LDADD = \ $(GNOME_FLASHBACK_LIBS) \ + $(top_builddir)/gnome-flashback/libautomount-manager/libautomount-manager.la \ $(top_builddir)/gnome-flashback/libdesktop-background/libdesktop-background.la \ $(top_builddir)/gnome-flashback/libdisplay-config/libdisplay-config.la \ $(top_builddir)/gnome-flashback/libend-session-dialog/libend-session-dialog.la \ diff --git a/gnome-flashback/flashback-application.c b/gnome-flashback/flashback-application.c index dc5afda..020f1f8 100644 --- a/gnome-flashback/flashback-application.c +++ b/gnome-flashback/flashback-application.c @@ -19,6 +19,7 @@ #include #include "config.h" #include "flashback-application.h" +#include "libautomount-manager/gsd-automount-manager.h" #include "libdesktop-background/desktop-background.h" #include "libdisplay-config/flashback-display-config.h" #include "libend-session-dialog/flashback-end-session-dialog.h" @@ -26,6 +27,7 @@ #include "libsound-applet/gvc-applet.h" #define FLASHBACK_SCHEMA "org.gnome.gnome-flashback" +#define KEY_AUTOMOUNT_MANAGER "automount-manager" #define KEY_DESKTOP_BACKGROUND "desktop-background" #define KEY_DISPLAY_CONFIG "display-config" #define KEY_END_SESSION_DIALOG "end-session-dialog" @@ -34,7 +36,7 @@ struct _FlashbackApplicationPrivate { GSettings *settings; - + GsdAutomountManager *automount; DesktopBackground *background; FlashbackDisplayConfig *config; FlashbackEndSessionDialog *dialog; @@ -51,6 +53,19 @@ flashback_application_settings_changed (GSettings *settings, { FlashbackApplication *app = FLASHBACK_APPLICATION (user_data); + if (key == NULL || g_strcmp0 (key, KEY_AUTOMOUNT_MANAGER) == 0) { + if (g_settings_get_boolean (settings, KEY_AUTOMOUNT_MANAGER)) { + if (app->priv->automount == NULL) { + app->priv->automount = gsd_automount_manager_new (); + } + } else { + if (app->priv->automount) { + g_object_unref (app->priv->automount); + app->priv->automount = NULL; + } + } + } + if (key == NULL || g_strcmp0 (key, KEY_DESKTOP_BACKGROUND) == 0) { if (g_settings_get_boolean (settings, KEY_DESKTOP_BACKGROUND)) { if (app->priv->background == NULL) { @@ -147,6 +162,11 @@ flashback_application_shutdown (GApplication *application) { FlashbackApplication *app = FLASHBACK_APPLICATION (application); + if (app->priv->automount) { + g_object_unref (app->priv->automount); + app->priv->automount = NULL; + } + if (app->priv->background) { g_object_unref (app->priv->background); app->priv->background = NULL; diff --git a/gnome-flashback/libautomount-manager/Makefile.am b/gnome-flashback/libautomount-manager/Makefile.am new file mode 100644 index 0000000..d73127b --- /dev/null +++ b/gnome-flashback/libautomount-manager/Makefile.am @@ -0,0 +1,17 @@ +noinst_LTLIBRARIES = \ + libautomount-manager.la + +AM_CPPFLAGS = \ + $(AUTOMOUNT_MANAGER_CFLAGS) \ + -I$(top_builddir)/gnome-flashback/libautomount-manager + +libautomount_manager_la_SOURCES = \ + gsd-automount-manager.c \ + gsd-automount-manager.h \ + gsd-autorun.c \ + gsd-autorun.h + +libautomount_manager_la_LIBADD = \ + $(AUTOMOUNT_MANAGER_LIBS) + +-include $(top_srcdir)/git.mk diff --git a/gnome-flashback/libautomount-manager/gsd-automount-manager.c b/gnome-flashback/libautomount-manager/gsd-automount-manager.c new file mode 100644 index 0000000..33af460 --- /dev/null +++ b/gnome-flashback/libautomount-manager/gsd-automount-manager.c @@ -0,0 +1,571 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Tomas Bzatek + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#include "gsd-automount-manager.h" +#include "gsd-autorun.h" + +#define GSD_AUTOMOUNT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManagerPrivate)) + +#define GNOME_SESSION_DBUS_NAME "org.gnome.SessionManager" +#define GNOME_SESSION_DBUS_OBJECT "/org/gnome/SessionManager" +#define GNOME_SESSION_DBUS_INTERFACE "org.gnome.SessionManager" + +struct GsdAutomountManagerPrivate +{ + GSettings *settings; + + GVolumeMonitor *volume_monitor; + unsigned int automount_idle_id; + + GDBusProxy *session; + gboolean session_is_active; + gboolean screensaver_active; + guint ss_watch_id; + GDBusProxy *ss_proxy; + + GList *volume_queue; +}; + +static void gsd_automount_manager_class_init (GsdAutomountManagerClass *klass); +static void gsd_automount_manager_init (GsdAutomountManager *gsd_automount_manager); + +G_DEFINE_TYPE (GsdAutomountManager, gsd_automount_manager, G_TYPE_OBJECT) + +GDBusProxy * +get_session_proxy (void) +{ + static GDBusProxy *session_proxy; + GError *error = NULL; + + if (session_proxy != NULL) { + g_object_ref (session_proxy); + } else { + session_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + GNOME_SESSION_DBUS_NAME, + GNOME_SESSION_DBUS_OBJECT, + GNOME_SESSION_DBUS_INTERFACE, + NULL, + &error); + if (error) { + g_warning ("Failed to connect to the session manager: %s", error->message); + g_error_free (error); + } else { + g_object_add_weak_pointer (G_OBJECT (session_proxy), (gpointer*)&session_proxy); + } + } + + return session_proxy; +} + +static GtkDialog * +show_error_dialog (const char *primary_text, + const char *secondary_text) +{ + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + "%s", ""); + + g_object_set (dialog, + "text", primary_text, + "secondary-text", secondary_text, + NULL); + + gtk_widget_show (GTK_WIDGET (dialog)); + + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + return GTK_DIALOG (dialog); +} + +static void +startup_volume_mount_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + g_volume_mount_finish (G_VOLUME (source_object), res, NULL); +} + +static void +automount_all_volumes (GsdAutomountManager *manager) +{ + GList *volumes, *l; + GMount *mount; + GVolume *volume; + + if (g_settings_get_boolean (manager->priv->settings, "automount")) { + /* automount all mountable volumes at start-up */ + volumes = g_volume_monitor_get_volumes (manager->priv->volume_monitor); + for (l = volumes; l != NULL; l = l->next) { + volume = l->data; + + if (!g_volume_should_automount (volume) || + !g_volume_can_mount (volume)) { + continue; + } + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + g_object_unref (mount); + continue; + } + + /* pass NULL as GMountOperation to avoid user interaction */ + g_volume_mount (volume, 0, NULL, NULL, startup_volume_mount_cb, NULL); + } + g_list_free_full (volumes, g_object_unref); + } +} + +static gboolean +automount_all_volumes_idle_cb (gpointer data) +{ + GsdAutomountManager *manager = GSD_AUTOMOUNT_MANAGER (data); + + automount_all_volumes (manager); + + manager->priv->automount_idle_id = 0; + return FALSE; +} + +static void +volume_mount_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GMountOperation *mount_op = user_data; + GError *error; + char *primary; + char *name; + + error = NULL; + gsd_allow_autorun_for_volume_finish (G_VOLUME (source_object)); + if (!g_volume_mount_finish (G_VOLUME (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_volume_get_name (G_VOLUME (source_object)); + primary = g_strdup_printf (_("Unable to mount %s"), name); + g_free (name); + show_error_dialog (primary, + error->message); + g_free (primary); + } + g_error_free (error); + } + + g_object_unref (mount_op); +} + +static void +do_mount_volume (GVolume *volume) +{ + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (NULL); + g_mount_operation_set_password_save (mount_op, G_PASSWORD_SAVE_FOR_SESSION); + + gsd_allow_autorun_for_volume (volume); + g_volume_mount (volume, 0, mount_op, NULL, volume_mount_cb, mount_op); +} + +static void +check_volume_queue (GsdAutomountManager *manager) +{ + GList *l; + GVolume *volume; + + if (manager->priv->screensaver_active) + return; + + l = manager->priv->volume_queue; + + while (l != NULL) { + volume = l->data; + + do_mount_volume (volume); + manager->priv->volume_queue = + g_list_remove (manager->priv->volume_queue, volume); + + g_object_unref (volume); + l = l->next; + } + + manager->priv->volume_queue = NULL; +} + +static void +check_screen_lock_and_mount (GsdAutomountManager *manager, + GVolume *volume) +{ + if (!manager->priv->session_is_active) + return; + + if (manager->priv->screensaver_active) { + /* queue the volume, to mount it after the screensaver state changed */ + g_debug ("Queuing volume %p", volume); + manager->priv->volume_queue = g_list_prepend (manager->priv->volume_queue, + g_object_ref (volume)); + } else { + /* mount it immediately */ + do_mount_volume (volume); + } +} + +static void +volume_removed_callback (GVolumeMonitor *monitor, + GVolume *volume, + GsdAutomountManager *manager) +{ + g_debug ("Volume %p removed, removing from the queue", volume); + + /* clear it from the queue, if present */ + manager->priv->volume_queue = + g_list_remove (manager->priv->volume_queue, volume); +} + +static void +volume_added_callback (GVolumeMonitor *monitor, + GVolume *volume, + GsdAutomountManager *manager) +{ + if (g_settings_get_boolean (manager->priv->settings, "automount") && + g_volume_should_automount (volume) && + g_volume_can_mount (volume)) { + check_screen_lock_and_mount (manager, volume); + } else { + /* Allow gsd_autorun() to run. When the mount is later + * added programmatically (i.e. for a blank CD), + * gsd_autorun() will be called by mount_added_callback(). */ + gsd_allow_autorun_for_volume (volume); + gsd_allow_autorun_for_volume_finish (volume); + } +} + +static void +autorun_show_window (GMount *mount, gpointer user_data) +{ + GFile *location; + char *uri; + GError *error; + char *primary; + char *name; + + location = g_mount_get_root (mount); + uri = g_file_get_uri (location); + + error = NULL; + /* use default folder handler */ + if (! gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error)) { + name = g_mount_get_name (mount); + primary = g_strdup_printf (_("Unable to open a folder for %s"), name); + g_free (name); + show_error_dialog (primary, + error->message); + g_free (primary); + g_error_free (error); + } + + g_free (uri); + g_object_unref (location); +} + +static void +mount_added_callback (GVolumeMonitor *monitor, + GMount *mount, + GsdAutomountManager *manager) +{ + /* don't autorun if the session is not active */ + if (!manager->priv->session_is_active) { + return; + } + + gsd_autorun (mount, manager->priv->settings, autorun_show_window, manager); +} + + +static void +session_props_changed (GDBusProxy *session, GVariant *v, char **props, gpointer user_data) +{ + GsdAutomountManager *manager = user_data; + GsdAutomountManagerPrivate *p = manager->priv; + GVariant *active_v = NULL; + gboolean is_active; + + active_v = g_dbus_proxy_get_cached_property (session, "SessionIsActive"); + if (!active_v) + return; + + g_variant_get (active_v, "b", &is_active); + g_variant_unref (active_v); + g_printerr ("AUTOMOUNT: session is active: %d -> %d\n", p->session_is_active, is_active); + p->session_is_active = is_active; + + if (!p->session_is_active) { + if (p->volume_queue != NULL) { + g_list_free_full (p->volume_queue, g_object_unref); + p->volume_queue = NULL; + } + } +} + +static void +do_initialize_session (GsdAutomountManager *manager) +{ + manager->priv->session = get_session_proxy (); + g_signal_connect (manager->priv->session, "g-properties-changed", + G_CALLBACK (session_props_changed), manager); + session_props_changed (manager->priv->session, NULL, NULL, manager); +} + +#define SCREENSAVER_NAME "org.gnome.ScreenSaver" +#define SCREENSAVER_PATH "/org/gnome/ScreenSaver" +#define SCREENSAVER_INTERFACE "org.gnome.ScreenSaver" + +static void +screensaver_signal_callback (GDBusProxy *proxy, + const gchar *sender_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GsdAutomountManager *manager = user_data; + + if (g_strcmp0 (signal_name, "ActiveChanged") == 0) { + g_variant_get (parameters, "(b)", &manager->priv->screensaver_active); + g_debug ("Screensaver active changed to %d", manager->priv->screensaver_active); + + check_volume_queue (manager); + } +} + +static void +screensaver_get_active_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GsdAutomountManager *manager = user_data; + GDBusProxy *proxy = manager->priv->ss_proxy; + GVariant *result; + GError *error = NULL; + + result = g_dbus_proxy_call_finish (proxy, + res, + &error); + + if (error != NULL) { + g_warning ("Can't call GetActive() on the ScreenSaver object: %s", + error->message); + g_error_free (error); + + return; + } + + g_variant_get (result, "(b)", &manager->priv->screensaver_active); + g_variant_unref (result); + + g_debug ("Screensaver GetActive() returned %d", manager->priv->screensaver_active); +} + +static void +screensaver_proxy_ready_cb (GObject *source, + GAsyncResult *res, + gpointer user_data) +{ + GsdAutomountManager *manager = user_data; + GError *error = NULL; + GDBusProxy *ss_proxy; + + ss_proxy = g_dbus_proxy_new_finish (res, &error); + + if (error != NULL) { + g_warning ("Can't get proxy for the ScreenSaver object: %s", + error->message); + g_error_free (error); + + return; + } + + g_debug ("ScreenSaver proxy ready"); + + manager->priv->ss_proxy = ss_proxy; + + g_signal_connect (ss_proxy, "g-signal", + G_CALLBACK (screensaver_signal_callback), manager); + + g_dbus_proxy_call (ss_proxy, + "GetActive", + NULL, + G_DBUS_CALL_FLAGS_NO_AUTO_START, + -1, + NULL, + screensaver_get_active_ready_cb, + manager); +} + +static void +screensaver_appeared_callback (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + GsdAutomountManager *manager = user_data; + + g_debug ("ScreenSaver name appeared"); + + manager->priv->screensaver_active = FALSE; + + g_dbus_proxy_new (connection, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + NULL, + name, + SCREENSAVER_PATH, + SCREENSAVER_INTERFACE, + NULL, + screensaver_proxy_ready_cb, + manager); +} + +static void +screensaver_vanished_callback (GDBusConnection *connection, + const gchar *name, + gpointer user_data) +{ + GsdAutomountManager *manager = user_data; + + g_debug ("ScreenSaver name vanished"); + + manager->priv->screensaver_active = FALSE; + g_clear_object (&manager->priv->ss_proxy); + + /* in this case force a clear of the volume queue, without + * mounting them. + */ + if (manager->priv->volume_queue != NULL) { + g_list_free_full (manager->priv->volume_queue, g_object_unref); + manager->priv->volume_queue = NULL; + } +} + +static void +do_initialize_screensaver (GsdAutomountManager *manager) +{ + GsdAutomountManagerPrivate *p = manager->priv; + + p->ss_watch_id = + g_bus_watch_name (G_BUS_TYPE_SESSION, + SCREENSAVER_NAME, + G_BUS_NAME_WATCHER_FLAGS_NONE, + screensaver_appeared_callback, + screensaver_vanished_callback, + manager, + NULL); +} + +static void +setup_automounter (GsdAutomountManager *manager) +{ + do_initialize_session (manager); + do_initialize_screensaver (manager); + + manager->priv->volume_monitor = g_volume_monitor_get (); + g_signal_connect_object (manager->priv->volume_monitor, "mount-added", + G_CALLBACK (mount_added_callback), manager, 0); + g_signal_connect_object (manager->priv->volume_monitor, "volume-added", + G_CALLBACK (volume_added_callback), manager, 0); + g_signal_connect_object (manager->priv->volume_monitor, "volume-removed", + G_CALLBACK (volume_removed_callback), manager, 0); + + manager->priv->automount_idle_id = + g_idle_add_full (G_PRIORITY_LOW, + automount_all_volumes_idle_cb, + manager, NULL); +} + +void +gsd_automount_manager_finalize (GObject *object) +{ + GsdAutomountManager *manager = GSD_AUTOMOUNT_MANAGER (object); + GsdAutomountManagerPrivate *p = manager->priv; + + g_debug ("Stopping automounting manager"); + + g_clear_object (&p->session); + g_clear_object (&p->volume_monitor); + g_clear_object (&p->settings); + g_clear_object (&p->ss_proxy); + + g_bus_unwatch_name (p->ss_watch_id); + + if (p->volume_queue != NULL) { + g_list_free_full (p->volume_queue, g_object_unref); + p->volume_queue = NULL; + } + + if (p->automount_idle_id != 0) { + g_source_remove (p->automount_idle_id); + p->automount_idle_id = 0; + } + + G_OBJECT_CLASS (gsd_automount_manager_parent_class)->finalize (object); +} + +static void +gsd_automount_manager_init (GsdAutomountManager *manager) +{ + manager->priv = GSD_AUTOMOUNT_MANAGER_GET_PRIVATE (manager); + + g_debug ("Starting automounting manager"); + + manager->priv->settings = g_settings_new ("org.gnome.desktop.media-handling"); + setup_automounter (manager); +} + +static void +gsd_automount_manager_class_init (GsdAutomountManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsd_automount_manager_finalize; + + g_type_class_add_private (klass, sizeof (GsdAutomountManagerPrivate)); +} + +GsdAutomountManager * +gsd_automount_manager_new (void) +{ + return GSD_AUTOMOUNT_MANAGER (g_object_new (GSD_TYPE_AUTOMOUNT_MANAGER, NULL)); +} diff --git a/gnome-flashback/libautomount-manager/gsd-automount-manager.h b/gnome-flashback/libautomount-manager/gsd-automount-manager.h new file mode 100644 index 0000000..42f9f58 --- /dev/null +++ b/gnome-flashback/libautomount-manager/gsd-automount-manager.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2010 Red Hat, Inc. + * + * This program 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Tomas Bzatek + */ + +#ifndef __GSD_AUTOMOUNT_MANAGER_H +#define __GSD_AUTOMOUNT_MANAGER_H + +#include + +G_BEGIN_DECLS + +#define GSD_TYPE_AUTOMOUNT_MANAGER (gsd_automount_manager_get_type ()) +#define GSD_AUTOMOUNT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManager)) +#define GSD_AUTOMOUNT_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManagerClass)) +#define GSD_IS_AUTOMOUNT_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GSD_TYPE_AUTOMOUNT_MANAGER)) +#define GSD_IS_AUTOMOUNT_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSD_TYPE_AUTOMOUNT_MANAGER)) +#define GSD_AUTOMOUNT_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GSD_TYPE_AUTOMOUNT_MANAGER, GsdAutomountManagerClass)) + +typedef struct GsdAutomountManagerPrivate GsdAutomountManagerPrivate; + +typedef struct +{ + GObject parent; + GsdAutomountManagerPrivate *priv; +} GsdAutomountManager; + +typedef struct +{ + GObjectClass parent_class; +} GsdAutomountManagerClass; + +GType gsd_automount_manager_get_type (void); + +GsdAutomountManager * gsd_automount_manager_new (void); +gboolean gsd_automount_manager_start (GsdAutomountManager *manager, + GError **error); +void gsd_automount_manager_stop (GsdAutomountManager *manager); + +G_END_DECLS + +#endif /* __GSD_AUTOMOUNT_MANAGER_H */ diff --git a/gnome-flashback/libautomount-manager/gsd-autorun.c b/gnome-flashback/libautomount-manager/gsd-autorun.c new file mode 100644 index 0000000..506d7f6 --- /dev/null +++ b/gnome-flashback/libautomount-manager/gsd-autorun.c @@ -0,0 +1,975 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ + +/* + * gsd-automount.c: helpers for automounting hotplugged volumes + * + * Copyright (C) 2008, 2010 Red Hat, Inc. + * + * Nautilus 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: David Zeuthen + * Cosimo Cecchi + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "gsd-autorun.h" + +static gboolean should_autorun_mount (GMount *mount); + +#define CUSTOM_ITEM_ASK "gsd-item-ask" +#define CUSTOM_ITEM_DO_NOTHING "gsd-item-do-nothing" +#define CUSTOM_ITEM_OPEN_FOLDER "gsd-item-open-folder" + +typedef struct +{ + GtkWidget *dialog; + + GMount *mount; + gboolean should_eject; + + gboolean selected_ignore; + gboolean selected_open_folder; + GAppInfo *selected_app; + + gboolean remember; + + char *x_content_type; + + GsdAutorunOpenWindow open_window_func; + gpointer user_data; +} AutorunDialogData; + +static int +gsd_autorun_g_strv_find (char **strv, const char *find_me) +{ + guint index; + + g_return_val_if_fail (find_me != NULL, -1); + + for (index = 0; strv[index] != NULL; ++index) { + if (strcmp (strv[index], find_me) == 0) { + return index; + } + } + + return -1; +} + + +#define ICON_SIZE_STANDARD 48 + +static gint +get_icon_size_for_stock_size (GtkIconSize size) +{ + gint w, h; + + if (gtk_icon_size_lookup (size, &w, &h)) { + return MAX (w, h); + } + return ICON_SIZE_STANDARD; +} + +static GdkPixbuf * +render_icon (GIcon *icon, gint icon_size) +{ + GdkPixbuf *pixbuf; + GtkIconInfo *info; + + pixbuf = NULL; + + if (G_IS_THEMED_ICON (icon)) { + gchar const * const *names; + + info = gtk_icon_theme_lookup_by_gicon (gtk_icon_theme_get_default (), + icon, + icon_size, + 0); + + if (info) { + pixbuf = gtk_icon_info_load_icon (info, NULL); + gtk_icon_info_free (info); + } + + if (pixbuf == NULL) { + names = g_themed_icon_get_names (G_THEMED_ICON (icon)); + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + *names, + icon_size, + 0, NULL); + } + } + else + if (G_IS_FILE_ICON (icon)) { + GFile *icon_file; + gchar *path; + + icon_file = g_file_icon_get_file (G_FILE_ICON (icon)); + path = g_file_get_path (icon_file); + pixbuf = gdk_pixbuf_new_from_file_at_size (path, + icon_size, icon_size, + NULL); + g_free (path); + g_object_unref (G_OBJECT (icon_file)); + } + + return pixbuf; +} + +static void +gsd_autorun_get_preferences (const char *x_content_type, + gboolean *pref_start_app, + gboolean *pref_ignore, + gboolean *pref_open_folder) +{ + GSettings *settings; + char **x_content_start_app; + char **x_content_ignore; + char **x_content_open_folder; + + g_return_if_fail (pref_start_app != NULL); + g_return_if_fail (pref_ignore != NULL); + g_return_if_fail (pref_open_folder != NULL); + + settings = g_settings_new ("org.gnome.desktop.media-handling"); + + *pref_start_app = FALSE; + *pref_ignore = FALSE; + *pref_open_folder = FALSE; + x_content_start_app = g_settings_get_strv (settings, "autorun-x-content-start-app"); + x_content_ignore = g_settings_get_strv (settings, "autorun-x-content-ignore"); + x_content_open_folder = g_settings_get_strv (settings, "autorun-x-content-open-folder"); + if (x_content_start_app != NULL) { + *pref_start_app = gsd_autorun_g_strv_find (x_content_start_app, x_content_type) != -1; + } + if (x_content_ignore != NULL) { + *pref_ignore = gsd_autorun_g_strv_find (x_content_ignore, x_content_type) != -1; + } + if (x_content_open_folder != NULL) { + *pref_open_folder = gsd_autorun_g_strv_find (x_content_open_folder, x_content_type) != -1; + } + g_strfreev (x_content_ignore); + g_strfreev (x_content_start_app); + g_strfreev (x_content_open_folder); + g_object_unref (settings); +} + +static char ** +remove_elem_from_str_array (char **v, + const char *s) +{ + GPtrArray *array; + guint idx; + + array = g_ptr_array_new (); + + for (idx = 0; v[idx] != NULL; idx++) { + if (g_strcmp0 (v[idx], s) == 0) { + continue; + } + + g_ptr_array_add (array, v[idx]); + } + + g_ptr_array_add (array, NULL); + + g_free (v); + + return (char **) g_ptr_array_free (array, FALSE); +} + +static char ** +add_elem_to_str_array (char **v, + const char *s) +{ + GPtrArray *array; + guint idx; + + array = g_ptr_array_new (); + + for (idx = 0; v[idx] != NULL; idx++) { + g_ptr_array_add (array, v[idx]); + } + + g_ptr_array_add (array, g_strdup (s)); + g_ptr_array_add (array, NULL); + + g_free (v); + + return (char **) g_ptr_array_free (array, FALSE); +} + +static void +gsd_autorun_set_preferences (const char *x_content_type, + gboolean pref_start_app, + gboolean pref_ignore, + gboolean pref_open_folder) +{ + GSettings *settings; + char **x_content_start_app; + char **x_content_ignore; + char **x_content_open_folder; + + g_assert (x_content_type != NULL); + + settings = g_settings_new ("org.gnome.desktop.media-handling"); + + x_content_start_app = g_settings_get_strv (settings, "autorun-x-content-start-app"); + x_content_ignore = g_settings_get_strv (settings, "autorun-x-content-ignore"); + x_content_open_folder = g_settings_get_strv (settings, "autorun-x-content-open-folder"); + + x_content_start_app = remove_elem_from_str_array (x_content_start_app, x_content_type); + if (pref_start_app) { + x_content_start_app = add_elem_to_str_array (x_content_start_app, x_content_type); + } + g_settings_set_strv (settings, "autorun-x-content-start-app", (const gchar * const*) x_content_start_app); + + x_content_ignore = remove_elem_from_str_array (x_content_ignore, x_content_type); + if (pref_ignore) { + x_content_ignore = add_elem_to_str_array (x_content_ignore, x_content_type); + } + g_settings_set_strv (settings, "autorun-x-content-ignore", (const gchar * const*) x_content_ignore); + + x_content_open_folder = remove_elem_from_str_array (x_content_open_folder, x_content_type); + if (pref_open_folder) { + x_content_open_folder = add_elem_to_str_array (x_content_open_folder, x_content_type); + } + g_settings_set_strv (settings, "autorun-x-content-open-folder", (const gchar * const*) x_content_open_folder); + + g_strfreev (x_content_open_folder); + g_strfreev (x_content_ignore); + g_strfreev (x_content_start_app); + g_object_unref (settings); +} + +static void +custom_item_activated_cb (GtkAppChooserButton *button, + const gchar *item, + gpointer user_data) +{ + gchar *content_type; + AutorunDialogData *data = user_data; + + content_type = gtk_app_chooser_get_content_type (GTK_APP_CHOOSER (button)); + + if (g_strcmp0 (item, CUSTOM_ITEM_ASK) == 0) { + gsd_autorun_set_preferences (content_type, + FALSE, FALSE, FALSE); + data->selected_open_folder = FALSE; + data->selected_ignore = FALSE; + } else if (g_strcmp0 (item, CUSTOM_ITEM_OPEN_FOLDER) == 0) { + gsd_autorun_set_preferences (content_type, + FALSE, FALSE, TRUE); + data->selected_open_folder = TRUE; + data->selected_ignore = FALSE; + } else if (g_strcmp0 (item, CUSTOM_ITEM_DO_NOTHING) == 0) { + gsd_autorun_set_preferences (content_type, + FALSE, TRUE, FALSE); + data->selected_open_folder = FALSE; + data->selected_ignore = TRUE; + } + + g_free (content_type); +} + +static void +combo_box_changed_cb (GtkComboBox *combo_box, + gpointer user_data) +{ + GAppInfo *info; + AutorunDialogData *data = user_data; + + info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (combo_box)); + + if (info == NULL) + return; + + g_clear_object (&data->selected_app); + data->selected_app = info; +} + +static void +prepare_combo_box (GtkWidget *combo_box, + AutorunDialogData *data) +{ + GtkAppChooserButton *app_chooser = GTK_APP_CHOOSER_BUTTON (combo_box); + GIcon *icon; + gboolean pref_ask; + gboolean pref_start_app; + gboolean pref_ignore; + gboolean pref_open_folder; + GAppInfo *info; + gchar *content_type; + + content_type = gtk_app_chooser_get_content_type (GTK_APP_CHOOSER (app_chooser)); + + /* fetch preferences for this content type */ + gsd_autorun_get_preferences (content_type, + &pref_start_app, &pref_ignore, &pref_open_folder); + pref_ask = !pref_start_app && !pref_ignore && !pref_open_folder; + + info = gtk_app_chooser_get_app_info (GTK_APP_CHOOSER (combo_box)); + + /* append the separator only if we have >= 1 apps in the chooser */ + if (info != NULL) { + gtk_app_chooser_button_append_separator (app_chooser); + g_object_unref (info); + } + + icon = g_themed_icon_new (GTK_STOCK_DIALOG_QUESTION); + gtk_app_chooser_button_append_custom_item (app_chooser, CUSTOM_ITEM_ASK, + _("Ask what to do"), + icon); + g_object_unref (icon); + + icon = g_themed_icon_new (GTK_STOCK_CLOSE); + gtk_app_chooser_button_append_custom_item (app_chooser, CUSTOM_ITEM_DO_NOTHING, + _("Do Nothing"), + icon); + g_object_unref (icon); + + icon = g_themed_icon_new ("folder-open"); + gtk_app_chooser_button_append_custom_item (app_chooser, CUSTOM_ITEM_OPEN_FOLDER, + _("Open Folder"), + icon); + g_object_unref (icon); + + gtk_app_chooser_button_set_show_dialog_item (app_chooser, TRUE); + + if (pref_ask) { + gtk_app_chooser_button_set_active_custom_item (app_chooser, CUSTOM_ITEM_ASK); + } else if (pref_ignore) { + gtk_app_chooser_button_set_active_custom_item (app_chooser, CUSTOM_ITEM_DO_NOTHING); + } else if (pref_open_folder) { + gtk_app_chooser_button_set_active_custom_item (app_chooser, CUSTOM_ITEM_OPEN_FOLDER); + } + + g_signal_connect (app_chooser, "changed", + G_CALLBACK (combo_box_changed_cb), data); + g_signal_connect (app_chooser, "custom-item-activated", + G_CALLBACK (custom_item_activated_cb), data); + + g_free (content_type); +} + +static gboolean +is_shift_pressed (void) +{ + gboolean ret; + XkbStateRec state; + Bool status; + + ret = FALSE; + + gdk_error_trap_push (); + status = XkbGetState (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), + XkbUseCoreKbd, &state); + gdk_error_trap_pop_ignored (); + + if (status == Success) { + ret = state.mods & ShiftMask; + } + + return ret; +} + +enum { + AUTORUN_DIALOG_RESPONSE_EJECT = 0 +}; + +static void +gsd_autorun_launch_for_mount (GMount *mount, GAppInfo *app_info) +{ + GFile *root; + GdkAppLaunchContext *launch_context; + GError *error; + gboolean result; + GList *list; + gchar *uri_scheme; + gchar *uri; + + root = g_mount_get_root (mount); + list = g_list_append (NULL, root); + + launch_context = gdk_app_launch_context_new (); + + error = NULL; + result = g_app_info_launch (app_info, + list, + G_APP_LAUNCH_CONTEXT (launch_context), + &error); + + g_object_unref (launch_context); + + if (!result) { + if (error->domain == G_IO_ERROR && + error->code == G_IO_ERROR_NOT_SUPPORTED) { + uri = g_file_get_uri (root); + uri_scheme = g_uri_parse_scheme (uri); + + /* FIXME: Present user a dialog to choose another app when the last one failed to handle a file */ + g_warning ("Cannot open location: %s\n", error->message); + + g_free (uri_scheme); + g_free (uri); + } else { + g_warning ("Cannot open app: %s\n", error->message); + } + g_error_free (error); + } + + g_list_free (list); + g_object_unref (root); +} + +static void autorun_dialog_mount_unmounted (GMount *mount, AutorunDialogData *data); + +static void +autorun_dialog_destroy (AutorunDialogData *data) +{ + g_signal_handlers_disconnect_by_func (G_OBJECT (data->mount), + G_CALLBACK (autorun_dialog_mount_unmounted), + data); + + gtk_widget_destroy (GTK_WIDGET (data->dialog)); + if (data->selected_app != NULL) { + g_object_unref (data->selected_app); + } + g_object_unref (data->mount); + g_free (data->x_content_type); + g_free (data); +} + +static void +autorun_dialog_mount_unmounted (GMount *mount, AutorunDialogData *data) +{ + /* remove the dialog if the media is unmounted */ + autorun_dialog_destroy (data); +} + +static void +unmount_mount_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char *primary; + gboolean unmounted; + gboolean should_eject; + GtkWidget *dialog; + + + should_eject = user_data != NULL; + + error = NULL; + if (should_eject) { + unmounted = g_mount_eject_with_operation_finish (G_MOUNT (source_object), + res, &error); + } else { + unmounted = g_mount_unmount_with_operation_finish (G_MOUNT (source_object), + res, &error); + } + + if (! unmounted) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + if (should_eject) { + primary = g_strdup_printf (_("Unable to eject %p"), source_object); + } else { + primary = g_strdup_printf (_("Unable to unmount %p"), source_object); + } + + dialog = gtk_message_dialog_new (NULL, + 0, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "%s", + primary); + gtk_message_dialog_format_secondary_markup (GTK_MESSAGE_DIALOG (dialog), + "%s", + error->message); + + gtk_widget_show (GTK_WIDGET (dialog)); + g_signal_connect (dialog, "response", + G_CALLBACK (gtk_widget_destroy), NULL); + g_free (primary); + } + } + + if (error != NULL) { + g_error_free (error); + } +} + +static void +do_unmount (GMount *mount, gboolean should_eject, GtkWindow *window) +{ + GMountOperation *mount_op; + + mount_op = gtk_mount_operation_new (window); + if (should_eject) { + g_mount_eject_with_operation (mount, + 0, + mount_op, + NULL, + unmount_mount_callback, + (gpointer) 1); + } else { + g_mount_unmount_with_operation (mount, + 0, + mount_op, + NULL, + unmount_mount_callback, + (gpointer) 0); + } + g_object_unref (mount_op); +} + +static void +autorun_dialog_response (GtkDialog *dialog, gint response, AutorunDialogData *data) +{ + switch (response) { + case AUTORUN_DIALOG_RESPONSE_EJECT: + do_unmount (data->mount, data->should_eject, GTK_WINDOW (dialog)); + break; + + case GTK_RESPONSE_NONE: + /* window was closed */ + break; + case GTK_RESPONSE_CANCEL: + break; + case GTK_RESPONSE_OK: + /* do the selected action */ + + if (data->remember) { + /* make sure we don't ask again */ + gsd_autorun_set_preferences (data->x_content_type, TRUE, data->selected_ignore, data->selected_open_folder); + if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL) { + g_app_info_set_as_default_for_type (data->selected_app, + data->x_content_type, + NULL); + } + } else { + /* make sure we do ask again */ + gsd_autorun_set_preferences (data->x_content_type, FALSE, FALSE, FALSE); + } + + if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL) { + gsd_autorun_launch_for_mount (data->mount, data->selected_app); + } else if (!data->selected_ignore && data->selected_open_folder) { + if (data->open_window_func != NULL) + data->open_window_func (data->mount, data->user_data); + } + break; + } + + autorun_dialog_destroy (data); +} + +static void +autorun_always_toggled (GtkToggleButton *togglebutton, AutorunDialogData *data) +{ + data->remember = gtk_toggle_button_get_active (togglebutton); +} + +static gboolean +combo_box_enter_ok (GtkWidget *togglebutton, GdkEventKey *event, GtkDialog *dialog) +{ + if (event->keyval == GDK_KEY_KP_Enter || event->keyval == GDK_KEY_Return) { + gtk_dialog_response (dialog, GTK_RESPONSE_OK); + return TRUE; + } + return FALSE; +} + +/* returns TRUE if a folder window should be opened */ +static gboolean +do_autorun_for_content_type (GMount *mount, + const char *x_content_type, + GsdAutorunOpenWindow open_window_func, + gpointer user_data) +{ + AutorunDialogData *data; + GtkWidget *dialog; + GtkWidget *hbox; + GtkWidget *vbox; + GtkWidget *label; + GtkWidget *combo_box; + GtkWidget *always_check_button; + GtkWidget *eject_button; + GtkWidget *image; + char *markup; + char *content_description; + char *mount_name; + GIcon *icon; + GdkPixbuf *pixbuf; + int icon_size; + gboolean user_forced_dialog; + gboolean pref_ask; + gboolean pref_start_app; + gboolean pref_ignore; + gboolean pref_open_folder; + char *media_greeting; + gboolean ret; + + ret = FALSE; + mount_name = NULL; + + if (g_content_type_is_a (x_content_type, "x-content/win32-software")) { + /* don't pop up the dialog anyway if the content type says + * windows software. + */ + goto out; + } + + user_forced_dialog = is_shift_pressed (); + + gsd_autorun_get_preferences (x_content_type, &pref_start_app, &pref_ignore, &pref_open_folder); + pref_ask = !pref_start_app && !pref_ignore && !pref_open_folder; + + if (user_forced_dialog) { + goto show_dialog; + } + + if (!pref_ask && !pref_ignore && !pref_open_folder) { + GAppInfo *app_info; + app_info = g_app_info_get_default_for_type (x_content_type, FALSE); + if (app_info != NULL) { + gsd_autorun_launch_for_mount (mount, app_info); + } + goto out; + } + + if (pref_open_folder) { + ret = TRUE; + goto out; + } + + if (pref_ignore) { + goto out; + } + +show_dialog: + + mount_name = g_mount_get_name (mount); + + dialog = gtk_dialog_new (); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 12); + + icon = g_mount_get_icon (mount); + icon_size = get_icon_size_for_stock_size (GTK_ICON_SIZE_DIALOG); + image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_DIALOG); + pixbuf = render_icon (icon, icon_size); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0); + /* also use the icon on the dialog */ + gtk_window_set_title (GTK_WINDOW (dialog), mount_name); + gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); + g_object_unref (icon); + if (pixbuf) { + g_object_unref (pixbuf); + } + vbox = gtk_vbox_new (FALSE, 12); + gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0); + + label = gtk_label_new (NULL); + + + /* Customize greeting for well-known x-content types */ + if (strcmp (x_content_type, "x-content/audio-cdda") == 0) { + media_greeting = _("You have just inserted an Audio CD."); + } else if (strcmp (x_content_type, "x-content/audio-dvd") == 0) { + media_greeting = _("You have just inserted an Audio DVD."); + } else if (strcmp (x_content_type, "x-content/video-dvd") == 0) { + media_greeting = _("You have just inserted a Video DVD."); + } else if (strcmp (x_content_type, "x-content/video-vcd") == 0) { + media_greeting = _("You have just inserted a Video CD."); + } else if (strcmp (x_content_type, "x-content/video-svcd") == 0) { + media_greeting = _("You have just inserted a Super Video CD."); + } else if (strcmp (x_content_type, "x-content/blank-cd") == 0) { + media_greeting = _("You have just inserted a blank CD."); + } else if (strcmp (x_content_type, "x-content/blank-dvd") == 0) { + media_greeting = _("You have just inserted a blank DVD."); + } else if (strcmp (x_content_type, "x-content/blank-cd") == 0) { + media_greeting = _("You have just inserted a blank Blu-Ray disc."); + } else if (strcmp (x_content_type, "x-content/blank-cd") == 0) { + media_greeting = _("You have just inserted a blank HD DVD."); + } else if (strcmp (x_content_type, "x-content/image-photocd") == 0) { + media_greeting = _("You have just inserted a Photo CD."); + } else if (strcmp (x_content_type, "x-content/image-picturecd") == 0) { + media_greeting = _("You have just inserted a Picture CD."); + } else if (strcmp (x_content_type, "x-content/image-dcf") == 0) { + media_greeting = _("You have just inserted a medium with digital photos."); + } else if (strcmp (x_content_type, "x-content/audio-player") == 0) { + media_greeting = _("You have just inserted a digital audio player."); + } else if (g_content_type_is_a (x_content_type, "x-content/software")) { + media_greeting = _("You have just inserted a medium with software intended to be automatically started."); + } else { + /* fallback to generic greeting */ + media_greeting = _("You have just inserted a medium."); + } + markup = g_strdup_printf ("%s %s", media_greeting, _("Choose what application to launch.")); + gtk_label_set_markup (GTK_LABEL (label), markup); + g_free (markup); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + + label = gtk_label_new (NULL); + content_description = g_content_type_get_description (x_content_type); + markup = g_strdup_printf (_("Select how to open \"%s\" and whether to perform this action in the future for other media of type \"%s\"."), mount_name, content_description); + g_free (content_description); + gtk_label_set_markup (GTK_LABEL (label), markup); + g_free (markup); + gtk_label_set_line_wrap (GTK_LABEL (label), TRUE); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0); + + data = g_new0 (AutorunDialogData, 1); + data->dialog = dialog; + data->mount = g_object_ref (mount); + data->remember = !pref_ask; + data->selected_ignore = pref_ignore; + data->x_content_type = g_strdup (x_content_type); + data->selected_app = g_app_info_get_default_for_type (x_content_type, FALSE); + data->open_window_func = open_window_func; + data->user_data = user_data; + + combo_box = gtk_app_chooser_button_new (x_content_type); + prepare_combo_box (combo_box, data); + g_signal_connect (G_OBJECT (combo_box), + "key-press-event", + G_CALLBACK (combo_box_enter_ok), + dialog); + + gtk_box_pack_start (GTK_BOX (vbox), combo_box, TRUE, TRUE, 0); + + always_check_button = gtk_check_button_new_with_mnemonic (_("_Always perform this action")); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (always_check_button), data->remember); + g_signal_connect (G_OBJECT (always_check_button), + "toggled", + G_CALLBACK (autorun_always_toggled), + data); + gtk_box_pack_start (GTK_BOX (vbox), always_check_button, TRUE, TRUE, 0); + + gtk_dialog_add_buttons (GTK_DIALOG (dialog), + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK); + + if (g_mount_can_eject (mount)) { + GtkWidget *eject_image; + eject_button = gtk_button_new_with_mnemonic (_("_Eject")); + eject_image = gtk_image_new_from_icon_name ("media-eject", GTK_ICON_SIZE_BUTTON); + gtk_button_set_image (GTK_BUTTON (eject_button), eject_image); + data->should_eject = TRUE; + } else { + eject_button = gtk_button_new_with_mnemonic (_("_Unmount")); + data->should_eject = FALSE; + } + gtk_dialog_add_action_widget (GTK_DIALOG (dialog), eject_button, AUTORUN_DIALOG_RESPONSE_EJECT); + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (gtk_dialog_get_action_area (GTK_DIALOG (dialog))), eject_button, TRUE); + + /* show the dialog */ + gtk_widget_show_all (dialog); + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (autorun_dialog_response), + data); + + g_signal_connect (G_OBJECT (data->mount), + "unmounted", + G_CALLBACK (autorun_dialog_mount_unmounted), + data); + +out: + g_free (mount_name); + return ret; +} + +typedef struct { + GMount *mount; + GsdAutorunOpenWindow open_window_func; + gpointer user_data; + GSettings *settings; +} AutorunData; + +static void +autorun_guessed_content_type_callback (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char **guessed_content_type; + AutorunData *data = user_data; + gboolean open_folder; + + open_folder = FALSE; + + error = NULL; + guessed_content_type = g_mount_guess_content_type_finish (G_MOUNT (source_object), res, &error); + g_object_set_data_full (source_object, + "gsd-content-type-cache", + g_strdupv (guessed_content_type), + (GDestroyNotify)g_strfreev); + if (error != NULL) { + g_warning ("Unable to guess content type for mount: %s", error->message); + g_error_free (error); + } else { + if (guessed_content_type != NULL && g_strv_length (guessed_content_type) > 0) { + int n; + for (n = 0; guessed_content_type[n] != NULL; n++) { + if (do_autorun_for_content_type (data->mount, guessed_content_type[n], + data->open_window_func, data->user_data)) { + open_folder = TRUE; + } + } + g_strfreev (guessed_content_type); + } else { + if (g_settings_get_boolean (data->settings, "automount-open")) { + open_folder = TRUE; + } + } + } + + /* only open the folder once.. */ + if (open_folder && data->open_window_func != NULL) { + data->open_window_func (data->mount, data->user_data); + } + + g_object_unref (data->mount); + g_object_unref (data->settings); + g_free (data); +} + +void +gsd_autorun (GMount *mount, + GSettings *settings, + GsdAutorunOpenWindow open_window_func, + gpointer user_data) +{ + AutorunData *data; + + if (!should_autorun_mount (mount) || + g_settings_get_boolean (settings, "autorun-never")) { + return; + } + + data = g_new0 (AutorunData, 1); + data->mount = g_object_ref (mount); + data->open_window_func = open_window_func; + data->user_data = user_data; + data->settings = g_object_ref (settings); + + g_mount_guess_content_type (mount, + FALSE, + NULL, + autorun_guessed_content_type_callback, + data); +} + +static gboolean +remove_allow_volume (gpointer data) +{ + GVolume *volume = data; + + g_object_set_data (G_OBJECT (volume), "gsd-allow-autorun", NULL); + return FALSE; +} + +void +gsd_allow_autorun_for_volume (GVolume *volume) +{ + g_object_set_data (G_OBJECT (volume), "gsd-allow-autorun", GINT_TO_POINTER (1)); +} + +#define INHIBIT_AUTORUN_SECONDS 10 + +void +gsd_allow_autorun_for_volume_finish (GVolume *volume) +{ + if (g_object_get_data (G_OBJECT (volume), "gsd-allow-autorun") != NULL) { + g_timeout_add_seconds_full (0, + INHIBIT_AUTORUN_SECONDS, + remove_allow_volume, + g_object_ref (volume), + g_object_unref); + } +} + +static gboolean +should_skip_native_mount_root (GFile *root) +{ + char *path; + gboolean should_skip; + + /* skip any mounts in hidden directory hierarchies */ + path = g_file_get_path (root); + should_skip = strstr (path, "/.") != NULL; + g_free (path); + + return should_skip; +} + +static gboolean +should_autorun_mount (GMount *mount) +{ + GFile *root; + GVolume *enclosing_volume; + gboolean ignore_autorun; + + ignore_autorun = TRUE; + enclosing_volume = g_mount_get_volume (mount); + if (enclosing_volume != NULL) { + if (g_object_get_data (G_OBJECT (enclosing_volume), "gsd-allow-autorun") != NULL) { + ignore_autorun = FALSE; + g_object_set_data (G_OBJECT (enclosing_volume), "gsd-allow-autorun", NULL); + } + } + + if (ignore_autorun) { + if (enclosing_volume != NULL) { + g_object_unref (enclosing_volume); + } + return FALSE; + } + + root = g_mount_get_root (mount); + + /* only do autorun on local files or files where g_volume_should_automount() returns TRUE */ + ignore_autorun = TRUE; + if ((g_file_is_native (root) && !should_skip_native_mount_root (root)) || + (enclosing_volume != NULL && g_volume_should_automount (enclosing_volume))) { + ignore_autorun = FALSE; + } + if (enclosing_volume != NULL) { + g_object_unref (enclosing_volume); + } + g_object_unref (root); + + return !ignore_autorun; +} diff --git a/gnome-flashback/libautomount-manager/gsd-autorun.h b/gnome-flashback/libautomount-manager/gsd-autorun.h new file mode 100644 index 0000000..d210bf4 --- /dev/null +++ b/gnome-flashback/libautomount-manager/gsd-autorun.h @@ -0,0 +1,53 @@ +/* + * gsd-automount.h:helpers for automounting hotplugged volumes + * + * Copyright (C) 2008 Red Hat, Inc. + * + * Nautilus 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: David Zeuthen + * Cosimo Cecchi + */ + +/* TODO: + * + * - unmount all the media we've automounted on shutdown + * - finish x-content / * types + * - finalize the semi-spec + * - add probing/sniffing code + * - implement missing features + * - "Open Folder when mounted" + * - Autorun spec (e.g. $ROOT/.autostart) + * + */ + +#ifndef __GSD_AUTORUN_H__ +#define __GSD_AUTORUN_H__ + +#include +#include + +typedef void (*GsdAutorunOpenWindow) (GMount *mount, + gpointer user_data); + +void gsd_autorun (GMount *mount, + GSettings *settings, + GsdAutorunOpenWindow open_window_func, + gpointer user_data); + +void gsd_allow_autorun_for_volume (GVolume *volume); +void gsd_allow_autorun_for_volume_finish (GVolume *volume); + +#endif /* __GSD_AUTORUN_H__ */ diff --git a/po/POTFILES.in b/po/POTFILES.in index c89a15b..0dcf7c7 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,8 @@ data/gnome-flashback-metacity.session.in data/org.gnome.gnome-flashback.gschema.xml.in.in gnome-flashback/libend-session-dialog/flashback-inhibit-dialog.c [type: gettext/glade]gnome-flashback/libend-session-dialog/flashback-inhibit-dialog.ui +gnome-flashback/libautomount-manager/gsd-automount-manager.c +gnome-flashback/libautomount-manager/gsd-autorun.c gnome-flashback/libsound-applet/gvc-applet.c gnome-flashback/libsound-applet/gvc-channel-bar.c gnome-flashback/libsound-applet/gvc-stream-status-icon.c -- 2.1.2