diff options
24 files changed, 2202 insertions, 709 deletions
@@ -1,3 +1,29 @@ +2008-01-17 Alexander Larsson <alexl@redhat.com> + + * libnautilus-private/nautilus-file.[ch]: + * libnautilus-private/nautilus-file-attributes.h: + * libnautilus-private/nautilus-file-private.h: + Add GMount attribute to NautilusFile. + (not implemented yet) + + * Makefile.am: + * configure.in: + * data/nautilus.xml.in: + * libnautilus-private/apps_nautilus_preferences.schemas.in: + * libnautilus-private/nautilus-autorun.[ch]: + * libnautilus-private/nautilus-global-preferences.c: + * libnautilus-private/nautilus-global-preferences.h: + * libnautilus-private/nautilus-program-choosing.c: + * src/Makefile.am: + * src/file-manager/fm-directory-view.c: + * src/nautilus-application.c: + * src/nautilus-autorun-software.c: Added. + * src/nautilus-file-management-properties.c: + * src/nautilus-file-management-properties.glade: + * src/nautilus-window-manage-views.c: + * src/nautilus-x-content-bar.[ch]: Added. + More work on autorun and x-content types from David Zeuthen + 2008-01-16 Alexander Larsson <alexl@redhat.com> * src/nautilus-application.c: diff --git a/Makefile.am b/Makefile.am index 79416d461..7e134d916 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,7 +9,8 @@ desktop_in_files = \ nautilus-home.desktop.in \ nautilus-computer.desktop.in \ nautilus-folder-handler.desktop.in \ - nautilus-file-management-properties.desktop.in + nautilus-file-management-properties.desktop.in \ + nautilus-autorun-software.desktop.in SUBDIRS = \ libnautilus-extension \ diff --git a/configure.in b/configure.in index ec9d3d039..a1695817c 100644 --- a/configure.in +++ b/configure.in @@ -484,6 +484,7 @@ nautilus-file-management-properties.desktop.in nautilus-home.desktop.in nautilus.desktop.in nautilus-folder-handler.desktop.in +nautilus-autorun-software.desktop.in po/Makefile.in src/Makefile src/file-manager/Makefile diff --git a/data/nautilus.xml.in b/data/nautilus.xml.in index 5d6a312d7..0a32b2a06 100644 --- a/data/nautilus.xml.in +++ b/data/nautilus.xml.in @@ -13,11 +13,11 @@ <mime-type type="x-content/video-vcd"> <!-- http://en.wikipedia.org/wiki/Video_CD --> - <_comment>VCD Video</_comment> + <_comment>Video CD</_comment> </mime-type> <mime-type type="x-content/video-svcd"> <!-- http://en.wikipedia.org/wiki/Super_Video_CD --> - <_comment>SVCD Video</_comment> + <_comment>Super Video CD</_comment> </mime-type> <mime-type type="x-content/video-dvd"> <!-- http://en.wikipedia.org/wiki/DVD-Video --> @@ -34,26 +34,57 @@ <_comment>Compact Disc Audio</_comment> </mime-type> - <mime-type type="x-content/blank-media"> + <mime-type type="x-content/blank-cd"> <!-- http://en.wikipedia.org/wiki/Compact_Disc --> + <_comment>Blank CD Disc</_comment> + </mime-type> + + <mime-type type="x-content/blank-dvd"> <!-- http://en.wikipedia.org/wiki/DVD --> - <_comment>Blank Media</_comment> + <_comment>Blank DVD Disc</_comment> </mime-type> - <mime-type type="x-content/software-autostart"> - <!-- http://www.freedesktop.org/wiki/Specifications/autostart-spec --> - <_comment>Software</_comment> + <mime-type type="x-content/blank-bd"> + <!-- http://en.wikipedia.org/wiki/Blu-ray_Disc --> + <_comment>Blank Blu-Ray Disc</_comment> </mime-type> - <!-- TODO: + <mime-type type="x-content/blank-hddvd"> + <!-- http://en.wikipedia.org/wiki/HD_DVD --> + <_comment>Blank HD DVD Disc</_comment> + </mime-type> + + <mime-type type="x-content/audio-dvd"> + <!-- http://en.wikipedia.org/wiki/DVD-Audio --> + <_comment>DVD Audio</_comment> + </mime-type> + + <mime-type type="x-content/video-bluray"> + <!-- http://en.wikipedia.org/wiki/Blu-ray_Disc --> + <_comment>Blu-Ray Video</_comment> + </mime-type> - - Blu-Ray video : http://en.wikipedia.org/wiki/Blu-ray_Disc - - HD-DVD video : http://en.wikipedia.org/wiki/HD_DVD - - DVD-Audio : http://en.wikipedia.org/wiki/DVD-Audio + <mime-type type="x-content/video-hddvd"> + <!-- http://en.wikipedia.org/wiki/HD_DVD --> + <_comment>HD DVD Video</_comment> + </mime-type> - - Photo CD : http://en.wikipedia.org/wiki/Photo_CD - - Picture CD : http://en.wikipedia.org/wiki/Picture_CD + <mime-type type="x-content/image-picturecd"> + <!-- http://en.wikipedia.org/wiki/Picture_CD --> + <_comment>Picture CD</_comment> + </mime-type> + <mime-type type="x-content/audio-player"> + <!-- see fd.o hal spec --> + <_comment>Portable Audio Player</_comment> + </mime-type> + + <mime-type type="x-content/software"> + <!-- http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html --> + <_comment>Software</_comment> + </mime-type> + + <!-- TODO: - SACD : http://en.wikipedia.org/wiki/Super_Audio_CD - This might be dead and appear to be totally unsupported anyway @@ -62,8 +93,6 @@ - Enhanced DVD Movie (see link 1. below) - ?? - - music player (x-content/music-player) - - picture files - audio files - video files diff --git a/libnautilus-private/apps_nautilus_preferences.schemas.in b/libnautilus-private/apps_nautilus_preferences.schemas.in index 9d0ca0ce1..3e606106b 100644 --- a/libnautilus-private/apps_nautilus_preferences.schemas.in +++ b/libnautilus-private/apps_nautilus_preferences.schemas.in @@ -102,6 +102,21 @@ </schema> <schema> + <key>/schemas/apps/nautilus/preferences/media_autorun_never_</key> + <applyto>/apps/nautilus/preferences/media_autorun_never</applyto> + <owner>nautilus</owner> + <type>bool</type> + <default>false</default> + <locale name="C"> + <short>Never prompt or autorun/autostart programs when media is inserted</short> + <long> + If set to true, then Nautilus will never prompt nor autorun/autostart + programs when media is inserted. + </long> + </locale> + </schema> + + <schema> <key>/schemas/apps/nautilus/preferences/media_autorun_x_content_ask</key> <applyto>/apps/nautilus/preferences/media_autorun_x_content_ask</applyto> <owner>nautilus</owner> @@ -109,9 +124,9 @@ <list_type>string</list_type> <default>[]</default> <locale name="C"> - <short>list of x-content/* types to ask the user what to do on insertion</short> + <short>List of x-content/* types to ask the user what to do on insertion</short> <long> - list of x-content/* types to ask the user what to do on insertion + List of x-content/* types to ask the user what to do on insertion. </long> </locale> </schema> @@ -124,9 +139,25 @@ <list_type>string</list_type> <default>[]</default> <locale name="C"> - <short>list of x-content/* types to ignore on insertion</short> + <short>List of x-content/* where to prompt the user on insertion</short> + <long> + List of x-content/* types to ask the user what to do on insertion. + </long> + </locale> + </schema> + + <schema> + <key>/schemas/apps/nautilus/preferences/media_autorun_x_content_open_folder</key> + <applyto>/apps/nautilus/preferences/media_autorun_x_content_open_folder</applyto> + <owner>nautilus</owner> + <type>list</type> + <list_type>string</list_type> + <default>[]</default> + <locale name="C"> + <short>List of x-content/* types where a folder window should be opened</short> <long> - list of x-content/* types to ask the user what to do on insertion + List of x-content/* types where a folder window should be opened + on insertion. </long> </locale> </schema> diff --git a/libnautilus-private/nautilus-autorun.c b/libnautilus-private/nautilus-autorun.c index 6ed1a6f42..eaf0bede3 100644 --- a/libnautilus-private/nautilus-autorun.c +++ b/libnautilus-private/nautilus-autorun.c @@ -29,6 +29,7 @@ #include <gtk/gtk.h> #include <gdk/gdkx.h> #include <gio/gdesktopappinfo.h> +#include <X11/XKBlib.h> #include <eel/eel-glib-extensions.h> #include <eel/eel-stock-dialogs.h> @@ -38,12 +39,15 @@ #include "nautilus-file-operations.h" #include "nautilus-autorun.h" #include "nautilus-program-choosing.h" +#include "nautilus-open-with-dialog.h" +#include "nautilus-desktop-icon-file.h" enum { AUTORUN_ASK, AUTORUN_IGNORE, AUTORUN_APP, + AUTORUN_OPEN_FOLDER, AUTORUN_SEP, }; enum @@ -56,24 +60,34 @@ enum }; void -nautilus_autorun_get_preferences (const char *x_content_type, gboolean *pref_ask, gboolean *pref_ignore) +nautilus_autorun_get_preferences (const char *x_content_type, + gboolean *pref_ask, + gboolean *pref_ignore, + gboolean *pref_open_folder) { char **x_content_ask; char **x_content_ignore; + char **x_content_open_folder; g_return_if_fail (pref_ask != NULL); g_return_if_fail (pref_ignore != NULL); + g_return_if_fail (pref_open_folder != NULL); *pref_ask = FALSE; *pref_ignore = FALSE; + *pref_open_folder = FALSE; x_content_ask = eel_preferences_get_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_ASK); x_content_ignore = eel_preferences_get_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE); + x_content_open_folder = eel_preferences_get_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER); if (x_content_ask != NULL) { *pref_ask = eel_g_strv_find (x_content_ask, x_content_type) != -1; } if (x_content_ignore != NULL) { *pref_ignore = eel_g_strv_find (x_content_ignore, x_content_type) != -1; } + if (x_content_open_folder != NULL) { + *pref_open_folder = eel_g_strv_find (x_content_open_folder, x_content_type) != -1; + } g_strfreev (x_content_ignore); g_strfreev (x_content_ask); @@ -112,13 +126,15 @@ add_elem_to_str_array (char **v, const char *s) void -nautilus_autorun_set_preferences (const char *x_content_type, gboolean pref_ask, gboolean pref_ignore) +nautilus_autorun_set_preferences (const char *x_content_type, gboolean pref_ask, gboolean pref_ignore, gboolean pref_open_folder) { char **x_content_ask; char **x_content_ignore; + char **x_content_open_folder; x_content_ask = eel_preferences_get_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_ASK); x_content_ignore = eel_preferences_get_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE); + x_content_open_folder = eel_preferences_get_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER); remove_elem_from_str_array (x_content_ask, x_content_type); if (pref_ask) { @@ -132,6 +148,13 @@ nautilus_autorun_set_preferences (const char *x_content_type, gboolean pref_ask, } eel_preferences_set_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE, x_content_ignore); + 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); + } + eel_preferences_set_string_array (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER, x_content_open_folder); + + g_strfreev (x_content_open_folder); g_strfreev (x_content_ignore); g_strfreev (x_content_ask); @@ -156,12 +179,24 @@ combo_box_separator_func (GtkTreeModel *model, typedef struct { + guint changed_signal_id; + GtkWidget *combo_box; + gboolean update_settings; NautilusAutorunComboBoxChanged changed_cb; gpointer user_data; } NautilusAutorunComboBoxData; static void +nautilus_autorun_combobox_data_destroy (NautilusAutorunComboBoxData *data) +{ + /* signal handler may be automatically disconnected by destroying the widget */ + if (g_signal_handler_is_connected (G_OBJECT (data->combo_box), data->changed_signal_id)) + g_signal_handler_disconnect (G_OBJECT (data->combo_box), data->changed_signal_id); + g_free (data); +} + +static void combo_box_changed (GtkComboBox *combo_box, NautilusAutorunComboBoxData *data) { @@ -193,26 +228,34 @@ combo_box_changed (GtkComboBox *combo_box, switch (type) { case AUTORUN_ASK: if (data->changed_cb != NULL) { - data->changed_cb (TRUE, FALSE, NULL, data->user_data); + data->changed_cb (TRUE, FALSE, FALSE, NULL, data->user_data); } if (data->update_settings) { - nautilus_autorun_set_preferences (x_content_type, TRUE, FALSE); + nautilus_autorun_set_preferences (x_content_type, TRUE, FALSE, FALSE); } break; case AUTORUN_IGNORE: if (data->changed_cb != NULL) { - data->changed_cb (FALSE, TRUE, NULL, data->user_data); + data->changed_cb (FALSE, TRUE, FALSE, NULL, data->user_data); } if (data->update_settings) { - nautilus_autorun_set_preferences (x_content_type, FALSE, TRUE); + nautilus_autorun_set_preferences (x_content_type, FALSE, TRUE, FALSE); + } + break; + case AUTORUN_OPEN_FOLDER: + if (data->changed_cb != NULL) { + data->changed_cb (FALSE, FALSE, TRUE, NULL, data->user_data); + } + if (data->update_settings) { + nautilus_autorun_set_preferences (x_content_type, FALSE, FALSE, TRUE); } break; case AUTORUN_APP: if (data->changed_cb != NULL) { - data->changed_cb (FALSE, FALSE, app_info, data->user_data); + data->changed_cb (FALSE, FALSE, FALSE, app_info, data->user_data); } if (data->update_settings) { - nautilus_autorun_set_preferences (x_content_type, FALSE, FALSE); + nautilus_autorun_set_preferences (x_content_type, FALSE, FALSE, FALSE); g_app_info_set_as_default_for_type (app_info, x_content_type, NULL); @@ -230,7 +273,6 @@ out: g_free (x_content_type); } - void nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, const char *x_content_type, @@ -251,14 +293,16 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, int num_apps; gboolean pref_ask; gboolean pref_ignore; + gboolean pref_open_folder; NautilusAutorunComboBoxData *data; GtkCellRenderer *renderer; - nautilus_autorun_get_preferences (x_content_type, &pref_ask, &pref_ignore); + nautilus_autorun_get_preferences (x_content_type, &pref_ask, &pref_ignore, &pref_open_folder); icon_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU); set_active = -1; + data = NULL; app_info_list = g_app_info_get_all_for_type (x_content_type); default_app_info = g_app_info_get_default_for_type (x_content_type, FALSE); @@ -310,7 +354,7 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, gtk_list_store_append (list_store, &iter); pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), - GTK_STOCK_CANCEL, + GTK_STOCK_CLOSE, icon_size, 0, NULL); @@ -321,8 +365,23 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type, COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_IGNORE, -1); - g_object_unref (pixbuf); - + g_object_unref (pixbuf); + + gtk_list_store_append (list_store, &iter); + pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), + "nautilus", + icon_size, + 0, + NULL); + gtk_list_store_set (list_store, &iter, + COLUMN_AUTORUN_PIXBUF, pixbuf, + COLUMN_AUTORUN_NAME, _("Open Folder"), + COLUMN_AUTORUN_APP_INFO, NULL, + COLUMN_AUTORUN_X_CONTENT_TYPE, x_content_type, + COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_OPEN_FOLDER, + -1); + g_object_unref (pixbuf); + gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, COLUMN_AUTORUN_PIXBUF, NULL, @@ -332,7 +391,7 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, COLUMN_AUTORUN_ITEM_TYPE, AUTORUN_SEP, -1); - for (l = app_info_list, n = include_ask ? 3 : 2; l != NULL; l = l->next, n++) { + for (l = app_info_list, n = include_ask ? 4 : 3; l != NULL; l = l->next, n++) { GIcon *icon; NautilusIconInfo *icon_info; char *open_string; @@ -349,7 +408,7 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, g_object_unref (icon_info); open_string = g_strdup_printf (_("Open %s"), g_app_info_get_name (app_info)); - + gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, COLUMN_AUTORUN_PIXBUF, pixbuf, @@ -376,15 +435,17 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (list_store)); + gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box)); + renderer = gtk_cell_renderer_pixbuf_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, FALSE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, - "pixbuf", 0, + "pixbuf", COLUMN_AUTORUN_PIXBUF, NULL); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), renderer, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), renderer, - "text", 1, + "text", COLUMN_AUTORUN_NAME, NULL); gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (combo_box), combo_box_separator_func, NULL, NULL); @@ -392,10 +453,13 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0); gtk_widget_set_sensitive (combo_box, FALSE); } else { + gtk_widget_set_sensitive (combo_box, TRUE); if (pref_ask && include_ask) { gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), 0); } else if (pref_ignore) { gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), include_ask ? 1 : 0); + } else if (pref_open_folder) { + gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), include_ask ? 2 : 1); } else if (set_active != -1) { gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), set_active); } else { @@ -406,17 +470,18 @@ nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, data->update_settings = update_settings; data->changed_cb = changed_cb; data->user_data = user_data; - - g_signal_connect (G_OBJECT (combo_box), - "changed", - G_CALLBACK (combo_box_changed), - data); - - /* TODO: unref 'data' when combo box goes bye-bye */ + data->combo_box = combo_box; + data->changed_signal_id = g_signal_connect (G_OBJECT (combo_box), + "changed", + G_CALLBACK (combo_box_changed), + data); } -} -#include <X11/XKBlib.h> + g_object_set_data_full (G_OBJECT (combo_box), + "nautilus_autorun_combobox_data", + data, + (GDestroyNotify) nautilus_autorun_combobox_data_destroy); +} static gboolean is_shift_pressed (void) @@ -441,51 +506,217 @@ is_shift_pressed (void) /*-- BEGIN MOVE TO GIO --*/ static gboolean -_dir_exists (GFile *mount_root, const char *dirname) +_check_nonempty_dir (GFile *mount_root, const char *dirname) { GFile *file; GFileInfo *file_info; - + GFileEnumerator *file_enum; + gboolean ret; + + ret = FALSE; + file = g_file_get_child (mount_root, dirname); + file_enum = g_file_enumerate_children (file, + G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + if (file_enum != NULL) { + file_info = g_file_enumerator_next_file (file_enum, NULL, NULL); + if (file_info != NULL) { + ret = TRUE; + g_object_unref (file_info); + } + g_object_unref (file_enum); + } + g_object_unref (file); + + return ret; +} + +static gboolean +_check_file (GFile *mount_root, const char *file_path, gboolean must_be_executable) +{ + GFile *file; + GFileInfo *file_info; + gboolean ret; + + ret = FALSE; + + file = g_file_get_child (mount_root, file_path); file_info = g_file_query_info (file, - G_FILE_ATTRIBUTE_STANDARD_NAME, + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, G_FILE_QUERY_INFO_NONE, NULL, NULL); if (file_info != NULL) { + if (must_be_executable) { + if (g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) + ret = TRUE; + } else { + ret = TRUE; + } g_object_unref (file_info); } g_object_unref (file); - return file_info != NULL; + return ret; } -static char ** -_g_mount_guess_content_type_for_mount_root (GFile *mount_root, - GError **error) +/** + * _g_mount_guess_content_type: + * @mount: a #GMount. + * @force_rescan: Whether to force a rescan of the content. Otherwise a cached result will be used if available. + * @error: return location for error or %NULL to ignore. + * + * Tries to guess the type of content stored on @mount. Returns one or + * more textual identifiers of well-known content types (typically + * prefixed with "x-content/"). TODO: link to fd.o spec about this. + * + * This function may do I/O and thus may take a long time to + * complete. For the async version, see + * _g_mount_guess_content_type_async(). + * + * Returns: a %NULL terminated array of content types or %NULL on + * error. Caller should free this array with g_strfreev() when done + * with it. + **/ +char ** +_g_mount_guess_content_type (GMount *mount, + gboolean force_rescan, + GError **error) { + unsigned int n; char **ret; GPtrArray *types; - + GFile *root; + GVolume *volume; + char *disc_type = NULL; + + /* TODO: We can't really sensibly cache anything right now.. + * But when moved to gio this can be done. + */ + types = g_ptr_array_new (); - - /* TODO: analyze mount_root and add more content types as needed */ - if (g_file_has_uri_scheme (mount_root, "cdda")) { + root = g_mount_get_root (mount); + volume = g_mount_get_volume (mount); + + /* Take advantage of information from HAL's quirk lists that maps a given + * make/model of (what appears to be just) a storage device to it's intended + * use. + * + * E.g. a mapping saying that a storage device is a music player, a digital + * camera, a videocam, a video play back device, a gps reader.. and so on... + */ + if (volume != NULL) { + char **stor_device_caps; + + /* See gvfs/hal/ghalvolume.c:do_update_from_hal()... + * + * This hack, using g_object_set|get_data() can be + * removed once g_mount_guess_content_type() is in gio + * and the actual code for probing media is in the + * gvfs hal backend. + */ + stor_device_caps = (char **) g_object_get_data (G_OBJECT (volume), "hal-storage-device-capabilities"); + if (stor_device_caps != NULL) { + for (n = 0; stor_device_caps[n] != NULL; n++) { + if (strcmp (stor_device_caps[n], "portable_audio_player") == 0) { + g_ptr_array_add (types, g_strdup ("x-content/audio-player")); + } + /* TODO: map other hal capabilities to x-content/ types */ + } + } + + disc_type = (char *) g_object_get_data (G_OBJECT (volume), "hal-volume.disc.type"); + } + + if (g_file_has_uri_scheme (root, "cdda")) { g_ptr_array_add (types, g_strdup ("x-content/audio-cdda")); goto no_sniff; } + + if (g_file_has_uri_scheme (root, "burn")) { + if (disc_type != NULL) { + if (g_str_has_prefix (disc_type, "dvd")) { + g_ptr_array_add (types, g_strdup ("x-content/blank-dvd")); + } else if (g_str_has_prefix (disc_type, "hddvd")) { + g_ptr_array_add (types, g_strdup ("x-content/blank-hddvd")); + } else if (g_str_has_prefix (disc_type, "bd")) { + g_ptr_array_add (types, g_strdup ("x-content/blank-bd")); + } else { + /* assume CD */ + g_ptr_array_add (types, g_strdup ("x-content/blank-cd")); + } + } + goto no_sniff; + } - if (_dir_exists (mount_root, "DCIM") || - _dir_exists (mount_root, "dcim")) { + if (_check_nonempty_dir (root, "DCIM") || + _check_nonempty_dir (root, "dcim")) { g_ptr_array_add (types, g_strdup ("x-content/image-dcf")); } - if (_dir_exists (mount_root, "VIDEO_TS") || - _dir_exists (mount_root, "video_ts")) { + if (_check_nonempty_dir (root, "VIDEO_TS") && + disc_type != NULL) { g_ptr_array_add (types, g_strdup ("x-content/video-dvd")); } + if (_check_nonempty_dir (root, "AUDIO_TS") && + disc_type != NULL) { + g_ptr_array_add (types, g_strdup ("x-content/audio-dvd")); + } + + + /* see http://www.ccs.neu.edu/home/bchafy/cdb/info/info.html for various docs */ + + if (_check_nonempty_dir (root, "SVCD") && + _check_nonempty_dir (root, "EXT") && + _check_nonempty_dir (root, "MPEG-2") && + disc_type != NULL) { + /* http://everything2.com/index.pl?node_id=1009222 */ + g_ptr_array_add (types, g_strdup ("x-content/video-svcd")); + } + + if (_check_nonempty_dir (root, "VCD") && + _check_nonempty_dir (root, "MPEGAV") && + disc_type != NULL) { + /* http://www.herongyang.com/CD-DVD/VCD-Movie-File-Directory-Structure.html */ + g_ptr_array_add (types, g_strdup ("x-content/video-vcd")); + } + + if (_check_nonempty_dir (root, "BDAV") && + _check_nonempty_dir (root, "BDMV") && + disc_type != NULL) { + /* http://www.blu-raydisc.com/Section-13470/Section-13890/Index.html */ + g_ptr_array_add (types, g_strdup ("x-content/video-bluray")); + } + + if (_check_nonempty_dir (root, "HVDVD_TS") && /* not a typo; should really spell HVDVD_TS */ + disc_type != NULL) { + /* http://www.cdfreaks.com/reviews/CDFreaks--CES-2006/Page-5.html */ + g_ptr_array_add (types, g_strdup ("x-content/video-hddvd")); + } + + if (_check_nonempty_dir (root, "PICTURES") && + disc_type != NULL) { + /* http://www.re.org/kristin/picturecd.html */ + g_ptr_array_add (types, g_strdup ("x-content/image-picturecd")); + } + + if (_check_file (root, ".autorun", TRUE) || + _check_file (root, "autorun", TRUE) || + _check_file (root, "autorun.sh", TRUE) || + _check_file (root, "autorun.exe", TRUE) || _check_file (root, "AUTORUN.EXE", TRUE) || + _check_file (root, "autorun.inf", FALSE) || _check_file (root, "AUTORUN.INF", FALSE)) { + /* http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html */ + + /* http://bugzilla.gnome.org/show_bug.cgi?id=509823#c3 for the autorun.exe and autorun.inf stuff */ + g_ptr_array_add (types, g_strdup ("x-content/software")); + } + + no_sniff: if (types->len == 0) { @@ -495,12 +726,16 @@ no_sniff: g_ptr_array_add (types, NULL); ret = (char **) g_ptr_array_free (types, FALSE); } - + + if (volume != NULL) + g_object_unref (volume); + g_object_unref (root); return ret; } typedef struct { char **guessed_content_type; + gboolean force_rescan; } GuessContentData; static void @@ -512,8 +747,8 @@ guess_content_thread (GSimpleAsyncResult *res, GError *error = NULL; op = g_simple_async_result_get_op_res_gpointer (res); - - op->guessed_content_type = _g_mount_guess_content_type_for_mount_root (G_FILE (object), &error); + + op->guessed_content_type = _g_mount_guess_content_type (G_MOUNT (object), op->force_rescan, &error); if (error != NULL) { g_simple_async_result_set_from_error (res, error); @@ -523,28 +758,32 @@ guess_content_thread (GSimpleAsyncResult *res, /** * _g_mount_guess_content_type_async: - * @mount_root: a #GFile. + * @mount: a #GMount. + * @force_rescan: Whether to force a rescan of the content. Otherwise a cached result will be used if available. * @cancellable: optional #GCancellable object, %NULL to ignore. * @callback: a #GAsyncReadyCallback. * @user_data: user data passed to @callback. * - * Like _g_mount_guess_content_type_async() but analyzes a given sub - * directory instead. + * This is an asynchronous version of _g_mount_guess_content_type(), + * and is finished by calling _g_mount_guess_content_type_finish() with + * the @mount and #GAsyncResults data returned in the @callback. */ -static void -_g_mount_guess_content_type_for_mount_root_async (GFile *mount_root, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +void +_g_mount_guess_content_type_async (GMount *mount, + gboolean force_rescan, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *res; GuessContentData *op; op = g_new0 (GuessContentData, 1); - res = g_simple_async_result_new (G_OBJECT (mount_root), + op->force_rescan = force_rescan; + res = g_simple_async_result_new (G_OBJECT (mount), callback, user_data, - _g_mount_guess_content_type_for_mount_root_async); + _g_mount_guess_content_type_async); g_simple_async_result_set_op_res_gpointer (res, op, g_free); g_simple_async_result_run_in_thread (res, guess_content_thread, G_PRIORITY_DEFAULT, cancellable); @@ -552,28 +791,29 @@ _g_mount_guess_content_type_for_mount_root_async (GFile *mount_roo } /** - * _g_mount_guess_content_type_for_mount_root_finish: - * @mount_root: a #GFile. + * _g_mount_guess_content_type_finish: + * @mount: a #GMount. * @result: a #GAsyncResult. * @error: a #GError location to store the error occuring, or %NULL to * ignore. * - * Like _g_mount_guess_content_type_finish() but analyzes a given sub - * directory instead. + * Finishes guessing content types of @mount. If any errors occured + * during the operation, @error will be set to contain the errors and + * %FALSE will be returned. * * Returns: a %NULL terminated array of content types or %NULL on * error. Caller should free this array with g_strfreev() when done * with it. **/ -static char ** -_g_mount_guess_content_type_for_mount_root_finish (GFile *mount_root, - GAsyncResult *result, - GError **error) +char ** +_g_mount_guess_content_type_finish (GMount *mount, + GAsyncResult *result, + GError **error) { GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); GuessContentData *op; - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _g_mount_guess_content_type_for_mount_root_async); + g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == _g_mount_guess_content_type_async); op = g_simple_async_result_get_op_res_gpointer (simple); return op->guessed_content_type; @@ -584,19 +824,26 @@ _g_mount_guess_content_type_for_mount_root_finish (GFile *mount_ro 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; + + NautilusAutorunOpenWindow open_window_func; + gpointer user_data; } AutorunDialogData; -static void -autorun_launch_for_mount (GMount *mount, GAppInfo *app_info) + +void +nautilus_autorun_launch_for_mount (GMount *mount, GAppInfo *app_info) { GFile *root; NautilusFile *file; @@ -613,6 +860,31 @@ autorun_launch_for_mount (GMount *mount, GAppInfo *app_info) g_list_free (files); } +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 autorun_dialog_response (GtkDialog *dialog, gint response, AutorunDialogData *data) { @@ -620,13 +892,6 @@ autorun_dialog_response (GtkDialog *dialog, gint response, AutorunDialogData *da case GTK_RESPONSE_NONE: /* window was closed */ break; - case 0: - /* eject/unmount */ - nautilus_file_operations_unmount_mount (NULL, - data->mount, - data->should_eject, - FALSE); - break; case GTK_RESPONSE_CANCEL: break; case GTK_RESPONSE_OK: @@ -634,35 +899,33 @@ autorun_dialog_response (GtkDialog *dialog, gint response, AutorunDialogData *da if (data->remember) { /* make sure we don't ask again */ - nautilus_autorun_set_preferences (data->x_content_type, FALSE, data->selected_ignore); - if (!data->selected_ignore && data->selected_app != NULL) { + nautilus_autorun_set_preferences (data->x_content_type, FALSE, 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 */ - nautilus_autorun_set_preferences (data->x_content_type, TRUE, FALSE); + nautilus_autorun_set_preferences (data->x_content_type, TRUE, FALSE, FALSE); } - if (!data->selected_ignore && data->selected_app != NULL) { - autorun_launch_for_mount (data->mount, data->selected_app); + if (!data->selected_ignore && !data->selected_open_folder && data->selected_app != NULL) { + nautilus_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; } - gtk_widget_destroy (GTK_WIDGET (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); + autorun_dialog_destroy (data); } static void autorun_combo_changed (gboolean selected_ask, gboolean selected_ignore, + gboolean selected_open_folder, GAppInfo *selected_app, gpointer user_data) { @@ -673,6 +936,7 @@ autorun_combo_changed (gboolean selected_ask, } data->selected_app = selected_app != NULL ? g_object_ref (selected_app) : NULL; data->selected_ignore = selected_ignore; + data->selected_open_folder = selected_open_folder; } @@ -683,8 +947,9 @@ autorun_always_toggled (GtkToggleButton *togglebutton, AutorunDialogData *data) } -static void -do_autorun_for_content_type (GMount *mount, const char *x_content_type) +/* returns TRUE if a folder window should be opened */ +static gboolean +do_autorun_for_content_type (GMount *mount, const char *x_content_type, NautilusAutorunOpenWindow open_window_func, gpointer user_data) { AutorunDialogData *data; GtkWidget *dialog; @@ -705,27 +970,35 @@ do_autorun_for_content_type (GMount *mount, const char *x_content_type) gboolean user_forced_dialog; gboolean pref_ask; gboolean pref_ignore; + gboolean pref_open_folder; char *media_greeting; + gboolean ret; + ret = FALSE; mount_name = NULL; user_forced_dialog = is_shift_pressed (); - nautilus_autorun_get_preferences (x_content_type, &pref_ask, &pref_ignore); + nautilus_autorun_get_preferences (x_content_type, &pref_ask, &pref_ignore, &pref_open_folder); if (user_forced_dialog) { goto show_dialog; } - if (!pref_ask && !pref_ignore) { + 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) { - autorun_launch_for_mount (mount, app_info); + nautilus_autorun_launch_for_mount (mount, app_info); } goto out; } + if (pref_open_folder) { + ret = TRUE; + goto out; + } + if (pref_ignore) { goto out; } @@ -746,6 +1019,7 @@ show_dialog: icon_info = nautilus_icon_info_lookup (icon, icon_size); pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon_info, icon_size); g_object_unref (icon_info); + g_object_unref (icon); image = gtk_image_new_from_pixbuf (pixbuf); gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); gtk_box_pack_start_defaults (GTK_BOX (hbox), image); @@ -760,22 +1034,40 @@ show_dialog: 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"); + 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"); + 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"); + 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"); + 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 media with digital photos"); - } else if (strcmp (x_content_type, "x-content/blank-media") == 0) { - media_greeting = _("You have just inserted blank media"); + media_greeting = _("You have just inserted media 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 (strcmp (x_content_type, "x-content/software") == 0) { + media_greeting = _("You have just inserted media with software intended to be automatically started."); } else { - media_greeting = _("You have just inserted media"); + /* fallback to generic greeting */ + media_greeting = _("You have just inserted media."); } - markup = g_strdup_printf ("<big><b>%s. %s</b></big>", media_greeting, _("Choose what application to launch.")); + markup = g_strdup_printf ("<big><b>%s %s</b></big>", 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); @@ -793,11 +1085,14 @@ show_dialog: gtk_box_pack_start_defaults (GTK_BOX (vbox), label); 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_combo_box_new (); nautilus_autorun_prepare_combo_box (combo_box, x_content_type, FALSE, FALSE, autorun_combo_changed, data); @@ -844,8 +1139,29 @@ show_dialog: 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; + NautilusAutorunOpenWindow open_window_func; + gpointer user_data; +} AutorunData; + + +static void +autorun_open_folder_for_mount (AutorunData *data) +{ + if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT_OPEN) && + data->open_window_func != NULL) + data->open_window_func (data->mount, data->user_data); } static void @@ -855,30 +1171,46 @@ autorun_guessed_content_type_callback (GObject *source_object, { GError *error; char **guessed_content_type; - GMount *mount = G_MOUNT (user_data); - + AutorunData *data = user_data; + gboolean open_folder; + + open_folder = FALSE; + error = NULL; - guessed_content_type = _g_mount_guess_content_type_for_mount_root_finish (G_FILE (source_object), res, &error); + guessed_content_type = _g_mount_guess_content_type_finish (G_MOUNT (source_object), res, &error); if (error != NULL) { g_warning ("Unabled to guess content type for mount: %s", error->message); g_error_free (error); } else { - if (guessed_content_type != NULL) { + if (guessed_content_type != NULL && g_strv_length (guessed_content_type) > 0) { int n; for (n = 0; guessed_content_type[n] != NULL; n++) { - do_autorun_for_content_type (mount, guessed_content_type[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 { + open_folder = TRUE; } } - g_object_unref (mount); + /* only open the folder once.. */ + if (open_folder) + autorun_open_folder_for_mount (data); + + g_object_unref (data->mount); + g_free (data); } void -nautilus_autorun (GMount *mount) +nautilus_autorun (GMount *mount, NautilusAutorunOpenWindow open_window_func, gpointer user_data) { - GFile *root; + AutorunData *data; + + if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_NEVER)) + return; /* TODO: only do this for local mounts */ @@ -886,10 +1218,81 @@ nautilus_autorun (GMount *mount) * we do this asynchronously (in another thread) since it * requires doing I/O. */ - root = g_mount_get_root (mount); - _g_mount_guess_content_type_for_mount_root_async (root, - NULL, - autorun_guessed_content_type_callback, - g_object_ref (mount)); - g_object_unref (root); + + data = g_new0 (AutorunData, 1); + data->mount = g_object_ref (mount); + data->open_window_func = open_window_func; + data->user_data = user_data; + + _g_mount_guess_content_type_async (mount, + TRUE, + NULL, + autorun_guessed_content_type_callback, + data); +} + +char ** +nautilus_autorun_get_x_content_types_for_file (NautilusFile *nautilus_file, + GMount **out_mount, + gboolean force_rescan, + gboolean include_child_dirs) +{ + GMount *mount; + char **x_content_types; + + x_content_types = NULL; + + g_return_val_if_fail (nautilus_file != NULL, NULL); + + mount = NULL; + if (g_type_is_a (G_OBJECT_TYPE (nautilus_file), NAUTILUS_TYPE_DESKTOP_ICON_FILE)) { + NautilusDesktopIconFile *desktop_icon_file = NAUTILUS_DESKTOP_ICON_FILE (nautilus_file); + NautilusDesktopLink *desktop_link; + + desktop_link = nautilus_desktop_icon_file_get_link (desktop_icon_file); + if (desktop_link != NULL) { + if (nautilus_desktop_link_get_link_type (desktop_link) == NAUTILUS_DESKTOP_LINK_MOUNT) { + mount = nautilus_desktop_link_get_mount (desktop_link); + } + g_object_unref (desktop_link); + } + } else { + GFile *file; + file = nautilus_file_get_location (nautilus_file); + if (file != NULL) { + mount = g_file_find_enclosing_mount (file, NULL, NULL); + if (mount != NULL) { + GFile *mount_root; + mount_root = g_mount_get_root (mount); + if (!include_child_dirs) { + if (!g_file_equal (mount_root, file)) { + g_object_unref (mount); + mount = NULL; + } + } + g_object_unref (mount_root); + } + g_object_unref (file); + } + } + + + /* TODO: handle files in computer:///. + * + * Also need to handle those in libnautilus-private/nautilus-program-choosing.c:nautilus_launch_application() + * + * These NautilusFile instances are of class NautilusVFSFile.. URI is 'computer:///CompactFlash%20Drive.drive' + */ + + if (mount != NULL) { + /* since we always guess the content type at mount type, we're guaranteed + * to get the cached results + */ + x_content_types = _g_mount_guess_content_type (mount, force_rescan, NULL); + if (out_mount != NULL) + *out_mount = g_object_ref (mount); + g_object_unref (mount); + } + + return x_content_types; } diff --git a/libnautilus-private/nautilus-autorun.h b/libnautilus-private/nautilus-autorun.h index 3726e29f4..131a49851 100644 --- a/libnautilus-private/nautilus-autorun.h +++ b/libnautilus-private/nautilus-autorun.h @@ -44,11 +44,29 @@ #include <eel/eel-background.h> #include <libnautilus-private/nautilus-file.h> +void _g_mount_guess_content_type_async (GMount *mount, + gboolean force_rescan, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +char ** _g_mount_guess_content_type_finish (GMount *mount, + GAsyncResult *result, + GError **error); + +char ** _g_mount_guess_content_type (GMount *mount, + gboolean force_rescan, + GError **error); + + typedef void (*NautilusAutorunComboBoxChanged) (gboolean selected_ask, gboolean selected_ignore, + gboolean selected_open_folder, GAppInfo *selected_app, gpointer user_data); +typedef void (*NautilusAutorunOpenWindow) (GMount *mount, gpointer user_data); + void nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, const char *x_content_type, gboolean include_ask, @@ -56,10 +74,16 @@ void nautilus_autorun_prepare_combo_box (GtkWidget *combo_box, NautilusAutorunComboBoxChanged changed_cb, gpointer user_data); -void nautilus_autorun_set_preferences (const char *x_content_type, gboolean pref_ask, gboolean pref_ignore); -void nautilus_autorun_get_preferences (const char *x_content_type, gboolean *pref_ask, gboolean *pref_ignore); +void nautilus_autorun_set_preferences (const char *x_content_type, gboolean pref_ask, gboolean pref_ignore, gboolean pref_open_folder); +void nautilus_autorun_get_preferences (const char *x_content_type, gboolean *pref_ask, gboolean *pref_ignore, gboolean *pref_open_folder); + +void nautilus_autorun (GMount *mount, NautilusAutorunOpenWindow open_window_func, gpointer user_data); -void nautilus_autorun (GMount *mount); +char **nautilus_autorun_get_x_content_types_for_file (NautilusFile *file, + GMount **out_mount, + gboolean force_rescan, + gboolean include_child_dirs); +void nautilus_autorun_launch_for_mount (GMount *mount, GAppInfo *app_info); #endif /* NAUTILUS_AUTORUN_H */ diff --git a/libnautilus-private/nautilus-file-attributes.h b/libnautilus-private/nautilus-file-attributes.h index d263cbec6..a660e48f2 100644 --- a/libnautilus-private/nautilus-file-attributes.h +++ b/libnautilus-private/nautilus-file-attributes.h @@ -40,6 +40,7 @@ typedef enum { NAUTILUS_FILE_ATTRIBUTE_LARGE_TOP_LEFT_TEXT = 1 << 7, NAUTILUS_FILE_ATTRIBUTE_EXTENSION_INFO = 1 << 8, NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL = 1 << 9, + NAUTILUS_FILE_ATTRIBUTE_MOUNT = 1 << 10, } NautilusFileAttributes; #endif /* NAUTILUS_FILE_ATTRIBUTES_H */ diff --git a/libnautilus-private/nautilus-file-private.h b/libnautilus-private/nautilus-file-private.h index efc9c84ec..01a2f6146 100644 --- a/libnautilus-private/nautilus-file-private.h +++ b/libnautilus-private/nautilus-file-private.h @@ -137,6 +137,9 @@ struct NautilusFileDetails GHashTable *extension_attributes; GHashTable *pending_extension_attributes; + /* Mount for mountpoint or the references GMount for a "mountable" */ + GMount *mount; + /* boolean fields: bitfield to save space, since there can be many NautilusFile objects. */ @@ -166,6 +169,8 @@ struct NautilusFileDetails eel_boolean_bit mime_list_failed : 1; eel_boolean_bit mime_list_is_up_to_date : 1; + eel_boolean_bit mount_is_up_to_date : 1; + eel_boolean_bit got_top_left_text : 1; eel_boolean_bit got_large_top_left_text : 1; eel_boolean_bit top_left_text_is_up_to_date : 1; diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index bd519a0e8..2f1f650eb 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -5606,6 +5606,14 @@ nautilus_file_is_mountpoint (NautilusFile *file) return file->details->is_mountpoint; } +GMount * +nautilus_file_get_mount (NautilusFile *file) +{ + if (file->details->mount) { + return g_object_ref (file->details->mount); + } + return NULL; +} /** * nautilus_file_is_broken_symbolic_link diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index 20c46a2cf..d735e1ea5 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -171,6 +171,7 @@ gboolean nautilus_file_is_mime_type (Nautilu gboolean nautilus_file_is_launchable (NautilusFile *file); gboolean nautilus_file_is_symbolic_link (NautilusFile *file); gboolean nautilus_file_is_mountpoint (NautilusFile *file); +GMount * nautilus_file_get_mount (NautilusFile *file); char * nautilus_file_get_volume_free_space (NautilusFile *file); char * nautilus_file_get_volume_name (NautilusFile *file); char * nautilus_file_get_symbolic_link_target_path (NautilusFile *file); diff --git a/libnautilus-private/nautilus-global-preferences.c b/libnautilus-private/nautilus-global-preferences.c index 55498d7f3..5f4f49860 100644 --- a/libnautilus-private/nautilus-global-preferences.c +++ b/libnautilus-private/nautilus-global-preferences.c @@ -492,6 +492,10 @@ static const PreferenceDefault preference_defaults[] = { PREFERENCE_BOOLEAN, GINT_TO_POINTER (TRUE) }, + { NAUTILUS_PREFERENCES_MEDIA_AUTORUN_NEVER, + PREFERENCE_BOOLEAN, + GINT_TO_POINTER (FALSE) + }, { NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_ASK, PREFERENCE_STRING_ARRAY, "", NULL, NULL, NULL @@ -500,6 +504,10 @@ static const PreferenceDefault preference_defaults[] = { PREFERENCE_STRING_ARRAY, "", NULL, NULL, NULL }, + { NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER, + PREFERENCE_STRING_ARRAY, + "", NULL, NULL, NULL + }, { NULL } }; diff --git a/libnautilus-private/nautilus-global-preferences.h b/libnautilus-private/nautilus-global-preferences.h index 4e4432024..e6d030190 100644 --- a/libnautilus-private/nautilus-global-preferences.h +++ b/libnautilus-private/nautilus-global-preferences.h @@ -51,8 +51,10 @@ G_BEGIN_DECLS #define NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT_OPEN "preferences/media_automount_open" /* Autorun options */ +#define NAUTILUS_PREFERENCES_MEDIA_AUTORUN_NEVER "preferences/media_autorun_never" #define NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_ASK "preferences/media_autorun_x_content_ask" #define NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_IGNORE "preferences/media_autorun_x_content_ignore" +#define NAUTILUS_PREFERENCES_MEDIA_AUTORUN_X_CONTENT_OPEN_FOLDER "preferences/media_autorun_x_content_open_folder" /* Trash options */ #define NAUTILUS_PREFERENCES_CONFIRM_TRASH "preferences/confirm_trash" diff --git a/libnautilus-private/nautilus-program-choosing.c b/libnautilus-private/nautilus-program-choosing.c index 73bb0810d..5f675af8f 100644 --- a/libnautilus-private/nautilus-program-choosing.c +++ b/libnautilus-private/nautilus-program-choosing.c @@ -49,6 +49,8 @@ #include <gdk/gdk.h> #include <gdk/gdkx.h> +#include "nautilus-desktop-icon-file.h" + extern char **environ; /* Cut and paste from gdkspawn-x11.c */ @@ -261,10 +263,30 @@ nautilus_launch_application (GAppInfo *application, location = NULL; - if (nautilus_file_is_nautilus_link (file)) { - uri = nautilus_file_get_activation_uri (file); - location = g_file_new_for_uri (uri); - g_free (uri); + if (g_type_is_a (G_OBJECT_TYPE (file), NAUTILUS_TYPE_DESKTOP_ICON_FILE)) { + NautilusDesktopIconFile *desktop_icon_file = NAUTILUS_DESKTOP_ICON_FILE (file); + NautilusDesktopLink *desktop_link; + + desktop_link = nautilus_desktop_icon_file_get_link (desktop_icon_file); + if (desktop_link != NULL) { + if (nautilus_desktop_link_get_link_type (desktop_link) == NAUTILUS_DESKTOP_LINK_MOUNT) { + GMount *mount; + mount = nautilus_desktop_link_get_mount (desktop_link); + if (mount != NULL) { + location = g_mount_get_root (mount); + g_object_unref (mount); + } + } + g_object_unref (desktop_link); + } + } + + if (location == NULL) { + if (nautilus_file_is_nautilus_link (file)) { + uri = nautilus_file_get_activation_uri (file); + location = g_file_new_for_uri (uri); + g_free (uri); + } } if (location == NULL) { diff --git a/nautilus-autorun-software.desktop.in.in b/nautilus-autorun-software.desktop.in.in new file mode 100644 index 000000000..b3c48ef2c --- /dev/null +++ b/nautilus-autorun-software.desktop.in.in @@ -0,0 +1,15 @@ +[Desktop Entry] +Encoding=UTF-8 +_Name=Autorun Prompt +TryExec=nautilus-autorun-software +Exec=nautilus-autorun-software %u +Icon=application-x-executable +NoDisplay=true +Terminal=false +StartupNotify=true +Type=Application +MimeType=x-content/software; +X-GNOME-Bugzilla-Bugzilla=GNOME +X-GNOME-Bugzilla-Product=nautilus +X-GNOME-Bugzilla-Component=general +X-GNOME-Bugzilla-Version=@VERSION@ diff --git a/src/Makefile.am b/src/Makefile.am index f31c3ce07..53740a20a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,6 +5,7 @@ SUBDIRS=file-manager bin_PROGRAMS= \ nautilus \ nautilus-file-management-properties \ + nautilus-autorun-software \ $(NULL) # nautilus-connect-server @@ -130,6 +131,8 @@ nautilus_SOURCES = \ nautilus-window-toolbars.c \ nautilus-window.c \ nautilus-window.h \ + nautilus-x-content-bar.c \ + nautilus-x-content-bar.h \ nautilus-zoom-control.c \ nautilus-zoom-control.h \ $(NULL) @@ -140,6 +143,10 @@ nautilus_file_management_properties_SOURCES= \ nautilus-file-management-properties-main.c \ $(NULL) +nautilus_autorun_software_SOURCES= \ + nautilus-autorun-software.c \ + $(NULL) + # Disabled for now in the gio world #nautilus_connect_server_SOURCES= \ # nautilus-connect-server-dialog.c \ diff --git a/src/file-manager/fm-directory-view.c b/src/file-manager/fm-directory-view.c index 916c49a8b..f8c1fceee 100644 --- a/src/file-manager/fm-directory-view.c +++ b/src/file-manager/fm-directory-view.c @@ -95,6 +95,7 @@ #include <libnautilus-private/nautilus-trash-monitor.h> #include <libnautilus-private/nautilus-ui-utilities.h> #include <libnautilus-private/nautilus-signaller.h> +#include <libnautilus-private/nautilus-autorun.h> #include <unistd.h> /* Minimum starting update inverval */ @@ -4158,6 +4159,27 @@ add_application_to_open_with_menu (FMDirectoryView *view, g_free (tip); } +static void +add_x_content_apps (NautilusFile *file, GList **applications) +{ + char **x_content_types; + + g_return_if_fail (applications != NULL); + + x_content_types = nautilus_autorun_get_x_content_types_for_file (file, NULL, FALSE, FALSE); + + if (x_content_types != NULL) { + unsigned int n; + for (n = 0; x_content_types[n] != NULL; n++) { + char *x_content_type = x_content_types[n]; + GList *app_info_for_x_content_type; + + app_info_for_x_content_type = g_app_info_get_all_for_type (x_content_type); + *applications = g_list_concat (*applications, app_info_for_x_content_type); + } + g_strfreev (x_content_types); + } +} static void reset_open_with_menu (FMDirectoryView *view, GList *selection) @@ -4191,6 +4213,7 @@ reset_open_with_menu (FMDirectoryView *view, GList *selection) filter_default = (selection != NULL); for (node = selection; node != NULL; node = node->next) { + file = NAUTILUS_FILE (node->data); other_applications_visible &= @@ -4208,6 +4231,11 @@ reset_open_with_menu (FMDirectoryView *view, GList *selection) applications = nautilus_mime_get_applications_for_files (selection); } + if (g_list_length (selection) == 1) { + add_x_content_apps (NAUTILUS_FILE (selection->data), &applications); + } + + num_applications = g_list_length (applications); for (node = applications, index = 0; node != NULL; node = node->next, index++) { diff --git a/src/nautilus-application.c b/src/nautilus-application.c index b7198eb08..f772b6461 100644 --- a/src/nautilus-application.c +++ b/src/nautilus-application.c @@ -118,6 +118,11 @@ static void mount_added_callback (GVolumeMonitor *mo static void volume_added_callback (GVolumeMonitor *monitor, GVolume *volume, NautilusApplication *application); +static void drive_connected_callback (GVolumeMonitor *monitor, + GDrive *drive, + NautilusApplication *application); +static void drive_listen_for_eject_button (GDrive *drive, + NautilusApplication *application); static void update_session (gpointer callback_data); static void init_session (void); static gboolean is_kdesktop_present (void); @@ -165,6 +170,55 @@ nautilus_application_get_n_windows (void) } static void +startup_volume_mount_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + if (!g_volume_mount_finish (G_VOLUME (source_object), res, NULL)) { + /* There was an error mounting the volume, so we + clear the automount part. This is otherwise done + when the mount is added to the volume monitor */ + g_object_set_data (source_object, "nautilus-automounted", GINT_TO_POINTER (0)); + } +} + +static void +automount_all_volumes (NautilusApplication *application) +{ + GList *volumes, *l; + GMount *mount; + GVolume *volume; + + if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT)) { + /* automount all mountable volumes at start-up */ + volumes = g_volume_monitor_get_volumes (application->volume_monitor); + for (l = volumes; l != NULL; l = l->next) { + volume = l->data; + + /* TODO: only do this for local volumes */ + + if (!g_volume_can_mount (volume)) { + continue; + } + + mount = g_volume_get_mount (volume); + if (mount != NULL) { + g_object_unref (mount); + continue; + } + + /* Set this so we don't autorun stuff from it */ + g_object_set_data (G_OBJECT (volume), "nautilus-automounted", GINT_TO_POINTER (1)); + + /* pass NULL as GMountOperation to avoid user interaction */ + g_volume_mount (volume, NULL, NULL, startup_volume_mount_cb, NULL); + } + eel_g_object_list_free (volumes); + } + +} + +static void nautilus_application_instance_init (NautilusApplication *application) { /* Create an undo manager */ @@ -172,22 +226,6 @@ nautilus_application_instance_init (NautilusApplication *application) application->shell = nautilus_shell_new (application); - /* Watch for mounts so we can restore open windows This used - * to be for showing new window on mount, but is not used - * anymore */ - - /* Watch for unmounts so we can close open windows */ - /* TODO-gio: This should be using the UNMOUNTED feature of GFileMonitor instead */ - application->volume_monitor = g_volume_monitor_get (); - g_signal_connect_object (application->volume_monitor, "mount_removed", - G_CALLBACK (mount_removed_callback), application, 0); - g_signal_connect_object (application->volume_monitor, "mount_pre_unmount", - G_CALLBACK (mount_removed_callback), application, 0); - g_signal_connect_object (application->volume_monitor, "mount_added", - G_CALLBACK (mount_added_callback), application, 0); - g_signal_connect_object (application->volume_monitor, "volume_added", - G_CALLBACK (volume_added_callback), application, 0); - /* register views */ fm_icon_view_register (); fm_desktop_icon_view_register (); @@ -343,49 +381,6 @@ nautilus_make_uri_list_from_shell_strv (const char * const *strv) } static void -migrate_old_nautilus_files (void) -{ - char *new_desktop_dir; - char *old_desktop_dir; - char *migrated_file; - char *link_name; - char *link_path; - int fd; - - old_desktop_dir = nautilus_get_gmc_desktop_directory (); - if (!g_file_test (old_desktop_dir, G_FILE_TEST_IS_DIR) || - g_file_test (old_desktop_dir, G_FILE_TEST_IS_SYMLINK)) { - g_free (old_desktop_dir); - return; - } - migrated_file = g_build_filename (old_desktop_dir, ".migrated", NULL); - if (!g_file_test (migrated_file, G_FILE_TEST_EXISTS)) { - link_name = g_filename_from_utf8 (_("Link To Old Desktop"), -1, NULL, NULL, NULL); - new_desktop_dir = nautilus_get_desktop_directory (); - link_path = g_build_filename (new_desktop_dir, link_name, NULL); - - - symlink ("../.gnome-desktop", link_path); - - g_free (link_name); - g_free (new_desktop_dir); - g_free (link_path); - - fd = creat (migrated_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (fd >= 0) { - close (fd); - } - - eel_show_info_dialog (_("A link called \"Link To Old Desktop\" has been created on the desktop."), - _("The location of the desktop directory has changed in GNOME 2.4. " - "You can open the link and move over the files you want, then delete the link."), - NULL); - } - g_free (old_desktop_dir); - g_free (migrated_file); -} - -static void menu_provider_items_updated_handler (NautilusMenuProvider *provider, GtkWidget* parent_window, gpointer data) { @@ -417,6 +412,8 @@ menu_provider_init_callback (void) static void finish_startup (NautilusApplication *application) { + GList *drives; + /* initialize nautilus modules */ nautilus_module_init (); @@ -425,14 +422,34 @@ finish_startup (NautilusApplication *application) /* attach menu-provider module callback */ menu_provider_init_callback (); - /* initialize URI authentication manager */ - gnome_authentication_manager_init (); - - /* Make the desktop work with old Nautilus. */ - migrate_old_nautilus_files (); - /* Initialize the desktop link monitor singleton */ nautilus_desktop_link_monitor_get (); + + /* Watch for mounts so we can restore open windows This used + * to be for showing new window on mount, but is not used + * anymore */ + + /* Watch for unmounts so we can close open windows */ + /* TODO-gio: This should be using the UNMOUNTED feature of GFileMonitor instead */ + application->volume_monitor = g_volume_monitor_get (); + g_signal_connect_object (application->volume_monitor, "mount_removed", + G_CALLBACK (mount_removed_callback), application, 0); + g_signal_connect_object (application->volume_monitor, "mount_pre_unmount", + G_CALLBACK (mount_removed_callback), application, 0); + g_signal_connect_object (application->volume_monitor, "mount_added", + G_CALLBACK (mount_added_callback), application, 0); + g_signal_connect_object (application->volume_monitor, "volume_added", + G_CALLBACK (volume_added_callback), application, 0); + g_signal_connect_object (application->volume_monitor, "drive_connected", + G_CALLBACK (drive_connected_callback), application, 0); + + /* listen for eject button presses */ + drives = g_volume_monitor_get_connected_drives (application->volume_monitor); + g_list_foreach (drives, (GFunc) drive_listen_for_eject_button, application); + g_list_foreach (drives, (GFunc) g_object_unref, NULL); + g_list_free (drives); + + automount_all_volumes (application); } static void @@ -1315,18 +1332,94 @@ volume_added_callback (GVolumeMonitor *monitor, GVolume *volume, NautilusApplication *application) { - if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT)) { + if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT) && + g_volume_can_mount (volume)) { nautilus_file_operations_mount_volume (NULL, volume); } } static void +drive_eject_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error; + char *primary; + char *name; + error = NULL; + if (!g_drive_eject_finish (G_DRIVE (source_object), res, &error)) { + if (error->code != G_IO_ERROR_FAILED_HANDLED) { + name = g_drive_get_name (G_DRIVE (source_object)); + primary = g_strdup_printf (_("Unable to eject %s"), name); + g_free (name); + eel_show_error_dialog (primary, + error->message, + NULL); + g_free (primary); + } + g_error_free (error); + } +} + +static void +drive_eject_button_pressed (GDrive *drive, + NautilusApplication *application) +{ + g_drive_eject (drive, 0, NULL, drive_eject_cb, NULL); +} + +static void +drive_listen_for_eject_button (GDrive *drive, NautilusApplication *application) +{ + g_signal_connect (drive, + "eject-button", + G_CALLBACK (drive_eject_button_pressed), + application); +} + +static void +drive_connected_callback (GVolumeMonitor *monitor, + GDrive *drive, + NautilusApplication *application) +{ + drive_listen_for_eject_button (drive, application); +} + +static void +autorun_show_window (GMount *mount, gpointer user_data) +{ + GFile *location; + NautilusApplication *application = user_data; + + location = g_mount_get_root (mount); + + /* Ther should probably be an easier way to do this */ + if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_BROWSER)) { + NautilusWindow *window; + window = nautilus_application_create_navigation_window (application, + NULL, + gdk_screen_get_default ()); + nautilus_window_go_to (window, location); + + } else { + nautilus_application_present_spatial_window (application, + NULL, + NULL, + location, + gdk_screen_get_default ()); + } + g_object_unref (location); +} + +static void mount_added_callback (GVolumeMonitor *monitor, GMount *mount, NautilusApplication *application) { NautilusDirectory *directory; GFile *root; + GVolume *enclosing_volume; + gboolean ignore_autorun; root = g_mount_get_root (mount); directory = nautilus_directory_get_existing (root); @@ -1336,7 +1429,21 @@ mount_added_callback (GVolumeMonitor *monitor, nautilus_directory_unref (directory); } - nautilus_autorun (mount); + ignore_autorun = FALSE; + + enclosing_volume = g_mount_get_volume (mount); + if (enclosing_volume != NULL) { + if (g_object_get_data (G_OBJECT (enclosing_volume), "nautilus-automounted") != NULL) { + ignore_autorun = TRUE; + /* Autorun if the user unmounts and then mounts */ + g_object_set_data (G_OBJECT (enclosing_volume), "nautilus-automounted", NULL); + } + g_object_unref (enclosing_volume); + } + + if (!ignore_autorun) { + nautilus_autorun (mount, autorun_show_window, application); + } } /* Called whenever a mount is unmounted. Check and see if there are diff --git a/src/nautilus-autorun-software.c b/src/nautilus-autorun-software.c new file mode 100644 index 000000000..7c1b5a533 --- /dev/null +++ b/src/nautilus-autorun-software.c @@ -0,0 +1,254 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ + +/* Nautilus + + Copyright (C) 2008 Red Hat, Inc. + + The Gnome Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The Gnome 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the Gnome Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: David Zeuthen <davidz@redhat.com> +*/ + +#include <config.h> + +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <gtk/gtk.h> +#include <gio/gio.h> + +#include <glib/gi18n.h> + +#include <libgnome/gnome-program.h> +#include <libgnomeui/gnome-ui-init.h> +#include <libnautilus-private/nautilus-module.h> +#include <libnautilus-private/nautilus-icon-info.h> + +typedef struct +{ + GtkWidget *dialog; + GMount *mount; +} AutorunSoftwareDialogData; + +static void autorun_software_dialog_mount_unmounted (GMount *mount, AutorunSoftwareDialogData *data); + +static void +autorun_software_dialog_destroy (AutorunSoftwareDialogData *data) +{ + g_signal_handlers_disconnect_by_func (G_OBJECT (data->mount), + G_CALLBACK (autorun_software_dialog_mount_unmounted), + data); + + gtk_widget_destroy (GTK_WIDGET (data->dialog)); + g_object_unref (data->mount); + g_free (data); +} + +static void +autorun_software_dialog_mount_unmounted (GMount *mount, AutorunSoftwareDialogData *data) +{ + autorun_software_dialog_destroy (data); +} + +static gboolean +_check_file (GFile *mount_root, const char *file_path, gboolean must_be_executable) +{ + GFile *file; + GFileInfo *file_info; + gboolean ret; + + ret = FALSE; + + file = g_file_get_child (mount_root, file_path); + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE, + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + if (file_info != NULL) { + if (must_be_executable) { + if (g_file_info_get_attribute_boolean (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) + ret = TRUE; + } else { + ret = TRUE; + } + g_object_unref (file_info); + } + g_object_unref (file); + + return ret; +} + +static void +autorun (GMount *mount) +{ + GFile *root; + GFile *program_to_spawn; + char *path_to_spawn; + char *cwd_for_program; + + root = g_mount_get_root (mount); + + /* Careful here, according to + * + * http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html + * + * the ordering does matter. + */ + + program_to_spawn = NULL; + path_to_spawn = NULL; + + if (_check_file (root, ".autorun", TRUE)) { + program_to_spawn = g_file_get_child (root, ".autorun"); + } else if (_check_file (root, "autorun", TRUE)) { + program_to_spawn = g_file_get_child (root, "autorun"); + } else if (_check_file (root, "autorun.sh", TRUE)) { + program_to_spawn = g_file_get_child (root, "autorun.sh"); + } else if (_check_file (root, "autorun.exe", TRUE)) { + /* TODO */ + } else if (_check_file (root, "AUTORUN.EXE", TRUE)) { + /* TODO */ + } else if (_check_file (root, "autorun.inf", FALSE)) { + /* TODO */ + } else if (_check_file (root, "AUTORUN.INF", FALSE)) { + /* TODO */ + } + + if (program_to_spawn != NULL) + path_to_spawn = g_file_get_path (program_to_spawn); + + cwd_for_program = g_file_get_path (root); + + if (path_to_spawn != NULL && cwd_for_program != NULL) { + if (chdir (cwd_for_program) == 0) { + execl (path_to_spawn, path_to_spawn, NULL); + g_warning ("Error execing program: %m"); + } + g_warning ("Error chdir to '%s': %m", cwd_for_program); + } + g_warning ("Cannot find path for program to spawn"); + + if (program_to_spawn != NULL) + g_object_unref (program_to_spawn); + g_free (path_to_spawn); + g_free (cwd_for_program); +} + +static void +present_autorun_for_software_dialog (GMount *mount) +{ + GIcon *icon; + int icon_size; + NautilusIconInfo *icon_info; + GdkPixbuf *pixbuf; + GtkWidget *image; + char *mount_name; + GtkWidget *dialog; + AutorunSoftwareDialogData *data; + + mount_name = g_mount_get_name (mount); + + dialog = gtk_message_dialog_new_with_markup (NULL, /* TODO: parent window? */ + 0, + GTK_MESSAGE_OTHER, + GTK_BUTTONS_CANCEL, + _("<big><b>This media contains software intended to be automatically started. Would you like to run it?</b></big>")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("The software will run directly from the media \"%s\". " + "You should never run software that you don't trust.\n" + "\n" + "If in doubt, press Cancel."), + mount_name); + + /* TODO: in a star trek future add support for verifying + * software on media (e.g. if it has a certificate, check it + * etc.) + */ + + + icon = g_mount_get_icon (mount); + icon_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_DIALOG); + icon_info = nautilus_icon_info_lookup (icon, icon_size); + pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon_info, icon_size); + image = gtk_image_new_from_pixbuf (pixbuf); + gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0); + + gtk_message_dialog_set_image (GTK_MESSAGE_DIALOG (dialog), image); + + gtk_window_set_title (GTK_WINDOW (dialog), mount_name); + gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf); + + data = g_new0 (AutorunSoftwareDialogData, 1); + data->dialog = dialog; + data->mount = g_object_ref (mount); + + g_signal_connect (G_OBJECT (mount), + "unmounted", + G_CALLBACK (autorun_software_dialog_mount_unmounted), + data); + + gtk_dialog_add_button (GTK_DIALOG (dialog), + _("_Run"), + GTK_RESPONSE_OK); + + gtk_widget_show_all (dialog); + + if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) { + autorun (mount); + } + + g_object_unref (icon_info); + g_object_unref (pixbuf); + g_free (mount_name); +} + +int +main (int argc, char *argv[]) +{ + GVolumeMonitor *monitor; + GFile *file; + GMount *mount; + + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + gnome_program_init ("nautilus-autorun-software", VERSION, + LIBGNOMEUI_MODULE, argc, argv, + NULL, NULL); + + if (argc != 2) + goto out; + + /* instantiate monitor so we get the "unmounted" signal properly */ + monitor = g_volume_monitor_get (); + if (monitor == NULL) + goto out; + + file = g_file_new_for_commandline_arg (argv[1]); + if (file == NULL) + goto out; + + mount = g_file_find_enclosing_mount (file, NULL, NULL); + if (mount == NULL) + goto out; + + present_autorun_for_software_dialog (mount); + +out: + return 0; +} diff --git a/src/nautilus-file-management-properties.c b/src/nautilus-file-management-properties.c index fa43efc62..7485b3c75 100644 --- a/src/nautilus-file-management-properties.c +++ b/src/nautilus-file-management-properties.c @@ -28,13 +28,8 @@ #include <string.h> #include <time.h> -#include <gtk/gtkdialog.h> -#include <gtk/gtkmenu.h> -#include <gtk/gtkmenuitem.h> -#include <gtk/gtkmessagedialog.h> -#include <gtk/gtknotebook.h> -#include <gtk/gtkcombobox.h> -#include <gtk/gtksizegroup.h> +#include <gtk/gtk.h> +#include <gio/gio.h> #include <libgnome/gnome-help.h> #include <glib/gi18n.h> @@ -74,8 +69,8 @@ #define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_OPEN_NEW_WINDOW_WIDGET "new_window_checkbutton" #define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_SHOW_HIDDEN_WIDGET "hidden_files_checkbutton" #define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_TREE_VIEW_FOLDERS_WIDGET "treeview_folders_checkbutton" -#define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_MEDIA_AUTOMOUNT "media_automount_checkbutton" #define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_MEDIA_AUTOMOUNT_OPEN "media_automount_open_checkbutton" +#define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_MEDIA_AUTORUN_NEVER "media_autorun_never_checkbutton" /* int enums */ #define NAUTILUS_FILE_MANAGEMENT_PROPERTIES_THUMBNAIL_LIMIT_WIDGET "preview_image_size_combobox" @@ -515,24 +510,145 @@ nautilus_file_management_properties_dialog_setup_list_column_page (GladeXML *xml gtk_box_pack_start (GTK_BOX (box), chooser, TRUE, TRUE, 0); } +static void +nautilus_file_management_properties_dialog_update_media_sensitivity (GladeXML *xml_dialog) +{ + gtk_widget_set_sensitive (glade_xml_get_widget (xml_dialog, "media_handling_vbox"), + ! eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_NEVER)); +} + +static void +other_type_combo_box_changed (GtkComboBox *combo_box, GtkComboBox *action_combo_box) +{ + GtkTreeIter iter; + GtkTreeModel *model; + char *x_content_type; + + x_content_type = NULL; + + if (!gtk_combo_box_get_active_iter (combo_box, &iter)) { + goto out; + } + + model = gtk_combo_box_get_model (combo_box); + if (model == NULL) { + goto out; + } + + gtk_tree_model_get (model, &iter, + 2, &x_content_type, + -1); + + nautilus_autorun_prepare_combo_box (GTK_WIDGET (action_combo_box), + x_content_type, + TRUE, + TRUE, + NULL, NULL); +out: + g_free (x_content_type); +} + static void nautilus_file_management_properties_dialog_setup_media_page (GladeXML *xml_dialog) { unsigned int n; + GList *l; + GList *content_types; + GtkWidget *other_type_combo_box; + GtkListStore *other_type_list_store; + GtkCellRenderer *renderer; + GtkTreeIter iter; const char *s[] = {"media_audio_cdda_combobox", "x-content/audio-cdda", "media_video_dvd_combobox", "x-content/video-dvd", - "media_video_vcd_combobox", "x-content/video-vcd", - "media_video_svcd_combobox", "x-content/video-svcd", - "media_blank_combobox", "x-content/blank-media", - "media_music_player_combobox", "x-content/music-player", + "media_music_player_combobox", "x-content/audio-player", "media_dcf_combobox", "x-content/image-dcf", + "media_software_combobox", "x-content/software", NULL}; for (n = 0; s[n*2] != NULL; n++) { nautilus_autorun_prepare_combo_box (glade_xml_get_widget (xml_dialog, s[n*2]), s[n*2 + 1], TRUE, TRUE, NULL, NULL); } + + other_type_combo_box = glade_xml_get_widget (xml_dialog, "media_other_type_combobox"); + + other_type_list_store = gtk_list_store_new (3, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (other_type_list_store), + 1, GTK_SORT_ASCENDING); + + + content_types = g_content_types_get_registered (); + + for (l = content_types; l != NULL; l = l->next) { + char *content_type = l->data; + char *description; + GIcon *icon; + NautilusIconInfo *icon_info; + GdkPixbuf *pixbuf; + int icon_size; + + if (!g_str_has_prefix (content_type, "x-content/")) + continue; + for (n = 0; s[n*2] != NULL; n++) { + if (strcmp (content_type, s[n*2 + 1]) == 0) { + goto skip; + } + } + + icon_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_MENU); + + description = g_content_type_get_description (content_type); + gtk_list_store_append (other_type_list_store, &iter); + icon = g_content_type_get_icon (content_type); + if (icon != NULL) { + icon_info = nautilus_icon_info_lookup (icon, icon_size); + g_object_unref (icon); + pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon_info, icon_size); + g_object_unref (icon_info); + } else { + pixbuf = NULL; + } + + gtk_list_store_set (other_type_list_store, &iter, + 0, pixbuf, + 1, description, + 2, content_type, + -1); + if (pixbuf != NULL) + g_object_unref (pixbuf); + g_free (description); + skip: + ; + } + g_list_foreach (content_types, (GFunc) g_free, NULL); + g_list_free (content_types); + + gtk_combo_box_set_model (GTK_COMBO_BOX (other_type_combo_box), GTK_TREE_MODEL (other_type_list_store)); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (other_type_combo_box), renderer, FALSE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (other_type_combo_box), renderer, + "pixbuf", 0, + NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (other_type_combo_box), renderer, TRUE); + gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (other_type_combo_box), renderer, + "text", 1, + NULL); + + g_signal_connect (G_OBJECT (other_type_combo_box), + "changed", + G_CALLBACK (other_type_combo_box_changed), + glade_xml_get_widget (xml_dialog, "media_other_action_combobox")); + + gtk_combo_box_set_active (GTK_COMBO_BOX (other_type_combo_box), 0); + + nautilus_file_management_properties_dialog_update_media_sensitivity (xml_dialog); } static void @@ -577,11 +693,11 @@ nautilus_file_management_properties_dialog_setup (GladeXML *xml_dialog, GtkWindo NAUTILUS_PREFERENCES_ALWAYS_USE_BROWSER); eel_preferences_glade_connect_bool (xml_dialog, - NAUTILUS_FILE_MANAGEMENT_PROPERTIES_MEDIA_AUTOMOUNT, - NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT); - eel_preferences_glade_connect_bool (xml_dialog, NAUTILUS_FILE_MANAGEMENT_PROPERTIES_MEDIA_AUTOMOUNT_OPEN, NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT_OPEN); + eel_preferences_glade_connect_bool (xml_dialog, + NAUTILUS_FILE_MANAGEMENT_PROPERTIES_MEDIA_AUTORUN_NEVER, + NAUTILUS_PREFERENCES_MEDIA_AUTORUN_NEVER); eel_preferences_glade_connect_bool (xml_dialog, NAUTILUS_FILE_MANAGEMENT_PROPERTIES_TRASH_CONFIRM_WIDGET, @@ -656,8 +772,12 @@ nautilus_file_management_properties_dialog_setup (GladeXML *xml_dialog, GtkWindo nautilus_file_management_properties_dialog_setup_icon_caption_page (xml_dialog); nautilus_file_management_properties_dialog_setup_list_column_page (xml_dialog); - nautilus_file_management_properties_dialog_setup_media_page (xml_dialog); + + eel_preferences_add_callback (NAUTILUS_PREFERENCES_MEDIA_AUTORUN_NEVER, + (EelPreferencesCallback ) nautilus_file_management_properties_dialog_update_media_sensitivity, + g_object_ref (xml_dialog)); + /* UI callbacks */ dialog = glade_xml_get_widget (xml_dialog, "file_management_dialog"); diff --git a/src/nautilus-file-management-properties.glade b/src/nautilus-file-management-properties.glade index 979944cbc..95d5bdb4e 100644 --- a/src/nautilus-file-management-properties.glade +++ b/src/nautilus-file-management-properties.glade @@ -2204,569 +2204,530 @@ Never</property> <property name="border_width">12</property> <property name="visible">True</property> <property name="homogeneous">False</property> - <property name="spacing">18</property> + <property name="spacing">6</property> <child> - <widget class="GtkVBox" id="vbox42"> + <widget class="GtkVBox" id="media_handling_vbox"> <property name="visible">True</property> <property name="homogeneous">False</property> <property name="spacing">6</property> <child> - <widget class="GtkLabel" id="label41"> + <widget class="GtkVBox" id="vbox44"> <property name="visible">True</property> - <property name="label" translatable="yes"><span weight="bold">Automatic Mounting</span></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkAlignment" id="alignment19"> - <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">12</property> - <property name="right_padding">0</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> <child> - <widget class="GtkVBox" id="vbox46"> + <widget class="GtkLabel" id="label42"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">6</property> - - <child> - <widget class="GtkCheckButton" id="media_automount_checkbutton"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Automatically _mount media</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkCheckButton" id="media_automount_open_checkbutton"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="label" translatable="yes">Open _folder when automounted</property> - <property name="use_underline">True</property> - <property name="relief">GTK_RELIEF_NORMAL</property> - <property name="focus_on_click">True</property> - <property name="active">False</property> - <property name="inconsistent">False</property> - <property name="draw_indicator">True</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <property name="label" translatable="yes"><span weight="bold">Media Handling</span></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkVBox" id="vbox44"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">6</property> - - <child> - <widget class="GtkLabel" id="label42"> - <property name="visible">True</property> - <property name="label" translatable="yes"><span weight="bold">Autorun</span></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkAlignment" id="alignment18"> - <property name="visible">True</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xscale">1</property> - <property name="yscale">1</property> - <property name="top_padding">0</property> - <property name="bottom_padding">0</property> - <property name="left_padding">12</property> - <property name="right_padding">0</property> <child> - <widget class="GtkVBox" id="vbox49"> + <widget class="GtkAlignment" id="alignment18"> <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">6</property> - - <child> - <widget class="GtkLabel" id="label60"> - <property name="visible">True</property> - <property name="label" translatable="yes">Choose what happens when inserting media or connecting devices to the system</property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">True</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> <child> - <widget class="GtkTable" id="table4"> + <widget class="GtkVBox" id="vbox52"> <property name="visible">True</property> - <property name="n_rows">7</property> - <property name="n_columns">2</property> <property name="homogeneous">False</property> - <property name="row_spacing">6</property> - <property name="column_spacing">6</property> + <property name="spacing">6</property> <child> - <widget class="GtkLabel" id="label44"> + <widget class="GtkLabel" id="label60"> <property name="visible">True</property> - <property name="label" translatable="yes">CD _Audio:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> + <property name="label" translatable="yes">Choose what happens when inserting media or connecting devices to the system</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> + <property name="wrap">True</property> <property name="selectable">False</property> <property name="xalign">0</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> - <property name="mnemonic_widget">media_audio_cdda_combobox</property> <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> <property name="width_chars">-1</property> <property name="single_line_mode">False</property> <property name="angle">0</property> </widget> <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">0</property> - <property name="bottom_attach">1</property> - <property name="x_options">fill</property> - <property name="y_options"></property> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> </packing> </child> <child> - <widget class="GtkLabel" id="label50"> + <widget class="GtkTable" id="table4"> <property name="visible">True</property> - <property name="label" translatable="yes">_DVD Video:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="mnemonic_widget">media_video_dvd_combobox</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="n_rows">5</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkLabel" id="label44"> + <property name="visible">True</property> + <property name="label" translatable="yes">CD _Audio:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_audio_cdda_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label50"> + <property name="visible">True</property> + <property name="label" translatable="yes">_DVD Video:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_video_dvd_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="media_audio_cdda_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="media_video_dvd_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label54"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Music Player:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_music_player_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="media_music_player_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label59"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Photos:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_dcf_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="media_dcf_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label57"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Software:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_software_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="media_software_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">4</property> + <property name="bottom_attach">5</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> </widget> <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">fill</property> - <property name="y_options"></property> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> </packing> </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> - <child> - <widget class="GtkLabel" id="label51"> - <property name="visible">True</property> - <property name="label" translatable="yes">_Video CD:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="mnemonic_widget">media_video_vcd_combobox</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">fill</property> - <property name="y_options"></property> - </packing> - </child> + <child> + <widget class="GtkVBox" id="vbox50"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> - <child> - <widget class="GtkLabel" id="label52"> - <property name="visible">True</property> - <property name="label" translatable="yes">_Super Video CD:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="mnemonic_widget">media_video_svcd_combobox</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="x_options">fill</property> - <property name="y_options"></property> - </packing> - </child> + <child> + <widget class="GtkLabel" id="label61"> + <property name="visible">True</property> + <property name="label" translatable="yes"><span weight="bold">Other Media</span></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> - <child> - <widget class="GtkLabel" id="label57"> - <property name="visible">True</property> - <property name="label" translatable="yes">_Blank Disc:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="mnemonic_widget">media_blank_combobox</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="x_options">fill</property> - <property name="y_options"></property> - </packing> - </child> + <child> + <widget class="GtkAlignment" id="alignment20"> + <property name="visible">True</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xscale">1</property> + <property name="yscale">1</property> + <property name="top_padding">0</property> + <property name="bottom_padding">0</property> + <property name="left_padding">12</property> + <property name="right_padding">0</property> - <child> - <widget class="GtkLabel" id="label54"> - <property name="visible">True</property> - <property name="label" translatable="yes">M_usic Player:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> - <property name="selectable">False</property> - <property name="xalign">0</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> - </widget> - <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="x_options">fill</property> - <property name="y_options"></property> - </packing> - </child> + <child> + <widget class="GtkVBox" id="vbox51"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> <child> - <widget class="GtkLabel" id="label59"> + <widget class="GtkLabel" id="label65"> <property name="visible">True</property> - <property name="label" translatable="yes">_Photo Flash Card:</property> - <property name="use_underline">True</property> - <property name="use_markup">False</property> + <property name="label" translatable="yes">Less common media formats can be configured here</property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">False</property> + <property name="wrap">True</property> <property name="selectable">False</property> <property name="xalign">0</property> <property name="yalign">0.5</property> <property name="xpad">0</property> <property name="ypad">0</property> - <property name="mnemonic_widget">media_dcf_combobox</property> <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> <property name="width_chars">-1</property> <property name="single_line_mode">False</property> <property name="angle">0</property> </widget> <packing> - <property name="left_attach">0</property> - <property name="right_attach">1</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="x_options">fill</property> - <property name="y_options"></property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_audio_cdda_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">0</property> - <property name="bottom_attach">1</property> - <property name="y_options">fill</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_video_dvd_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">fill</property> - <property name="y_options">fill</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_video_vcd_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">fill</property> - <property name="y_options">fill</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_video_svcd_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="x_options">fill</property> - <property name="y_options">fill</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_blank_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="x_options">fill</property> - <property name="y_options">fill</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_music_player_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="x_options">fill</property> - <property name="y_options">fill</property> - </packing> - </child> - - <child> - <widget class="GtkComboBox" id="media_dcf_combobox"> - <property name="visible">True</property> - <property name="add_tearoffs">False</property> - <property name="focus_on_click">True</property> - </widget> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="x_options">fill</property> - <property name="y_options">fill</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="GtkHBox" id="hbox37"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> - - <child> - <widget class="GtkImage" id="image1"> - <property name="visible">True</property> - <property name="stock">gtk-dialog-info</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">8</property> - <property name="ypad">0</property> - </widget> - <packing> <property name="padding">0</property> <property name="expand">False</property> - <property name="fill">True</property> + <property name="fill">False</property> </packing> </child> <child> - <widget class="GtkLabel" id="label58"> + <widget class="GtkTable" id="table5"> <property name="visible">True</property> - <property name="label" translatable="yes"><i>You can configure applications for other kinds of media and devices by holding down the Shift key while inserting the media or device.</i></property> - <property name="use_underline">False</property> - <property name="use_markup">True</property> - <property name="justify">GTK_JUSTIFY_LEFT</property> - <property name="wrap">True</property> - <property name="selectable">False</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> - <property name="width_chars">-1</property> - <property name="single_line_mode">False</property> - <property name="angle">0</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">6</property> + + <child> + <widget class="GtkComboBox" id="media_other_type_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label64"> + <property name="visible">True</property> + <property name="label" translatable="yes">Acti_on:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_other_action_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkComboBox" id="media_other_action_combobox"> + <property name="visible">True</property> + <property name="add_tearoffs">False</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label63"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Type:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">media_other_type_combobox</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> </widget> <packing> <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> + <property name="expand">True</property> + <property name="fill">True</property> </packing> </child> </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> </child> </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> </child> </widget> <packing> @@ -2778,8 +2739,46 @@ Never</property> </widget> <packing> <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="media_autorun_never_checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">_Never prompt or start programs on media insertion</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkCheckButton" id="media_automount_open_checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">B_rowse media when inserted</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> </packing> </child> </widget> diff --git a/src/nautilus-window-manage-views.c b/src/nautilus-window-manage-views.c index 4eae4419e..df6df5799 100644 --- a/src/nautilus-window-manage-views.c +++ b/src/nautilus-window-manage-views.c @@ -36,6 +36,7 @@ #include "nautilus-main.h" #include "nautilus-window-private.h" #include "nautilus-trash-bar.h" +#include "nautilus-x-content-bar.h" #include "nautilus-zoom-control.h" #include <eel/eel-accessibility.h> #include <eel/eel-debug.h> @@ -63,6 +64,7 @@ #include <libnautilus-private/nautilus-search-directory.h> #include <libnautilus-private/nautilus-view-factory.h> #include <libnautilus-private/nautilus-window-info.h> +#include <libnautilus-private/nautilus-autorun.h> /* FIXME bugzilla.gnome.org 41243: * We should use inheritance instead of these special cases @@ -1291,6 +1293,32 @@ add_extension_extra_widgets (NautilusWindow *window, GFile *location) } static void +nautilus_window_show_x_content_bar (NautilusWindow *window, GMount *mount, char **x_content_types) +{ + unsigned int n; + + g_assert (NAUTILUS_IS_WINDOW (window)); + + for (n = 0; x_content_types[n] != NULL; n++) { + GAppInfo *default_app; + + /* skip blank media; the burn:/// location will provide it's own cluebar */ + if (g_str_has_prefix (x_content_types[n], "x-content/blank-")) + continue; + + /* only show the cluebar if a default app is available */ + default_app = g_app_info_get_default_for_type (x_content_types[n], FALSE); + if (default_app != NULL) { + GtkWidget *bar; + bar = nautilus_x_content_bar_new (mount, x_content_types[n]); + gtk_widget_show (bar); + nautilus_window_add_extra_location_widget (window, bar); + g_object_unref (default_app); + } + } +} + +static void nautilus_window_show_trash_bar (NautilusWindow *window) { GtkWidget *bar; @@ -1312,6 +1340,8 @@ update_for_new_location (NautilusWindow *window) NautilusDirectory *directory; gboolean location_really_changed; char *uri; + char **x_content_types; + GMount *mount; new_location = window->details->pending_location; window->details->pending_location = NULL; @@ -1370,6 +1400,13 @@ update_for_new_location (NautilusWindow *window) nautilus_window_show_trash_bar (window); } + x_content_types = nautilus_autorun_get_x_content_types_for_file (file, &mount, FALSE, TRUE); + if (x_content_types != NULL) { + nautilus_window_show_x_content_bar (window, mount, x_content_types); + g_strfreev (x_content_types); + g_object_unref (mount); + } + nautilus_directory_unref (directory); add_extension_extra_widgets (window, window->details->location); diff --git a/src/nautilus-x-content-bar.c b/src/nautilus-x-content-bar.c new file mode 100644 index 000000000..be5869068 --- /dev/null +++ b/src/nautilus-x-content-bar.c @@ -0,0 +1,297 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2006 Paolo Borelli <pborelli@katamail.com> + * + * 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. + * + * Authors: David Zeuthen <davidz@redhat.com> + * Paolo Borelli <pborelli@katamail.com> + * + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> +#include <gtk/gtk.h> +#include <string.h> + +#include "nautilus-x-content-bar.h" +#include <libnautilus-private/nautilus-autorun.h> +#include <libnautilus-private/nautilus-icon-info.h> + +#define NAUTILUS_X_CONTENT_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NAUTILUS_TYPE_X_CONTENT_BAR, NautilusXContentBarPrivate)) + +struct NautilusXContentBarPrivate +{ + GtkWidget *label; + GtkWidget *button; + + char *x_content_type; + GMount *mount; +}; + +enum { + PROP_0, + PROP_MOUNT, + PROP_X_CONTENT_TYPE, +}; + +G_DEFINE_TYPE (NautilusXContentBar, nautilus_x_content_bar, GTK_TYPE_HBOX) + +void +nautilus_x_content_bar_set_x_content_type (NautilusXContentBar *bar, const char *x_content_type) +{ + char *message; + char *description; + GAppInfo *default_app; + + g_free (bar->priv->x_content_type); + bar->priv->x_content_type = g_strdup (x_content_type); + + description = g_content_type_get_description (x_content_type); + + /* Customize greeting for well-known x-content types */ + if (strcmp (x_content_type, "x-content/audio-cdda") == 0) { + message = g_strdup (_("These files are on an Audio CD.")); + } else if (strcmp (x_content_type, "x-content/audio-dvd") == 0) { + message = g_strdup (_("These files are on an Audio DVD.")); + } else if (strcmp (x_content_type, "x-content/video-dvd") == 0) { + message = g_strdup (_("These files are on a Video DVD.")); + } else if (strcmp (x_content_type, "x-content/video-vcd") == 0) { + message = g_strdup (_("These files are on a Video CD.")); + } else if (strcmp (x_content_type, "x-content/video-svcd") == 0) { + message = g_strdup (_("These files are on a Super Video CD.")); + } else if (strcmp (x_content_type, "x-content/image-photocd") == 0) { + message = g_strdup (_("These files are on a Photo CD.")); + } else if (strcmp (x_content_type, "x-content/image-picturecd") == 0) { + message = g_strdup (_("These files are on a Picture CD.")); + } else if (strcmp (x_content_type, "x-content/image-dcf") == 0) { + message = g_strdup (_("The media contains digital photos.")); + } else if (strcmp (x_content_type, "x-content/audio-player") == 0) { + message = g_strdup (_("These files are on a digital audio player.")); + } else if (strcmp (x_content_type, "x-content/software") == 0) { + message = g_strdup (_("The media contains software.")); + } else { + /* fallback to generic greeting */ + message = g_strdup_printf (_("The media has been detected as \"%s\"."), description); + } + + + gtk_label_set_text (GTK_LABEL (bar->priv->label), message); + gtk_widget_show (bar->priv->label); + + /* TODO: We really need a GtkBrowserBackButton-ish widget here.. until then, we only + * show the default application. */ + + default_app = g_app_info_get_default_for_type (x_content_type, FALSE); + if (default_app != NULL) { + char *button_text; + const char *name; + GIcon *icon; + GtkWidget *image; + + icon = g_app_info_get_icon (default_app); + if (icon != NULL) { + GdkPixbuf *pixbuf; + int icon_size; + NautilusIconInfo *icon_info; + icon_size = nautilus_get_icon_size_for_stock_size (GTK_ICON_SIZE_BUTTON); + icon_info = nautilus_icon_info_lookup (icon, icon_size); + pixbuf = nautilus_icon_info_get_pixbuf_at_size (icon_info, icon_size); + image = gtk_image_new_from_pixbuf (pixbuf); + g_object_unref (pixbuf); + g_object_unref (icon_info); + g_object_unref (icon); + } else { + image = NULL; + } + + name = g_app_info_get_name (default_app); + button_text = g_strdup_printf (_("Open %s"), name); + + gtk_button_set_image (GTK_BUTTON (bar->priv->button), image); + gtk_button_set_label (GTK_BUTTON (bar->priv->button), button_text); + gtk_widget_show (bar->priv->button); + g_free (button_text); + g_object_unref (default_app); + } else { + gtk_widget_hide (bar->priv->button); + } + + g_free (message); + g_free (description); +} + +const char * +nautilus_x_content_bar_get_x_content_type (NautilusXContentBar *bar) +{ + return bar->priv->x_content_type; +} + +GMount * +nautilus_x_content_bar_get_mount (NautilusXContentBar *bar) +{ + return bar->priv->mount != NULL ? g_object_ref (bar->priv->mount) : NULL; +} + +void +nautilus_x_content_bar_set_mount (NautilusXContentBar *bar, GMount *mount) +{ + if (bar->priv->mount != NULL) + g_object_unref (bar->priv->mount); + bar->priv->mount = mount != NULL ? g_object_ref (mount) : NULL; +} + + +static void +nautilus_x_content_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusXContentBar *bar; + + bar = NAUTILUS_X_CONTENT_BAR (object); + + switch (prop_id) { + case PROP_MOUNT: + nautilus_x_content_bar_set_mount (bar, G_MOUNT (g_value_get_object (value))); + break; + case PROP_X_CONTENT_TYPE: + nautilus_x_content_bar_set_x_content_type (bar, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nautilus_x_content_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusXContentBar *bar; + + bar = NAUTILUS_X_CONTENT_BAR (object); + + switch (prop_id) { + case PROP_MOUNT: + g_value_set_object (value, bar->priv->mount); + break; + case PROP_X_CONTENT_TYPE: + g_value_set_string (value, bar->priv->x_content_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nautilus_x_content_bar_finalize (GObject *object) +{ + NautilusXContentBar *bar = NAUTILUS_X_CONTENT_BAR (object); + + g_free (bar->priv->x_content_type); + if (bar->priv->mount != NULL) + g_object_unref (bar->priv->mount); + + G_OBJECT_CLASS (nautilus_x_content_bar_parent_class)->finalize (object); +} + +static void +nautilus_x_content_bar_class_init (NautilusXContentBarClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->get_property = nautilus_x_content_bar_get_property; + object_class->set_property = nautilus_x_content_bar_set_property; + object_class->finalize = nautilus_x_content_bar_finalize; + + g_type_class_add_private (klass, sizeof (NautilusXContentBarPrivate)); + + g_object_class_install_property (object_class, + PROP_MOUNT, + g_param_spec_object ( + "mount", + "The GMount to run programs for", + "The GMount to run programs for", + G_TYPE_MOUNT, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + g_object_class_install_property (object_class, + PROP_X_CONTENT_TYPE, + g_param_spec_string ( + "x-content-type", + "The x-content type for the cluebar", + "The x-content type for the cluebar", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); +} + +static void +button_clicked_callback (GtkWidget *button, NautilusXContentBar *bar) +{ + GAppInfo *default_app; + + if (bar->priv->x_content_type == NULL || + bar->priv->mount == NULL) + return; + + default_app = g_app_info_get_default_for_type (bar->priv->x_content_type, FALSE); + if (default_app != NULL) { + nautilus_autorun_launch_for_mount (bar->priv->mount, default_app); + g_object_unref (default_app); + } +} + +static void +nautilus_x_content_bar_init (NautilusXContentBar *bar) +{ + GtkWidget *hbox; + + bar->priv = NAUTILUS_X_CONTENT_BAR_GET_PRIVATE (bar); + + hbox = GTK_WIDGET (bar); + + bar->priv->label = gtk_label_new (NULL); + gtk_box_pack_start (GTK_BOX (bar), bar->priv->label, FALSE, FALSE, 0); + + bar->priv->button = gtk_button_new (); + gtk_box_pack_end (GTK_BOX (hbox), bar->priv->button, FALSE, FALSE, 0); + + g_signal_connect (bar->priv->button, + "clicked", + G_CALLBACK (button_clicked_callback), + bar); +} + +GtkWidget * +nautilus_x_content_bar_new (GMount *mount, + const char *x_content_type) +{ + GObject *bar; + + bar = g_object_new (NAUTILUS_TYPE_X_CONTENT_BAR, + "mount", mount, + "x-content-type", x_content_type, + NULL); + + return GTK_WIDGET (bar); +} diff --git a/src/nautilus-x-content-bar.h b/src/nautilus-x-content-bar.h new file mode 100644 index 000000000..922cb1934 --- /dev/null +++ b/src/nautilus-x-content-bar.h @@ -0,0 +1,67 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2008 Red Hat, Inc. + * Copyright (C) 2006 Paolo Borelli <pborelli@katamail.com> + * + * 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. + * + * Authors: David Zeuthen <davidz@redhat.com> + * Paolo Borelli <pborelli@katamail.com> + * + */ + +#ifndef __NAUTILUS_X_CONTENT_BAR_H +#define __NAUTILUS_X_CONTENT_BAR_H + +#include <gtk/gtk.h> +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define NAUTILUS_TYPE_X_CONTENT_BAR (nautilus_x_content_bar_get_type ()) +#define NAUTILUS_X_CONTENT_BAR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NAUTILUS_TYPE_X_CONTENT_BAR, NautilusXContentBar)) +#define NAUTILUS_X_CONTENT_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NAUTILUS_TYPE_X_CONTENT_BAR, NautilusXContentBarClass)) +#define NAUTILUS_IS_X_CONTENT_BAR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NAUTILUS_TYPE_X_CONTENT_BAR)) +#define NAUTILUS_IS_X_CONTENT_BAR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NAUTILUS_TYPE_X_CONTENT_BAR)) +#define NAUTILUS_X_CONTENT_BAR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NAUTILUS_TYPE_X_CONTENT_BAR, NautilusXContentBarClass)) + +typedef struct NautilusXContentBarPrivate NautilusXContentBarPrivate; + +typedef struct +{ + GtkHBox box; + + NautilusXContentBarPrivate *priv; +} NautilusXContentBar; + +typedef struct +{ + GtkHBoxClass parent_class; +} NautilusXContentBarClass; + +GType nautilus_x_content_bar_get_type (void) G_GNUC_CONST; + +GtkWidget *nautilus_x_content_bar_new (GMount *mount, + const char *x_content_type); +const char *nautilus_x_content_bar_get_x_content_type (NautilusXContentBar *bar); +void nautilus_x_content_bar_set_x_content_type (NautilusXContentBar *bar, + const char *x_content_type); +void nautilus_x_content_bar_set_mount (NautilusXContentBar *bar, + GMount *mount); +GMount *nautilus_x_content_bar_get_mount (NautilusXContentBar *bar); + +G_END_DECLS + +#endif /* __NAUTILUS_X_CONTENT_BAR_H */ |