[gtk+/merge-places-sidebar] Fix compilation
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk+/merge-places-sidebar] Fix compilation
- Date: Mon, 15 Apr 2013 23:38:23 +0000 (UTC)
commit 0ba4952eb1aa57acd76e2627985ce28f0d431614
Author: Federico Mena Quintero <federico gnome org>
Date: Mon Apr 15 18:30:10 2013 -0500
Fix compilation
Signed-off-by: Federico Mena Quintero <federico gnome org>
gtk/gtkfilechooserdefault.c | 7227 +++++++++++++++++++++---------------------
gtk/gtkfilechooserdefault.ui | 7 -
2 files changed, 3611 insertions(+), 3623 deletions(-)
---
diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c
index 55a928d..c6bea39 100644
--- a/gtk/gtkfilechooserdefault.c
+++ b/gtk/gtkfilechooserdefault.c
@@ -275,6 +275,7 @@ typedef struct {
GtkTreeViewColumn *list_name_column;
GtkCellRenderer *list_name_renderer;
+ GtkCellRenderer *list_pixbuf_renderer;
GtkTreeViewColumn *list_mtime_column;
GtkTreeViewColumn *list_size_column;
@@ -541,7 +542,6 @@ static GSList * recent_get_selected_files (GtkFileChooserDefault *impl);
static void set_file_system_backend (GtkFileChooserDefault *impl);
static void unset_file_system_backend (GtkFileChooserDefault *impl);
-
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_TYPE_BOX,
@@ -550,2074 +550,2025 @@ G_DEFINE_TYPE_WITH_CODE (GtkFileChooserDefault, _gtk_file_chooser_default, GTK_T
G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER_EMBED,
gtk_file_chooser_embed_default_iface_init));
-
static void
-add_normal_and_shifted_binding (GtkBindingSet *binding_set,
- guint keyval,
- GdkModifierType modifiers,
- const gchar *signal_name)
+gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
- gtk_binding_entry_add_signal (binding_set,
- keyval, modifiers,
- signal_name, 0);
+ iface->select_file = gtk_file_chooser_default_select_file;
+ iface->unselect_file = gtk_file_chooser_default_unselect_file;
+ iface->select_all = gtk_file_chooser_default_select_all;
+ iface->unselect_all = gtk_file_chooser_default_unselect_all;
+ iface->get_files = gtk_file_chooser_default_get_files;
+ iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
+ iface->get_file_system = gtk_file_chooser_default_get_file_system;
+ iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
+ iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
+ iface->set_current_name = gtk_file_chooser_default_set_current_name;
+ iface->add_filter = gtk_file_chooser_default_add_filter;
+ iface->remove_filter = gtk_file_chooser_default_remove_filter;
+ iface->list_filters = gtk_file_chooser_default_list_filters;
+ iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
+ iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
+ iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
+}
- gtk_binding_entry_add_signal (binding_set,
- keyval, modifiers | GDK_SHIFT_MASK,
- signal_name, 0);
+static void
+gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
+{
+ iface->get_default_size = gtk_file_chooser_default_get_default_size;
+ iface->should_respond = gtk_file_chooser_default_should_respond;
+ iface->initial_focus = gtk_file_chooser_default_initial_focus;
}
-/********************************************************************
- * Class/Instance Initializer *
- ********************************************************************/
+static void
+pending_select_files_free (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ g_slist_free_full (priv->pending_select_files, g_object_unref);
+ priv->pending_select_files = NULL;
+}
static void
-_gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
+pending_select_files_add (GtkFileChooserDefault *impl,
+ GFile *file)
{
- static const guint quick_bookmark_keyvals[10] = {
- GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9,
GDK_KEY_0
- };
- GObjectClass *gobject_class = G_OBJECT_CLASS (class);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
- GtkBindingSet *binding_set;
- int i;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- gobject_class->finalize = gtk_file_chooser_default_finalize;
- gobject_class->constructor = gtk_file_chooser_default_constructor;
- gobject_class->set_property = gtk_file_chooser_default_set_property;
- gobject_class->get_property = gtk_file_chooser_default_get_property;
- gobject_class->dispose = gtk_file_chooser_default_dispose;
+ priv->pending_select_files =
+ g_slist_prepend (priv->pending_select_files, g_object_ref (file));
+}
- widget_class->show_all = gtk_file_chooser_default_show_all;
- widget_class->realize = gtk_file_chooser_default_realize;
- widget_class->map = gtk_file_chooser_default_map;
- widget_class->unmap = gtk_file_chooser_default_unmap;
- widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
- widget_class->style_updated = gtk_file_chooser_default_style_updated;
- widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
+static void
+gtk_file_chooser_default_finalize (GObject *object)
+{
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GSList *l;
- signals[LOCATION_POPUP] =
- g_signal_new_class_handler (I_("location-popup"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (location_popup_handler),
- NULL, NULL,
- _gtk_marshal_VOID__STRING,
- G_TYPE_NONE, 1, G_TYPE_STRING);
+ unset_file_system_backend (impl);
- signals[LOCATION_POPUP_ON_PASTE] =
- g_signal_new_class_handler (I_("location-popup-on-paste"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (location_popup_on_paste_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ g_free (priv->browse_files_last_selected_name);
- signals[LOCATION_TOGGLE_POPUP] =
- g_signal_new_class_handler (I_("location-toggle-popup"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (location_toggle_popup_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ for (l = priv->filters; l; l = l->next)
+ {
+ GtkFileFilter *filter;
- signals[UP_FOLDER] =
- g_signal_new_class_handler (I_("up-folder"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (up_folder_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ filter = GTK_FILE_FILTER (l->data);
+ g_object_unref (filter);
+ }
+ g_slist_free (priv->filters);
- signals[DOWN_FOLDER] =
- g_signal_new_class_handler (I_("down-folder"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (down_folder_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ if (priv->current_filter)
+ g_object_unref (priv->current_filter);
- signals[HOME_FOLDER] =
- g_signal_new_class_handler (I_("home-folder"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (home_folder_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ if (priv->current_volume_file)
+ g_object_unref (priv->current_volume_file);
- signals[DESKTOP_FOLDER] =
- g_signal_new_class_handler (I_("desktop-folder"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (desktop_folder_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ if (priv->current_folder)
+ g_object_unref (priv->current_folder);
- signals[QUICK_BOOKMARK] =
- g_signal_new_class_handler (I_("quick-bookmark"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (quick_bookmark_handler),
- NULL, NULL,
- _gtk_marshal_VOID__INT,
- G_TYPE_NONE, 1, G_TYPE_INT);
+ if (priv->preview_file)
+ g_object_unref (priv->preview_file);
- signals[SHOW_HIDDEN] =
- g_signal_new_class_handler (I_("show-hidden"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (show_hidden_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ if (priv->browse_path_bar_size_group)
+ g_object_unref (priv->browse_path_bar_size_group);
- signals[SEARCH_SHORTCUT] =
- g_signal_new_class_handler (I_("search-shortcut"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (search_shortcut_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ /* Free all the Models we have */
+ stop_loading_and_clear_list_model (impl, FALSE);
+ search_clear_model (impl, FALSE);
+ recent_clear_model (impl, FALSE);
- signals[RECENT_SHORTCUT] =
- g_signal_new_class_handler (I_("recent-shortcut"),
- G_OBJECT_CLASS_TYPE (class),
- G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
- G_CALLBACK (recent_shortcut_handler),
- NULL, NULL,
- _gtk_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ /* stopping the load above should have cleared this */
+ g_assert (priv->load_timeout_id == 0);
- binding_set = gtk_binding_set_by_class (class);
+ g_free (priv->preview_display_name);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_l, GDK_CONTROL_MASK,
- "location-toggle-popup",
- 0);
+ g_free (priv->edited_new_text);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_slash, 0,
- "location-popup",
- 1, G_TYPE_STRING, "/");
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_KP_Divide, 0,
- "location-popup",
- 1, G_TYPE_STRING, "/");
+ impl->priv = NULL;
-#ifdef G_OS_UNIX
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_asciitilde, 0,
- "location-popup",
- 1, G_TYPE_STRING, "~");
-#endif
+ G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
+}
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_v, GDK_CONTROL_MASK,
- "location-popup-on-paste",
- 0);
+/* Shows an error dialog set as transient for the specified window */
+static void
+error_message_with_parent (GtkWindow *parent,
+ const char *msg,
+ const char *detail)
+{
+ GtkWidget *dialog;
- add_normal_and_shifted_binding (binding_set,
- GDK_KEY_Up, GDK_MOD1_MASK,
- "up-folder");
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ msg);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", detail);
- add_normal_and_shifted_binding (binding_set,
- GDK_KEY_KP_Up, GDK_MOD1_MASK,
- "up-folder");
+ if (parent && gtk_window_has_group (parent))
+ gtk_window_group_add_window (gtk_window_get_group (parent),
+ GTK_WINDOW (dialog));
- add_normal_and_shifted_binding (binding_set,
- GDK_KEY_Down, GDK_MOD1_MASK,
- "down-folder");
- add_normal_and_shifted_binding (binding_set,
- GDK_KEY_KP_Down, GDK_MOD1_MASK,
- "down-folder");
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_Home, GDK_MOD1_MASK,
- "home-folder",
- 0);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_KP_Home, GDK_MOD1_MASK,
- "home-folder",
- 0);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_d, GDK_MOD1_MASK,
- "desktop-folder",
- 0);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_h, GDK_CONTROL_MASK,
- "show-hidden",
- 0);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_s, GDK_MOD1_MASK,
- "search-shortcut",
- 0);
- gtk_binding_entry_add_signal (binding_set,
- GDK_KEY_r, GDK_MOD1_MASK,
- "recent-shortcut",
- 0);
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+ GtkWidget *toplevel;
- for (i = 0; i < 10; i++)
- gtk_binding_entry_add_signal (binding_set,
- quick_bookmark_keyvals[i], GDK_MOD1_MASK,
- "quick-bookmark",
- 1, G_TYPE_INT, i);
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (!gtk_widget_is_toplevel (toplevel))
+ return NULL;
+ else
+ return GTK_WINDOW (toplevel);
+}
- _gtk_file_chooser_install_properties (gobject_class);
+/* Shows an error dialog for the file chooser */
+static void
+error_message (GtkFileChooserDefault *impl,
+ const char *msg,
+ const char *detail)
+{
+ error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+}
- g_type_class_add_private (gobject_class, sizeof (GtkFileChooserDefaultPrivate));
+/* Shows a simple error dialog relative to a path. Frees the GError as well. */
+static void
+error_dialog (GtkFileChooserDefault *impl,
+ const char *msg,
+ GFile *file,
+ GError *error)
+{
+ if (error)
+ {
+ char *uri = NULL;
+ char *text;
- /* Bind class to template */
+ if (file)
+ uri = g_file_get_uri (file);
+ text = g_strdup_printf (msg, uri);
+ error_message (impl, text, error->message);
+ g_free (text);
+ g_free (uri);
+ g_error_free (error);
+ }
+}
- gtk_widget_class_set_template_from_resource (widget_class,
- "/org/gtk/libgtk/gtkfilechooserdefault.ui");
+/* Shows an error dialog about not being able to create a folder */
+static void
+error_creating_folder_dialog (GtkFileChooserDefault *impl,
+ GFile *file,
+ GError *error)
+{
+ error_dialog (impl,
+ _("The folder could not be created"),
+ file, error);
+}
- /* A *lot* of widgets that we need to handle .... */
+/* Shows an error about not being able to create a folder because a file with
+ * the same name is already there.
+ */
+static void
+error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
+ GFile *file,
+ GError *error)
+{
+ error_dialog (impl,
+ _("The folder could not be created, as a file with the same "
+ "name already exists. Try using a different name for the "
+ "folder, or rename the file first."),
+ file, error);
+}
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_box);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_hpaned);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_header_box);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_box);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_files_tree_view);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_new_folder_button);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar_hbox);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar_size_group);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_special_mode_icon);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_special_mode_label);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_select_a_folder_info_bar);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_select_a_folder_label);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_select_a_folder_icon);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, filter_combo_hbox);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, filter_combo);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, preview_box);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, extra_align);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_button);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_entry_box);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_label);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_name_column);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_pixbuf_renderer);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_name_renderer);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_mtime_column);
- gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_size_column);
+static void
+error_with_file_under_nonfolder (GtkFileChooserDefault *impl,
+ GFile *parent_file)
+{
+ GError *error;
- /* And a *lot* of callbacks to bind ... */
- gtk_widget_class_bind_callback (widget_class, browse_files_key_press_event_cb);
- gtk_widget_class_bind_callback (widget_class, file_list_drag_drop_cb);
- gtk_widget_class_bind_callback (widget_class, file_list_drag_data_received_cb);
- gtk_widget_class_bind_callback (widget_class, list_popup_menu_cb);
- gtk_widget_class_bind_callback (widget_class, file_list_query_tooltip_cb);
- gtk_widget_class_bind_callback (widget_class, list_button_press_event_cb);
- gtk_widget_class_bind_callback (widget_class, list_row_activated);
- gtk_widget_class_bind_callback (widget_class, file_list_drag_motion_cb);
- gtk_widget_class_bind_callback (widget_class, list_selection_changed);
- gtk_widget_class_bind_callback (widget_class, renderer_editing_canceled_cb);
- gtk_widget_class_bind_callback (widget_class, renderer_edited_cb);
- gtk_widget_class_bind_callback (widget_class, filter_combo_changed);
- gtk_widget_class_bind_callback (widget_class, location_button_toggled_cb);
- gtk_widget_class_bind_callback (widget_class, new_folder_button_clicked);
- gtk_widget_class_bind_callback (widget_class, path_bar_clicked);
- gtk_widget_class_bind_callback (widget_class, places_sidebar_open_location_cb);
- gtk_widget_class_bind_callback (widget_class, places_sidebar_show_error_message_cb);
+ error = NULL;
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
+ _("You need to choose a valid filename."));
+
+ error_dialog (impl,
+ _("Cannot create a file under %s as it is not a folder"),
+ parent_file, error);
}
+/* Shows an error about not being able to select a folder because a file with
+ * the same name is already there.
+ */
static void
-gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
+error_selecting_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
+ GFile *file)
{
- iface->select_file = gtk_file_chooser_default_select_file;
- iface->unselect_file = gtk_file_chooser_default_unselect_file;
- iface->select_all = gtk_file_chooser_default_select_all;
- iface->unselect_all = gtk_file_chooser_default_unselect_all;
- iface->get_files = gtk_file_chooser_default_get_files;
- iface->get_preview_file = gtk_file_chooser_default_get_preview_file;
- iface->get_file_system = gtk_file_chooser_default_get_file_system;
- iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
- iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
- iface->set_current_name = gtk_file_chooser_default_set_current_name;
- iface->add_filter = gtk_file_chooser_default_add_filter;
- iface->remove_filter = gtk_file_chooser_default_remove_filter;
- iface->list_filters = gtk_file_chooser_default_list_filters;
- iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
- iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
- iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
+ error_dialog (impl,
+ _("You may only select folders. The item that you selected is not a folder; "
+ "try using a different item."),
+ file, NULL);
}
+/* Shows an error dialog about not being able to create a filename */
static void
-gtk_file_chooser_embed_default_iface_init (GtkFileChooserEmbedIface *iface)
+error_building_filename_dialog (GtkFileChooserDefault *impl,
+ GError *error)
{
- iface->get_default_size = gtk_file_chooser_default_get_default_size;
- iface->should_respond = gtk_file_chooser_default_should_respond;
- iface->initial_focus = gtk_file_chooser_default_initial_focus;
+ error_dialog (impl, _("Invalid file name"),
+ NULL, error);
}
+/* Shows an error dialog when we cannot switch to a folder */
static void
-post_process_ui (GtkFileChooserDefault *impl)
+error_changing_folder_dialog (GtkFileChooserDefault *impl,
+ GFile *file,
+ GError *error)
{
- GtkTreeSelection *selection;
- GtkStyleContext *context;
- GtkCellRenderer *cell;
- GList *cells;
-
- /* Some qdata, qdata can't be set with GtkBuilder */
- g_object_set_data (G_OBJECT (impl->priv->browse_files_tree_view), "fmq-name", "file_list");
- g_object_set_data (G_OBJECT (impl->priv->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
+ error_dialog (impl, _("The folder contents could not be displayed"),
+ file, error);
+}
- /* Setup file list treeview */
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view));
- gtk_tree_selection_set_select_function (selection,
- list_select_func,
- impl, NULL);
- gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->priv->browse_files_tree_view),
- GDK_BUTTON1_MASK,
- NULL, 0,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gtk_drag_source_add_uri_targets (impl->priv->browse_files_tree_view);
+/* Changes folders, displaying an error dialog if this fails */
+static gboolean
+change_folder_and_display_error (GtkFileChooserDefault *impl,
+ GFile *file,
+ gboolean clear_entry)
+{
+ GError *error;
+ gboolean result;
- gtk_drag_dest_set (impl->priv->browse_files_tree_view,
- GTK_DEST_DEFAULT_ALL,
- NULL, 0,
- GDK_ACTION_COPY | GDK_ACTION_MOVE);
- gtk_drag_dest_add_uri_targets (impl->priv->browse_files_tree_view);
+ g_return_val_if_fail (G_IS_FILE (file), FALSE);
- /* File browser treemodel columns are shared between GtkFileChooser implementations,
- * so we don't set cell renderer attributes in GtkBuilder, but rather keep that
- * in code.
+ /* We copy the path because of this case:
+ *
+ * list_row_activated()
+ * fetches path from model; path belongs to the model (*)
+ * calls change_folder_and_display_error()
+ * calls gtk_file_chooser_set_current_folder_file()
+ * changing folders fails, sets model to NULL, thus freeing the path in (*)
*/
- file_list_set_sort_column_ids (impl);
- update_cell_renderer_attributes (impl);
- /* Get the combo's text renderer and set ellipsize parameters,
- * perhaps GtkComboBoxText should declare the cell renderer
- * as an 'internal-child', then we could configure it in GtkBuilder
- * instead of hard coding it here.
- */
- cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->priv->filter_combo));
- g_assert (cells);
- cell = cells->data;
- g_object_set (G_OBJECT (cell),
- "ellipsize", PANGO_ELLIPSIZE_END,
- NULL);
+ error = NULL;
+ result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry,
&error);
- g_list_free (cells);
+ if (!result)
+ error_changing_folder_dialog (impl, file, error);
- /* Set the GtkPathBar file system backend */
- _gtk_path_bar_set_file_system (GTK_PATH_BAR (impl->priv->browse_path_bar), impl->priv->file_system);
+ return result;
+}
- /* Set the fixed size icon renderer, this requires
- * that priv->icon_size be already setup.
- */
- set_icon_cell_renderer_fixed_size (impl);
+static void
+emit_default_size_changed (GtkFileChooserDefault *impl)
+{
+ profile_msg (" emit default-size-changed start", NULL);
+ g_signal_emit_by_name (impl, "default-size-changed");
+ profile_msg (" emit default-size-changed end", NULL);
}
static void
-_gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
+update_preview_widget_visibility (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefaultPrivate *priv;
-
- profile_start ("start", NULL);
-#ifdef PROFILE_FILE_CHOOSER
- access ("MARK: *** CREATE FILE CHOOSER", F_OK);
-#endif
- impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (impl,
- GTK_TYPE_FILE_CHOOSER_DEFAULT,
- GtkFileChooserDefaultPrivate);
- priv = impl->priv;
-
- priv->local_only = TRUE;
- priv->preview_widget_active = TRUE;
- priv->use_preview_label = TRUE;
- priv->select_multiple = FALSE;
- priv->show_hidden = FALSE;
- priv->show_size_column = TRUE;
- priv->icon_size = FALLBACK_ICON_SIZE;
- priv->load_state = LOAD_EMPTY;
- priv->reload_state = RELOAD_EMPTY;
- priv->pending_select_files = NULL;
- priv->location_mode = LOCATION_MODE_PATH_BAR;
- priv->operation_mode = OPERATION_MODE_BROWSE;
- priv->sort_column = MODEL_COL_NAME;
- priv->sort_order = GTK_SORT_ASCENDING;
- priv->recent_manager = gtk_recent_manager_get_default ();
- priv->create_folders = TRUE;
-
- /* Ensure GTK+ private types used by the template
- * definition before calling gtk_widget_init_template()
- */
- g_type_ensure (GTK_TYPE_PATH_BAR);
- gtk_widget_init_template (GTK_WIDGET (impl));
-
- set_file_system_backend (impl);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- priv->bookmarks_manager = _gtk_bookmarks_manager_new (NULL, NULL);
+ if (priv->use_preview_label)
+ {
+ if (!priv->preview_label)
+ {
+ priv->preview_label = gtk_label_new (priv->preview_display_name);
+ gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_label, FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (priv->preview_box), priv->preview_label, 0);
+ gtk_label_set_ellipsize (GTK_LABEL (priv->preview_label), PANGO_ELLIPSIZE_MIDDLE);
+ gtk_widget_show (priv->preview_label);
+ }
+ }
+ else
+ {
+ if (priv->preview_label)
+ {
+ gtk_widget_destroy (priv->preview_label);
+ priv->preview_label = NULL;
+ }
+ }
- /* Setup various attributes and callbacks in the UI
- * which cannot be done with GtkBuilder.
- */
- post_process_ui (impl);
+ if (priv->preview_widget_active && priv->preview_widget)
+ gtk_widget_show (priv->preview_box);
+ else
+ gtk_widget_hide (priv->preview_box);
- profile_end ("end", NULL);
+ if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
+ emit_default_size_changed (impl);
}
static void
-pending_select_files_free (GtkFileChooserDefault *impl)
+set_preview_widget (GtkFileChooserDefault *impl,
+ GtkWidget *preview_widget)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- g_slist_free_full (priv->pending_select_files, g_object_unref);
- priv->pending_select_files = NULL;
-}
+ if (preview_widget == priv->preview_widget)
+ return;
-static void
-pending_select_files_add (GtkFileChooserDefault *impl,
- GFile *file)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (priv->preview_widget)
+ gtk_container_remove (GTK_CONTAINER (priv->preview_box),
+ priv->preview_widget);
- priv->pending_select_files =
- g_slist_prepend (priv->pending_select_files, g_object_ref (file));
+ priv->preview_widget = preview_widget;
+ if (priv->preview_widget)
+ {
+ gtk_widget_show (priv->preview_widget);
+ gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_widget, TRUE, TRUE, 0);
+ gtk_box_reorder_child (GTK_BOX (priv->preview_box),
+ priv->preview_widget,
+ (priv->use_preview_label && priv->preview_label) ? 1 : 0);
+ }
+
+ update_preview_widget_visibility (impl);
}
-static void
-gtk_file_chooser_default_finalize (GObject *object)
+/* FIXME: GtkFileSystem needs a function to split a remote path
+ * into hostname and path components, or maybe just have a
+ * gtk_file_system_path_get_display_name().
+ *
+ * This function is also used in gtkfilechooserbutton.c
+ */
+gchar *
+_gtk_file_chooser_label_for_file (GFile *file)
{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GSList *l;
-
- unset_file_system_backend (impl);
+ const gchar *path, *start, *end, *p;
+ gchar *uri, *host, *label;
- g_free (priv->browse_files_last_selected_name);
+ uri = g_file_get_uri (file);
- for (l = priv->filters; l; l = l->next)
+ start = strstr (uri, "://");
+ if (start)
{
- GtkFileFilter *filter;
+ start += 3;
+ path = strchr (start, '/');
+ if (path)
+ end = path;
+ else
+ {
+ end = uri + strlen (uri);
+ path = "/";
+ }
- filter = GTK_FILE_FILTER (l->data);
- g_object_unref (filter);
+ /* strip username */
+ p = strchr (start, '@');
+ if (p && p < end)
+ start = p + 1;
+
+ p = strchr (start, ':');
+ if (p && p < end)
+ end = p;
+
+ host = g_strndup (start, end - start);
+
+ /* Translators: the first string is a path and the second string
+ * is a hostname. Nautilus and the panel contain the same string
+ * to translate.
+ */
+ label = g_strdup_printf (_("%1$s on %2$s"), path, host);
+
+ g_free (host);
}
- g_slist_free (priv->filters);
+ else
+ {
+ label = g_strdup (uri);
+ }
+
+ g_free (uri);
- if (priv->current_filter)
- g_object_unref (priv->current_filter);
+ return label;
+}
- if (priv->current_volume_file)
- g_object_unref (priv->current_volume_file);
+/* Callback used when the "New Folder" button is clicked */
+static void
+new_folder_button_clicked (GtkButton *button,
+ GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeIter iter;
+ GtkTreePath *path;
- if (priv->current_folder)
- g_object_unref (priv->current_folder);
+ if (!priv->browse_files_model)
+ return; /* FIXME: this sucks. Disable the New Folder button or something. */
- if (priv->preview_file)
- g_object_unref (priv->preview_file);
+ /* Prevent button from being clicked twice */
+ gtk_widget_set_sensitive (priv->browse_new_folder_button, FALSE);
- if (priv->browse_path_bar_size_group)
- g_object_unref (priv->browse_path_bar_size_group);
+ _gtk_file_system_model_add_editable (priv->browse_files_model, &iter);
- /* Free all the Models we have */
- stop_loading_and_clear_list_model (impl, FALSE);
- search_clear_model (impl, FALSE);
- recent_clear_model (impl, FALSE);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->browse_files_model), &iter);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ path, priv->list_name_column,
+ FALSE, 0.0, 0.0);
- /* stopping the load above should have cleared this */
- g_assert (priv->load_timeout_id == 0);
+ g_object_set (priv->list_name_renderer, "editable", TRUE, NULL);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ path,
+ priv->list_name_column,
+ TRUE);
- g_free (priv->preview_display_name);
+ gtk_tree_path_free (path);
+}
- g_free (priv->edited_new_text);
+static GSource *
+add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback)
+{
+ GSource *source;
- impl->priv = NULL;
+ source = g_idle_source_new ();
+ g_source_set_closure (source,
+ g_cclosure_new_object (callback, G_OBJECT (impl)));
+ g_source_attach (source, NULL);
- G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->finalize (object);
+ return source;
}
-/* Shows an error dialog set as transient for the specified window */
-static void
-error_message_with_parent (GtkWindow *parent,
- const char *msg,
- const char *detail)
+/* Idle handler for creating a new folder after editing its name cell, or for
+ * canceling the editing.
+ */
+static gboolean
+edited_idle_cb (GtkFileChooserDefault *impl)
{
- GtkWidget *dialog;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- dialog = gtk_message_dialog_new (parent,
- GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_OK,
- "%s",
- msg);
- gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
- "%s", detail);
+ gdk_threads_enter ();
+
+ g_source_destroy (priv->edited_idle);
+ priv->edited_idle = NULL;
- if (parent && gtk_window_has_group (parent))
- gtk_window_group_add_window (gtk_window_get_group (parent),
- GTK_WINDOW (dialog));
+ _gtk_file_system_model_remove_editable (priv->browse_files_model);
+ g_object_set (priv->list_name_renderer, "editable", FALSE, NULL);
- gtk_dialog_run (GTK_DIALOG (dialog));
- gtk_widget_destroy (dialog);
-}
+ gtk_widget_set_sensitive (priv->browse_new_folder_button, TRUE);
-/* Returns a toplevel GtkWindow, or NULL if none */
-static GtkWindow *
-get_toplevel (GtkWidget *widget)
-{
- GtkWidget *toplevel;
+ if (priv->edited_new_text /* not cancelled? */
+ && (strlen (priv->edited_new_text) != 0)
+ && (strcmp (priv->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is
empty or has not been edited */
+ {
+ GError *error = NULL;
+ GFile *file;
- toplevel = gtk_widget_get_toplevel (widget);
- if (!gtk_widget_is_toplevel (toplevel))
- return NULL;
- else
- return GTK_WINDOW (toplevel);
-}
+ file = g_file_get_child_for_display_name (priv->current_folder,
+ priv->edited_new_text,
+ &error);
+ if (file)
+ {
+ GError *error = NULL;
-/* Shows an error dialog for the file chooser */
-static void
-error_message (GtkFileChooserDefault *impl,
- const char *msg,
- const char *detail)
-{
- error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+ if (g_file_make_directory (file, NULL, &error))
+ change_folder_and_display_error (impl, file, FALSE);
+ else
+ error_creating_folder_dialog (impl, file, error);
+
+ g_object_unref (file);
+ }
+ else
+ error_creating_folder_dialog (impl, file, error);
+
+ g_free (priv->edited_new_text);
+ priv->edited_new_text = NULL;
+ }
+
+ gdk_threads_leave ();
+
+ return FALSE;
}
-/* Shows a simple error dialog relative to a path. Frees the GError as well. */
static void
-error_dialog (GtkFileChooserDefault *impl,
- const char *msg,
- GFile *file,
- GError *error)
+queue_edited_idle (GtkFileChooserDefault *impl,
+ const gchar *new_text)
{
- if (error)
- {
- char *uri = NULL;
- char *text;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (file)
- uri = g_file_get_uri (file);
- text = g_strdup_printf (msg, uri);
- error_message (impl, text, error->message);
- g_free (text);
- g_free (uri);
- g_error_free (error);
- }
+ /* We create the folder in an idle handler so that we don't modify the tree
+ * just now.
+ */
+
+ if (!priv->edited_idle)
+ priv->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb));
+
+ g_free (priv->edited_new_text);
+ priv->edited_new_text = g_strdup (new_text);
}
-/* Shows an error dialog about not being able to create a folder */
+/* Callback used from the text cell renderer when the new folder is named */
static void
-error_creating_folder_dialog (GtkFileChooserDefault *impl,
- GFile *file,
- GError *error)
+renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
+ const gchar *path,
+ const gchar *new_text,
+ GtkFileChooserDefault *impl)
{
- error_dialog (impl,
- _("The folder could not be created"),
- file, error);
+ /* work around bug #154921 */
+ g_object_set (cell_renderer_text,
+ "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
+ queue_edited_idle (impl, new_text);
}
-/* Shows an error about not being able to create a folder because a file with
- * the same name is already there.
+/* Callback used from the text cell renderer when the new folder edition gets
+ * canceled.
*/
static void
-error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
- GFile *file,
- GError *error)
+renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
+ GtkFileChooserDefault *impl)
{
- error_dialog (impl,
- _("The folder could not be created, as a file with the same "
- "name already exists. Try using a different name for the "
- "folder, or rename the file first."),
- file, error);
+ /* work around bug #154921 */
+ g_object_set (cell_renderer_text,
+ "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
+ queue_edited_idle (impl, NULL);
}
+
+struct selection_check_closure {
+ GtkFileChooserDefault *impl;
+ int num_selected;
+ gboolean all_files;
+ gboolean all_folders;
+};
+
+/* Used from gtk_tree_selection_selected_foreach() */
static void
-error_with_file_under_nonfolder (GtkFileChooserDefault *impl,
- GFile *parent_file)
+selection_check_foreach_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
{
- GError *error;
+ struct selection_check_closure *closure;
+ gboolean is_folder;
+ GFile *file;
- error = NULL;
- g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
- _("You need to choose a valid filename."));
+ gtk_tree_model_get (model, iter,
+ MODEL_COL_FILE, &file,
+ MODEL_COL_IS_FOLDER, &is_folder,
+ -1);
- error_dialog (impl,
- _("Cannot create a file under %s as it is not a folder"),
- parent_file, error);
-}
+ if (file == NULL)
+ return;
-/* Shows an error about not being able to select a folder because a file with
- * the same name is already there.
- */
-static void
-error_selecting_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
- GFile *file)
-{
- error_dialog (impl,
- _("You may only select folders. The item that you selected is not a folder; "
- "try using a different item."),
- file, NULL);
-}
+ g_object_unref (file);
-/* Shows an error dialog about not being able to create a filename */
-static void
-error_building_filename_dialog (GtkFileChooserDefault *impl,
- GError *error)
-{
- error_dialog (impl, _("Invalid file name"),
- NULL, error);
+ closure = data;
+ closure->num_selected++;
+
+ closure->all_folders = closure->all_folders && is_folder;
+ closure->all_files = closure->all_files && !is_folder;
}
-/* Shows an error dialog when we cannot switch to a folder */
+/* Checks whether the selected items in the file list are all files or all folders */
static void
-error_changing_folder_dialog (GtkFileChooserDefault *impl,
- GFile *file,
- GError *error)
+selection_check (GtkFileChooserDefault *impl,
+ gint *num_selected,
+ gboolean *all_files,
+ gboolean *all_folders)
{
- error_dialog (impl, _("The folder contents could not be displayed"),
- file, error);
-}
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ struct selection_check_closure closure;
+ GtkTreeSelection *selection;
-/* Changes folders, displaying an error dialog if this fails */
-static gboolean
-change_folder_and_display_error (GtkFileChooserDefault *impl,
- GFile *file,
- gboolean clear_entry)
-{
- GError *error;
- gboolean result;
+ closure.impl = impl;
+ closure.num_selected = 0;
+ closure.all_files = TRUE;
+ closure.all_folders = TRUE;
- g_return_val_if_fail (G_IS_FILE (file), FALSE);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ gtk_tree_selection_selected_foreach (selection,
+ selection_check_foreach_cb,
+ &closure);
- /* We copy the path because of this case:
- *
- * list_row_activated()
- * fetches path from model; path belongs to the model (*)
- * calls change_folder_and_display_error()
- * calls gtk_file_chooser_set_current_folder_file()
- * changing folders fails, sets model to NULL, thus freeing the path in (*)
- */
+ g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
- error = NULL;
- result = gtk_file_chooser_default_update_current_folder (GTK_FILE_CHOOSER (impl), file, TRUE, clear_entry,
&error);
+ if (num_selected)
+ *num_selected = closure.num_selected;
- if (!result)
- error_changing_folder_dialog (impl, file, error);
+ if (all_files)
+ *all_files = closure.all_files;
- return result;
+ if (all_folders)
+ *all_folders = closure.all_folders;
}
-static void
-emit_default_size_changed (GtkFileChooserDefault *impl)
+static gboolean
+file_is_recent_uri (GFile *file)
{
- profile_msg (" emit default-size-changed start", NULL);
- g_signal_emit_by_name (impl, "default-size-changed");
- profile_msg (" emit default-size-changed end", NULL);
+ GFile *recent;
+ gboolean same;
+
+ recent = g_file_new_for_uri ("recent:///");
+ same = g_file_equal (file, recent);
+ g_object_unref (recent);
+
+ return same;
}
static void
-update_preview_widget_visibility (GtkFileChooserDefault *impl)
+places_sidebar_open_location_cb (GtkPlacesSidebar *sidebar, GFile *location, GtkPlacesOpenFlags open_flags,
GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ gboolean clear_entry;
- if (priv->use_preview_label)
- {
- if (!priv->preview_label)
- {
- priv->preview_label = gtk_label_new (priv->preview_display_name);
- gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_label, FALSE, FALSE, 0);
- gtk_box_reorder_child (GTK_BOX (priv->preview_box), priv->preview_label, 0);
- gtk_label_set_ellipsize (GTK_LABEL (priv->preview_label), PANGO_ELLIPSIZE_MIDDLE);
- gtk_widget_show (priv->preview_label);
- }
- }
+ /* In the Save modes, we want to preserve what the uesr typed in the filename
+ * entry, so that he may choose another folder without erasing his typed name.
+ */
+ if (priv->location_entry
+ && !(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
+ clear_entry = TRUE;
else
- {
- if (priv->preview_label)
- {
- gtk_widget_destroy (priv->preview_label);
- priv->preview_label = NULL;
- }
- }
+ clear_entry = FALSE;
- if (priv->preview_widget_active && priv->preview_widget)
- gtk_widget_show (priv->preview_box);
- else
- gtk_widget_hide (priv->preview_box);
+ /* FIXME-places-sidebar:
+ *
+ * GtkPlacesSidebar doesn't have a Search item anymore. We should put that function in a toolbar-like
button, like
+ * in Nautilus, and do operation_mode_set (impl, OPERATION_MODE_SEARCH);
+ */
- if (!gtk_widget_get_mapped (GTK_WIDGET (impl)))
- emit_default_size_changed (impl);
+ if (file_is_recent_uri (location))
+ operation_mode_set (impl, OPERATION_MODE_RECENT);
+ else
+ change_folder_and_display_error (impl, location, clear_entry);
}
+/* Callback used when the places sidebar needs us to display an error message */
static void
-set_preview_widget (GtkFileChooserDefault *impl,
- GtkWidget *preview_widget)
+places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar,
+ const char *primary,
+ const char *secondary,
+ GtkFileChooserDefault *impl)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- if (preview_widget == priv->preview_widget)
- return;
+ error_message (impl, primary, secondary);
+}
- if (priv->preview_widget)
- gtk_container_remove (GTK_CONTAINER (priv->preview_box),
- priv->preview_widget);
+static gboolean
+key_is_left_or_right (GdkEventKey *event)
+{
+ guint modifiers;
- priv->preview_widget = preview_widget;
- if (priv->preview_widget)
- {
- gtk_widget_show (priv->preview_widget);
- gtk_box_pack_start (GTK_BOX (priv->preview_box), priv->preview_widget, TRUE, TRUE, 0);
- gtk_box_reorder_child (GTK_BOX (priv->preview_box),
- priv->preview_widget,
- (priv->use_preview_label && priv->preview_label) ? 1 : 0);
- }
+ modifiers = gtk_accelerator_get_default_mod_mask ();
- update_preview_widget_visibility (impl);
+ return ((event->keyval == GDK_KEY_Right
+ || event->keyval == GDK_KEY_KP_Right
+ || event->keyval == GDK_KEY_Left
+ || event->keyval == GDK_KEY_KP_Left)
+ && (event->state & modifiers) == 0);
}
-/* FIXME: GtkFileSystem needs a function to split a remote path
- * into hostname and path components, or maybe just have a
- * gtk_file_system_path_get_display_name().
- *
- * This function is also used in gtkfilechooserbutton.c
+/* Handles key press events on the file list, so that we can trap Enter to
+ * activate the default button on our own. Also, checks to see if '/' has been
+ * pressed.
*/
-gchar *
-_gtk_file_chooser_label_for_file (GFile *file)
+static gboolean
+browse_files_key_press_event_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer data)
{
- const gchar *path, *start, *end, *p;
- gchar *uri, *host, *label;
+ GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GdkModifierType no_text_input_mask;
- uri = g_file_get_uri (file);
+ no_text_input_mask =
+ gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
- start = strstr (uri, "://");
- if (start)
+ if ((event->keyval == GDK_KEY_slash
+ || event->keyval == GDK_KEY_KP_Divide
+#ifdef G_OS_UNIX
+ || event->keyval == GDK_KEY_asciitilde
+#endif
+ ) && !(event->state & no_text_input_mask))
{
- start += 3;
- path = strchr (start, '/');
- if (path)
- end = path;
- else
- {
- end = uri + strlen (uri);
- path = "/";
- }
+ location_popup_handler (impl, event->string);
+ return TRUE;
+ }
- /* strip username */
- p = strchr (start, '@');
- if (p && p < end)
- start = p + 1;
-
- p = strchr (start, ':');
- if (p && p < end)
- end = p;
-
- host = g_strndup (start, end - start);
-
- /* Translators: the first string is a path and the second string
- * is a hostname. Nautilus and the panel contain the same string
- * to translate.
- */
- label = g_strdup_printf (_("%1$s on %2$s"), path, host);
-
- g_free (host);
+ if (key_is_left_or_right (event))
+ {
+ gtk_widget_grab_focus (priv->places_sidebar);
+ return TRUE;
}
- else
+
+ if ((event->keyval == GDK_KEY_Return
+ || event->keyval == GDK_KEY_ISO_Enter
+ || event->keyval == GDK_KEY_KP_Enter
+ || event->keyval == GDK_KEY_space
+ || event->keyval == GDK_KEY_KP_Space)
+ && !(event->state & gtk_accelerator_get_default_mod_mask ())
+ && !(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
{
- label = g_strdup (uri);
+ GtkWindow *window;
+
+ window = get_toplevel (widget);
+ if (window)
+ {
+ GtkWidget *default_widget, *focus_widget;
+
+ default_widget = gtk_window_get_default_widget (window);
+ focus_widget = gtk_window_get_focus (window);
+
+ if (widget != default_widget &&
+ !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
+ {
+ gtk_window_activate_default (window);
+
+ return TRUE;
+ }
+ }
}
-
- g_free (uri);
- return label;
+ return FALSE;
}
-/* Callback used when the "New Folder" button is clicked */
+/* Callback used when the file list's popup menu is detached */
static void
-new_folder_button_clicked (GtkButton *button,
- GtkFileChooserDefault *impl)
+popup_menu_detach_cb (GtkWidget *attach_widget,
+ GtkMenu *menu)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeIter iter;
- GtkTreePath *path;
-
- if (!priv->browse_files_model)
- return; /* FIXME: this sucks. Disable the New Folder button or something. */
-
- /* Prevent button from being clicked twice */
- gtk_widget_set_sensitive (priv->browse_new_folder_button, FALSE);
-
- _gtk_file_system_model_add_editable (priv->browse_files_model, &iter);
+ GtkFileChooserDefault *impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
+ GtkFileChooserDefaultPrivate *priv;
- path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->browse_files_model), &iter);
- gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->browse_files_tree_view),
- path, priv->list_name_column,
- FALSE, 0.0, 0.0);
+ g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
- g_object_set (priv->list_name_renderer, "editable", TRUE, NULL);
- gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view),
- path,
- priv->list_name_column,
- TRUE);
+ priv = impl->priv;
- gtk_tree_path_free (path);
+ priv->browse_files_popup_menu = NULL;
+ priv->browse_files_popup_menu_add_shortcut_item = NULL;
+ priv->browse_files_popup_menu_hidden_files_item = NULL;
+ priv->browse_files_popup_menu_copy_file_location_item = NULL;
}
-static GSource *
-add_idle_while_impl_is_alive (GtkFileChooserDefault *impl, GCallback callback)
+/* Callback used from gtk_tree_selection_selected_foreach(); adds a bookmark for
+ * each selected item in the file list.
+ */
+static void
+add_bookmark_foreach_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
{
- GSource *source;
+ GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GFile *file;
- source = g_idle_source_new ();
- g_source_set_closure (source,
- g_cclosure_new_object (callback, G_OBJECT (impl)));
- g_source_attach (source, NULL);
+ gtk_tree_model_get (model, iter,
+ MODEL_COL_FILE, &file,
+ -1);
- return source;
+ _gtk_bookmarks_manager_insert_bookmark (priv->bookmarks_manager, file, 0, NULL); /* NULL-GError */
+
+ g_object_unref (file);
}
-/* Idle handler for creating a new folder after editing its name cell, or for
- * canceling the editing.
- */
-static gboolean
-edited_idle_cb (GtkFileChooserDefault *impl)
+/* Callback used when the "Add to Bookmarks" menu item is activated */
+static void
+add_to_shortcuts_cb (GtkMenuItem *item,
+ GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeSelection *selection;
- gdk_threads_enter ();
-
- g_source_destroy (priv->edited_idle);
- priv->edited_idle = NULL;
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- _gtk_file_system_model_remove_editable (priv->browse_files_model);
- g_object_set (priv->list_name_renderer, "editable", FALSE, NULL);
+ gtk_tree_selection_selected_foreach (selection,
+ add_bookmark_foreach_cb,
+ impl);
+}
- gtk_widget_set_sensitive (priv->browse_new_folder_button, TRUE);
+/* callback used to set data to clipboard */
+static void
+copy_file_get_cb (GtkClipboard *clipboard,
+ GtkSelectionData *selection_data,
+ guint info,
+ gpointer data)
+{
+ GSList *selected_files = data;
- if (priv->edited_new_text /* not cancelled? */
- && (strlen (priv->edited_new_text) != 0)
- && (strcmp (priv->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is
empty or has not been edited */
+ if (selected_files)
{
- GError *error = NULL;
- GFile *file;
+ gint num_files = g_slist_length (selected_files);
+ gchar **uris;
+ gint i;
+ GSList *l;
- file = g_file_get_child_for_display_name (priv->current_folder,
- priv->edited_new_text,
- &error);
- if (file)
- {
- GError *error = NULL;
-
- if (g_file_make_directory (file, NULL, &error))
- change_folder_and_display_error (impl, file, FALSE);
- else
- error_creating_folder_dialog (impl, file, error);
-
- g_object_unref (file);
- }
- else
- error_creating_folder_dialog (impl, file, error);
-
- g_free (priv->edited_new_text);
- priv->edited_new_text = NULL;
- }
+ uris = g_new (gchar *, num_files + 1);
+ uris[num_files] = NULL; /* null terminator */
- gdk_threads_leave ();
+ i = 0;
- return FALSE;
-}
+ for (l = selected_files; l; l = l->next)
+ {
+ GFile *file = (GFile *) l->data;
-static void
-queue_edited_idle (GtkFileChooserDefault *impl,
- const gchar *new_text)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (info == SELECTION_URI)
+ uris[i] = g_file_get_uri (file);
+ else /* if (info == SELECTION_TEXT) - let this be the fallback */
+ uris[i] = g_file_get_parse_name (file);
- /* We create the folder in an idle handler so that we don't modify the tree
- * just now.
- */
+ i++;
+ }
- if (!priv->edited_idle)
- priv->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb));
+ if (info == SELECTION_URI)
+ gtk_selection_data_set_uris (selection_data, uris);
+ else /* if (info == SELECTION_TEXT) - let this be the fallback */
+ {
+ char *str = g_strjoinv (" ", uris);
+ gtk_selection_data_set_text (selection_data, str, -1);
+ g_free (str);
+ }
- g_free (priv->edited_new_text);
- priv->edited_new_text = g_strdup (new_text);
+ g_strfreev (uris);
+ }
}
-/* Callback used from the text cell renderer when the new folder is named */
+/* callback used to clear the clipboard data */
static void
-renderer_edited_cb (GtkCellRendererText *cell_renderer_text,
- const gchar *path,
- const gchar *new_text,
- GtkFileChooserDefault *impl)
+copy_file_clear_cb (GtkClipboard *clipboard,
+ gpointer data)
{
- /* work around bug #154921 */
- g_object_set (cell_renderer_text,
- "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
- queue_edited_idle (impl, new_text);
+ GSList *selected_files = data;
+
+ g_slist_foreach (selected_files, (GFunc) g_object_unref, NULL);
+ g_slist_free (selected_files);
}
-/* Callback used from the text cell renderer when the new folder edition gets
- * canceled.
- */
+/* Callback used when the "Copy file’s location" menu item is activated */
static void
-renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text,
- GtkFileChooserDefault *impl)
+copy_file_location_cb (GtkMenuItem *item,
+ GtkFileChooserDefault *impl)
{
- /* work around bug #154921 */
- g_object_set (cell_renderer_text,
- "mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
- queue_edited_idle (impl, NULL);
-}
-
+ GSList *selected_files = NULL;
-struct selection_check_closure {
- GtkFileChooserDefault *impl;
- int num_selected;
- gboolean all_files;
- gboolean all_folders;
-};
+ selected_files = search_get_selected_files (impl);
-/* Used from gtk_tree_selection_selected_foreach() */
-static void
-selection_check_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- struct selection_check_closure *closure;
- gboolean is_folder;
- GFile *file;
+ if (selected_files)
+ {
+ GtkClipboard *clipboard;
+ GtkTargetList *target_list;
+ GtkTargetEntry *targets;
+ int n_targets;
- gtk_tree_model_get (model, iter,
- MODEL_COL_FILE, &file,
- MODEL_COL_IS_FOLDER, &is_folder,
- -1);
+ clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl), GDK_SELECTION_CLIPBOARD);
- if (file == NULL)
- return;
+ target_list = gtk_target_list_new (NULL, 0);
+ gtk_target_list_add_text_targets (target_list, SELECTION_TEXT);
+ gtk_target_list_add_uri_targets (target_list, SELECTION_URI);
- g_object_unref (file);
+ targets = gtk_target_table_new_from_list (target_list, &n_targets);
+ gtk_target_list_unref (target_list);
- closure = data;
- closure->num_selected++;
+ gtk_clipboard_set_with_data (clipboard, targets, n_targets,
+ copy_file_get_cb,
+ copy_file_clear_cb,
+ selected_files);
- closure->all_folders = closure->all_folders && is_folder;
- closure->all_files = closure->all_files && !is_folder;
+ gtk_target_table_free (targets, n_targets);
+ }
}
-/* Checks whether the selected items in the file list are all files or all folders */
+/* Callback used when the "Visit this file" menu item is activated */
static void
-selection_check (GtkFileChooserDefault *impl,
- gint *num_selected,
- gboolean *all_files,
- gboolean *all_folders)
+visit_file_cb (GtkMenuItem *item,
+ GtkFileChooserDefault *impl)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- struct selection_check_closure closure;
- GtkTreeSelection *selection;
-
- closure.impl = impl;
- closure.num_selected = 0;
- closure.all_files = TRUE;
- closure.all_folders = TRUE;
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection,
- selection_check_foreach_cb,
- &closure);
+ GSList *files;
- g_assert (closure.num_selected == 0 || !(closure.all_files && closure.all_folders));
+ files = search_get_selected_files (impl);
- if (num_selected)
- *num_selected = closure.num_selected;
+ /* Sigh, just use the first one */
+ if (files)
+ {
+ GFile *file = files->data;
- if (all_files)
- *all_files = closure.all_files;
+ gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL); /* NULL-GError */
+ }
- if (all_folders)
- *all_folders = closure.all_folders;
+ g_slist_foreach (files, (GFunc) g_object_unref, NULL);
+ g_slist_free (files);
}
-static gboolean
-file_is_recent_uri (GFile *file)
+/* callback used when the "Show Hidden Files" menu item is toggled */
+static void
+show_hidden_toggled_cb (GtkCheckMenuItem *item,
+ GtkFileChooserDefault *impl)
{
- GFile *recent;
- gboolean same;
-
- recent = g_file_new_for_uri ("recent:///");
- same = g_file_equal (file, recent);
- g_object_unref (recent);
-
- return same;
+ g_object_set (impl,
+ "show-hidden", gtk_check_menu_item_get_active (item),
+ NULL);
}
+/* Callback used when the "Show Size Column" menu item is toggled */
static void
-places_sidebar_open_location_cb (GtkPlacesSidebar *sidebar, GFile *location, GtkPlacesOpenFlags open_flags,
GtkFileChooserDefault *impl)
+show_size_column_toggled_cb (GtkCheckMenuItem *item,
+ GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gboolean clear_entry;
-
- /* In the Save modes, we want to preserve what the uesr typed in the filename
- * entry, so that he may choose another folder without erasing his typed name.
- */
- if (priv->location_entry
- && !(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
- clear_entry = TRUE;
- else
- clear_entry = FALSE;
- /* FIXME-places-sidebar:
- *
- * GtkPlacesSidebar doesn't have a Search item anymore. We should put that function in a toolbar-like
button, like
- * in Nautilus, and do operation_mode_set (impl, OPERATION_MODE_SEARCH);
- */
+ priv->show_size_column = gtk_check_menu_item_get_active (item);
- if (file_is_recent_uri (location))
- operation_mode_set (impl, OPERATION_MODE_RECENT);
- else
- change_folder_and_display_error (impl, location, clear_entry);
+ gtk_tree_view_column_set_visible (priv->list_size_column,
+ priv->show_size_column);
}
-/* Callback used when the places sidebar needs us to display an error message */
+/* Shows an error dialog about not being able to select a dragged file */
static void
-places_sidebar_show_error_message_cb (GtkPlacesSidebar *sidebar,
- const char *primary,
- const char *secondary,
- GtkFileChooserDefault *impl)
+error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
+ GFile *file,
+ GError *error)
{
- error_message (impl, primary, secondary);
+ error_dialog (impl,
+ _("Could not select file"),
+ file, error);
}
-static gboolean
-key_is_left_or_right (GdkEventKey *event)
+static void
+file_list_drag_data_select_uris (GtkFileChooserDefault *impl,
+ gchar **uris)
{
- guint modifiers;
+ int i;
+ char *uri;
+ GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
- modifiers = gtk_accelerator_get_default_mod_mask ();
+ for (i = 1; uris[i]; i++)
+ {
+ GFile *file;
+ GError *error = NULL;
- return ((event->keyval == GDK_KEY_Right
- || event->keyval == GDK_KEY_KP_Right
- || event->keyval == GDK_KEY_Left
- || event->keyval == GDK_KEY_KP_Left)
- && (event->state & modifiers) == 0);
+ uri = uris[i];
+ file = g_file_new_for_uri (uri);
+
+ gtk_file_chooser_default_select_file (chooser, file, &error);
+ if (error)
+ error_selecting_dragged_file_dialog (impl, file, error);
+
+ g_object_unref (file);
+ }
}
-/* Handles key press events on the file list, so that we can trap Enter to
- * activate the default button on our own. Also, checks to see if '/' has been
- * pressed.
- */
-static gboolean
-browse_files_key_press_event_cb (GtkWidget *widget,
- GdkEventKey *event,
- gpointer data)
+struct FileListDragData
{
- GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data;
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GdkModifierType no_text_input_mask;
+ GtkFileChooserDefault *impl;
+ gchar **uris;
+ GFile *file;
+};
- no_text_input_mask =
- gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_NO_TEXT_INPUT);
+static void
+file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
+ GFileInfo *info,
+ const GError *error,
+ gpointer user_data)
+{
+ gboolean cancelled = g_cancellable_is_cancelled (cancellable);
+ struct FileListDragData *data = user_data;
+ GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
+ GtkFileChooserDefaultPrivate *priv = data->impl->priv;
- if ((event->keyval == GDK_KEY_slash
- || event->keyval == GDK_KEY_KP_Divide
-#ifdef G_OS_UNIX
- || event->keyval == GDK_KEY_asciitilde
-#endif
- ) && !(event->state & no_text_input_mask))
+ if (cancellable != priv->file_list_drag_data_received_cancellable)
+ goto out;
+
+ priv->file_list_drag_data_received_cancellable = NULL;
+
+ if (cancelled || error)
+ goto out;
+
+ if ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
+ data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
+ change_folder_and_display_error (data->impl, data->file, FALSE);
+ else
{
- location_popup_handler (impl, event->string);
- return TRUE;
+ GError *error = NULL;
+
+ gtk_file_chooser_default_unselect_all (chooser);
+ gtk_file_chooser_default_select_file (chooser, data->file, &error);
+ if (error)
+ error_selecting_dragged_file_dialog (data->impl, data->file, error);
+ else
+ browse_files_center_selected_row (data->impl);
}
- if (key_is_left_or_right (event))
+ if (priv->select_multiple)
+ file_list_drag_data_select_uris (data->impl, data->uris);
+
+out:
+ g_object_unref (data->impl);
+ g_strfreev (data->uris);
+ g_object_unref (data->file);
+ g_free (data);
+
+ g_object_unref (cancellable);
+}
+
+static void
+file_list_drag_data_received_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time_,
+ gpointer data)
+{
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ gchar **uris;
+ char *uri;
+ GFile *file;
+
+ /* Allow only drags from other widgets; see bug #533891. */
+ if (gtk_drag_get_source_widget (context) == widget)
{
- gtk_widget_grab_focus (priv->places_sidebar);
- return TRUE;
+ g_signal_stop_emission_by_name (widget, "drag-data-received");
+ return;
}
- if ((event->keyval == GDK_KEY_Return
- || event->keyval == GDK_KEY_ISO_Enter
- || event->keyval == GDK_KEY_KP_Enter
- || event->keyval == GDK_KEY_space
- || event->keyval == GDK_KEY_KP_Space)
- && !(event->state & gtk_accelerator_get_default_mod_mask ())
- && !(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
+ /* Parse the text/uri-list string, navigate to the first one */
+ uris = gtk_selection_data_get_uris (selection_data);
+ if (uris && uris[0])
{
- GtkWindow *window;
+ struct FileListDragData *data;
- window = get_toplevel (widget);
- if (window)
- {
- GtkWidget *default_widget, *focus_widget;
+ uri = uris[0];
+ file = g_file_new_for_uri (uri);
- default_widget = gtk_window_get_default_widget (window);
- focus_widget = gtk_window_get_focus (window);
+ data = g_new0 (struct FileListDragData, 1);
+ data->impl = g_object_ref (impl);
+ data->uris = uris;
+ data->file = file;
- if (widget != default_widget &&
- !(widget == focus_widget && (!default_widget || !gtk_widget_get_sensitive (default_widget))))
- {
- gtk_window_activate_default (window);
+ if (priv->file_list_drag_data_received_cancellable)
+ g_cancellable_cancel (priv->file_list_drag_data_received_cancellable);
- return TRUE;
- }
- }
+ priv->file_list_drag_data_received_cancellable =
+ _gtk_file_system_get_info (priv->file_system, file,
+ "standard::type",
+ file_list_drag_data_received_get_info_cb,
+ data);
}
- return FALSE;
+ g_signal_stop_emission_by_name (widget, "drag-data-received");
}
-/* Callback used when the file list's popup menu is detached */
-static void
-popup_menu_detach_cb (GtkWidget *attach_widget,
- GtkMenu *menu)
+/* Don't do anything with the drag_drop signal */
+static gboolean
+file_list_drag_drop_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_,
+ GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = g_object_get_data (G_OBJECT (attach_widget), "GtkFileChooserDefault");
- GtkFileChooserDefaultPrivate *priv;
-
- g_assert (GTK_IS_FILE_CHOOSER_DEFAULT (impl));
-
- priv = impl->priv;
+ g_signal_stop_emission_by_name (widget, "drag-drop");
+ return TRUE;
+}
- priv->browse_files_popup_menu = NULL;
- priv->browse_files_popup_menu_add_shortcut_item = NULL;
- priv->browse_files_popup_menu_hidden_files_item = NULL;
- priv->browse_files_popup_menu_copy_file_location_item = NULL;
+/* Disable the normal tree drag motion handler, it makes it look like you're
+ dropping the dragged item onto a tree item */
+static gboolean
+file_list_drag_motion_cb (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_,
+ GtkFileChooserDefault *impl)
+{
+ g_signal_stop_emission_by_name (widget, "drag-motion");
+ return TRUE;
}
-/* Callback used from gtk_tree_selection_selected_foreach(); adds a bookmark for
- * each selected item in the file list.
+/* Sensitizes the "Copy file’s location" context menu item if there is actually
+ * a selection active.
*/
static void
-add_bookmark_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
+check_copy_file_location_sensitivity (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = (GtkFileChooserDefault *) data;
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GFile *file;
+ GtkTreeSelection *selection;
+ gboolean active;
- gtk_tree_model_get (model, iter,
- MODEL_COL_FILE, &file,
- -1);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ if (gtk_tree_selection_count_selected_rows (selection) == 0)
+ active = FALSE;
+ else
+ active = TRUE;
- _gtk_bookmarks_manager_insert_bookmark (priv->bookmarks_manager, file, 0, NULL); /* NULL-GError */
+ if (priv->browse_files_popup_menu_copy_file_location_item)
+ gtk_widget_set_sensitive (priv->browse_files_popup_menu_copy_file_location_item, active);
+}
- g_object_unref (file);
+static GtkWidget *
+file_list_add_image_menu_item (GtkFileChooserDefault *impl,
+ const char *stock_name,
+ const char *mnemonic_label,
+ GCallback callback)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkWidget *item;
+
+ item = gtk_image_menu_item_new_with_mnemonic (mnemonic_label);
+ gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
+ gtk_image_new_from_stock (stock_name, GTK_ICON_SIZE_MENU));
+ g_signal_connect (item, "activate", callback, impl);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item);
+
+ return item;
}
-/* Callback used when the "Add to Bookmarks" menu item is activated */
-static void
-add_to_shortcuts_cb (GtkMenuItem *item,
- GtkFileChooserDefault *impl)
+static GtkWidget *
+file_list_add_check_menu_item (GtkFileChooserDefault *impl,
+ const char *mnemonic_label,
+ GCallback callback)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeSelection *selection;
+ GtkWidget *item;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ item = gtk_check_menu_item_new_with_mnemonic (mnemonic_label);
+ g_signal_connect (item, "toggled", callback, impl);
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item);
- gtk_tree_selection_selected_foreach (selection,
- add_bookmark_foreach_cb,
- impl);
+ return item;
}
-/* callback used to set data to clipboard */
+/* Constructs the popup menu for the file list if needed */
static void
-copy_file_get_cb (GtkClipboard *clipboard,
- GtkSelectionData *selection_data,
- guint info,
- gpointer data)
+file_list_build_popup_menu (GtkFileChooserDefault *impl)
{
- GSList *selected_files = data;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkWidget *item;
- if (selected_files)
- {
- gint num_files = g_slist_length (selected_files);
- gchar **uris;
- gint i;
- GSList *l;
+ if (priv->browse_files_popup_menu)
+ return;
- uris = g_new (gchar *, num_files + 1);
- uris[num_files] = NULL; /* null terminator */
+ priv->browse_files_popup_menu = gtk_menu_new ();
+ gtk_menu_attach_to_widget (GTK_MENU (priv->browse_files_popup_menu),
+ priv->browse_files_tree_view,
+ popup_menu_detach_cb);
- i = 0;
+ priv->browse_files_popup_menu_visit_file_item = file_list_add_image_menu_item (impl,
GTK_STOCK_DIRECTORY, _("_Visit this file"),
+ G_CALLBACK
(visit_file_cb));
- for (l = selected_files; l; l = l->next)
- {
- GFile *file = (GFile *) l->data;
+ priv->browse_files_popup_menu_copy_file_location_item = file_list_add_image_menu_item (impl,
GTK_STOCK_COPY, _("_Copy file’s location"),
+ G_CALLBACK
(copy_file_location_cb));
- if (info == SELECTION_URI)
- uris[i] = g_file_get_uri (file);
- else /* if (info == SELECTION_TEXT) - let this be the fallback */
- uris[i] = g_file_get_parse_name (file);
+ priv->browse_files_popup_menu_add_shortcut_item = file_list_add_image_menu_item (impl, GTK_STOCK_ADD,
_("_Add to Bookmarks"),
+ G_CALLBACK
(add_to_shortcuts_cb));
- i++;
- }
+ item = gtk_separator_menu_item_new ();
+ gtk_widget_show (item);
+ gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item);
- if (info == SELECTION_URI)
- gtk_selection_data_set_uris (selection_data, uris);
- else /* if (info == SELECTION_TEXT) - let this be the fallback */
- {
- char *str = g_strjoinv (" ", uris);
- gtk_selection_data_set_text (selection_data, str, -1);
- g_free (str);
- }
+ priv->browse_files_popup_menu_hidden_files_item = file_list_add_check_menu_item (impl, _("Show
_Hidden Files"),
+ G_CALLBACK
(show_hidden_toggled_cb));
- g_strfreev (uris);
- }
+ priv->browse_files_popup_menu_size_column_item = file_list_add_check_menu_item (impl, _("Show _Size
Column"),
+ G_CALLBACK
(show_size_column_toggled_cb));
+
+ check_copy_file_location_sensitivity (impl);
}
-/* callback used to clear the clipboard data */
+/* Updates the popup menu for the file list, creating it if necessary */
static void
-copy_file_clear_cb (GtkClipboard *clipboard,
- gpointer data)
+file_list_update_popup_menu (GtkFileChooserDefault *impl)
{
- GSList *selected_files = data;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- g_slist_foreach (selected_files, (GFunc) g_object_unref, NULL);
- g_slist_free (selected_files);
+ file_list_build_popup_menu (impl);
+
+ /* The sensitivity of the Add to Bookmarks item is set in
+ * bookmarks_check_add_sensitivity()
+ */
+
+ /* 'Visit this file' */
+ gtk_widget_set_visible (priv->browse_files_popup_menu_visit_file_item, (priv->operation_mode !=
OPERATION_MODE_BROWSE));
+
+ /* 'Show Hidden Files' */
+ g_signal_handlers_block_by_func (priv->browse_files_popup_menu_hidden_files_item,
+ G_CALLBACK (show_hidden_toggled_cb), impl);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->browse_files_popup_menu_hidden_files_item),
+ priv->show_hidden);
+ g_signal_handlers_unblock_by_func (priv->browse_files_popup_menu_hidden_files_item,
+ G_CALLBACK (show_hidden_toggled_cb), impl);
+
+ /* 'Show Size Column' */
+ g_signal_handlers_block_by_func (priv->browse_files_popup_menu_size_column_item,
+ G_CALLBACK (show_size_column_toggled_cb), impl);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->browse_files_popup_menu_size_column_item),
+ priv->show_size_column);
+ g_signal_handlers_unblock_by_func (priv->browse_files_popup_menu_size_column_item,
+ G_CALLBACK (show_size_column_toggled_cb), impl);
}
-/* Callback used when the "Copy file’s location" menu item is activated */
static void
-copy_file_location_cb (GtkMenuItem *item,
- GtkFileChooserDefault *impl)
+popup_position_func (GtkMenu *menu,
+ gint *x,
+ gint *y,
+ gboolean *push_in,
+ gpointer user_data)
{
- GSList *selected_files = NULL;
+ GtkAllocation allocation;
+ GtkWidget *widget = GTK_WIDGET (user_data);
+ GdkScreen *screen = gtk_widget_get_screen (widget);
+ GtkRequisition req;
+ gint monitor_num;
+ GdkRectangle monitor;
- selected_files = search_get_selected_files (impl);
+ g_return_if_fail (gtk_widget_get_realized (widget));
- if (selected_files)
- {
- GtkClipboard *clipboard;
- GtkTargetList *target_list;
- GtkTargetEntry *targets;
- int n_targets;
+ gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
- clipboard = gtk_widget_get_clipboard (GTK_WIDGET (impl), GDK_SELECTION_CLIPBOARD);
+ gtk_widget_get_preferred_size (GTK_WIDGET (menu),
+ &req, NULL);
- target_list = gtk_target_list_new (NULL, 0);
- gtk_target_list_add_text_targets (target_list, SELECTION_TEXT);
- gtk_target_list_add_uri_targets (target_list, SELECTION_URI);
+ gtk_widget_get_allocation (widget, &allocation);
+ *x += (allocation.width - req.width) / 2;
+ *y += (allocation.height - req.height) / 2;
- targets = gtk_target_table_new_from_list (target_list, &n_targets);
- gtk_target_list_unref (target_list);
+ monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
+ gtk_menu_set_monitor (menu, monitor_num);
+ gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
- gtk_clipboard_set_with_data (clipboard, targets, n_targets,
- copy_file_get_cb,
- copy_file_clear_cb,
- selected_files);
+ *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
+ *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
- gtk_target_table_free (targets, n_targets);
- }
+ *push_in = FALSE;
}
-/* Callback used when the "Visit this file" menu item is activated */
static void
-visit_file_cb (GtkMenuItem *item,
- GtkFileChooserDefault *impl)
+file_list_popup_menu (GtkFileChooserDefault *impl,
+ GdkEventButton *event)
{
- GSList *files;
-
- files = search_get_selected_files (impl);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- /* Sigh, just use the first one */
- if (files)
+ file_list_update_popup_menu (impl);
+ if (event)
+ gtk_menu_popup (GTK_MENU (priv->browse_files_popup_menu),
+ NULL, NULL, NULL, NULL,
+ event->button, event->time);
+ else
{
- GFile *file = files->data;
-
- gtk_file_chooser_default_select_file (GTK_FILE_CHOOSER (impl), file, NULL); /* NULL-GError */
+ gtk_menu_popup (GTK_MENU (priv->browse_files_popup_menu),
+ NULL, NULL,
+ popup_position_func, priv->browse_files_tree_view,
+ 0, GDK_CURRENT_TIME);
+ gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->browse_files_popup_menu),
+ FALSE);
}
- g_slist_foreach (files, (GFunc) g_object_unref, NULL);
- g_slist_free (files);
}
-/* callback used when the "Show Hidden Files" menu item is toggled */
-static void
-show_hidden_toggled_cb (GtkCheckMenuItem *item,
- GtkFileChooserDefault *impl)
+/* Callback used for the GtkWidget::popup-menu signal of the file list */
+static gboolean
+list_popup_menu_cb (GtkWidget *widget,
+ GtkFileChooserDefault *impl)
{
- g_object_set (impl,
- "show-hidden", gtk_check_menu_item_get_active (item),
- NULL);
+ file_list_popup_menu (impl, NULL);
+ return TRUE;
}
-/* Callback used when the "Show Size Column" menu item is toggled */
-static void
-show_size_column_toggled_cb (GtkCheckMenuItem *item,
- GtkFileChooserDefault *impl)
+/* Callback used when a button is pressed on the file list. We trap button 3 to
+ * bring up a popup menu.
+ */
+static gboolean
+list_button_press_event_cb (GtkWidget *widget,
+ GdkEventButton *event,
+ GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- priv->show_size_column = gtk_check_menu_item_get_active (item);
+ static gboolean in_press = FALSE;
- gtk_tree_view_column_set_visible (priv->list_size_column,
- priv->show_size_column);
-}
+ if (in_press)
+ return FALSE;
-/* Shows an error dialog about not being able to select a dragged file */
-static void
-error_selecting_dragged_file_dialog (GtkFileChooserDefault *impl,
- GFile *file,
- GError *error)
-{
- error_dialog (impl,
- _("Could not select file"),
- file, error);
-}
+ if (!gdk_event_triggers_context_menu ((GdkEvent *) event))
+ return FALSE;
-static void
-file_list_drag_data_select_uris (GtkFileChooserDefault *impl,
- gchar **uris)
-{
- int i;
- char *uri;
- GtkFileChooser *chooser = GTK_FILE_CHOOSER (impl);
+ in_press = TRUE;
+ gtk_widget_event (priv->browse_files_tree_view, (GdkEvent *) event);
+ in_press = FALSE;
- for (i = 1; uris[i]; i++)
- {
- GFile *file;
- GError *error = NULL;
+ file_list_popup_menu (impl, event);
+ return TRUE;
+}
- uri = uris[i];
- file = g_file_new_for_uri (uri);
+typedef struct {
+ OperationMode operation_mode;
+ gint general_column;
+ gint model_column;
+} ColumnMap;
- gtk_file_chooser_default_select_file (chooser, file, &error);
- if (error)
- error_selecting_dragged_file_dialog (impl, file, error);
+/* Sets the sort column IDs for the file list; needs to be done whenever we
+ * change the model on the treeview.
+ */
+static void
+file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- g_object_unref (file);
- }
+ gtk_tree_view_column_set_sort_column_id (priv->list_name_column, MODEL_COL_NAME);
+ gtk_tree_view_column_set_sort_column_id (priv->list_mtime_column, MODEL_COL_MTIME);
+ gtk_tree_view_column_set_sort_column_id (priv->list_size_column, MODEL_COL_SIZE);
}
-struct FileListDragData
+static gboolean
+file_list_query_tooltip_cb (GtkWidget *widget,
+ gint x,
+ gint y,
+ gboolean keyboard_tip,
+ GtkTooltip *tooltip,
+ gpointer user_data)
{
- GtkFileChooserDefault *impl;
- gchar **uris;
+ GtkFileChooserDefault *impl = user_data;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeIter iter;
GFile *file;
-};
-
-static void
-file_list_drag_data_received_get_info_cb (GCancellable *cancellable,
- GFileInfo *info,
- const GError *error,
- gpointer user_data)
-{
- gboolean cancelled = g_cancellable_is_cancelled (cancellable);
- struct FileListDragData *data = user_data;
- GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->impl);
- GtkFileChooserDefaultPrivate *priv = data->impl->priv;
+ gchar *filename;
- if (cancellable != priv->file_list_drag_data_received_cancellable)
- goto out;
+ if (priv->operation_mode == OPERATION_MODE_BROWSE)
+ return FALSE;
- priv->file_list_drag_data_received_cancellable = NULL;
- if (cancelled || error)
- goto out;
+ if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ &x, &y,
+ keyboard_tip,
+ &model, &path, &iter))
+ return FALSE;
+
+ gtk_tree_model_get (model, &iter,
+ MODEL_COL_FILE, &file,
+ -1);
- if ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SAVE) &&
- data->uris[1] == 0 && !error && _gtk_file_info_consider_as_directory (info))
- change_folder_and_display_error (data->impl, data->file, FALSE);
- else
+ if (file == NULL)
{
- GError *error = NULL;
-
- gtk_file_chooser_default_unselect_all (chooser);
- gtk_file_chooser_default_select_file (chooser, data->file, &error);
- if (error)
- error_selecting_dragged_file_dialog (data->impl, data->file, error);
- else
- browse_files_center_selected_row (data->impl);
+ gtk_tree_path_free (path);
+ return FALSE;
}
- if (priv->select_multiple)
- file_list_drag_data_select_uris (data->impl, data->uris);
+ filename = g_file_get_path (file);
+ gtk_tooltip_set_text (tooltip, filename);
+ gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ tooltip,
+ path);
-out:
- g_object_unref (data->impl);
- g_strfreev (data->uris);
- g_object_unref (data->file);
- g_free (data);
+ g_free (filename);
+ g_object_unref (file);
+ gtk_tree_path_free (path);
- g_object_unref (cancellable);
+ return TRUE;
}
static void
-file_list_drag_data_received_cb (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- GtkSelectionData *selection_data,
- guint info,
- guint time_,
- gpointer data)
+set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gchar **uris;
- char *uri;
- GFile *file;
-
- /* Allow only drags from other widgets; see bug #533891. */
- if (gtk_drag_get_source_widget (context) == widget)
- {
- g_signal_stop_emission_by_name (widget, "drag-data-received");
- return;
- }
-
- /* Parse the text/uri-list string, navigate to the first one */
- uris = gtk_selection_data_get_uris (selection_data);
- if (uris && uris[0])
- {
- struct FileListDragData *data;
-
- uri = uris[0];
- file = g_file_new_for_uri (uri);
-
- data = g_new0 (struct FileListDragData, 1);
- data->impl = g_object_ref (impl);
- data->uris = uris;
- data->file = file;
-
- if (priv->file_list_drag_data_received_cancellable)
- g_cancellable_cancel (priv->file_list_drag_data_received_cancellable);
-
- priv->file_list_drag_data_received_cancellable =
- _gtk_file_system_get_info (priv->file_system, file,
- "standard::type",
- file_list_drag_data_received_get_info_cb,
- data);
- }
+ gint xpad, ypad;
- g_signal_stop_emission_by_name (widget, "drag-data-received");
+ gtk_cell_renderer_get_padding (priv->list_pixbuf_renderer, &xpad, &ypad);
+ gtk_cell_renderer_set_fixed_size (priv->list_pixbuf_renderer,
+ xpad * 2 + priv->icon_size,
+ ypad * 2 + priv->icon_size);
}
-/* Don't do anything with the drag_drop signal */
-static gboolean
-file_list_drag_drop_cb (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time_,
- GtkFileChooserDefault *impl)
-{
- g_signal_stop_emission_by_name (widget, "drag-drop");
- return TRUE;
-}
-/* Disable the normal tree drag motion handler, it makes it look like you're
- dropping the dragged item onto a tree item */
-static gboolean
-file_list_drag_motion_cb (GtkWidget *widget,
- GdkDragContext *context,
- gint x,
- gint y,
- guint time_,
- GtkFileChooserDefault *impl)
+static void
+location_entry_create (GtkFileChooserDefault *impl)
{
- g_signal_stop_emission_by_name (widget, "drag-motion");
- return TRUE;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ if (!priv->location_entry)
+ priv->location_entry = _gtk_file_chooser_entry_new (TRUE);
+
+ _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->local_only);
+ _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action);
+ gtk_entry_set_width_chars (GTK_ENTRY (priv->location_entry), 45);
+ gtk_entry_set_activates_default (GTK_ENTRY (priv->location_entry), TRUE);
}
-/* Sensitizes the "Copy file’s location" context menu item if there is actually
- * a selection active.
- */
+/* Creates the widgets specific to Save mode */
static void
-check_copy_file_location_sensitivity (GtkFileChooserDefault *impl)
+save_widgets_create (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeSelection *selection;
- gboolean active;
+ GtkWidget *vbox;
+ GtkWidget *widget;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- if (gtk_tree_selection_count_selected_rows (selection) == 0)
- active = FALSE;
- else
- active = TRUE;
+ if (priv->save_widgets != NULL)
+ return;
- if (priv->browse_files_popup_menu_copy_file_location_item)
- gtk_widget_set_sensitive (priv->browse_files_popup_menu_copy_file_location_item, active);
+ location_switch_to_path_bar (impl);
+
+ vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+
+ priv->save_widgets_table = gtk_grid_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), priv->save_widgets_table, FALSE, FALSE, 0);
+ gtk_widget_show (priv->save_widgets_table);
+ gtk_grid_set_row_spacing (GTK_GRID (priv->save_widgets_table), 12);
+ gtk_grid_set_column_spacing (GTK_GRID (priv->save_widgets_table), 12);
+
+ /* Label */
+
+ widget = gtk_label_new_with_mnemonic (_("_Name:"));
+ gtk_widget_set_halign (widget, GTK_ALIGN_START);
+ gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
+ gtk_grid_attach (GTK_GRID (priv->save_widgets_table), widget, 0, 0, 1, 1);
+ gtk_widget_show (widget);
+
+ /* Location entry */
+
+ location_entry_create (impl);
+ gtk_widget_set_hexpand (priv->location_entry, TRUE);
+ gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->location_entry, 1, 0, 1, 1);
+ gtk_widget_show (priv->location_entry);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), priv->location_entry);
+
+ /* Folder combo */
+ priv->save_folder_label = gtk_label_new (NULL);
+ gtk_widget_set_halign (priv->save_folder_label, GTK_ALIGN_START);
+ gtk_widget_set_valign (priv->save_folder_label, GTK_ALIGN_CENTER);
+ gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->save_folder_label, 0, 1, 1, 1);
+ gtk_widget_show (priv->save_folder_label);
+
+ priv->save_widgets = vbox;
+ gtk_box_pack_start (GTK_BOX (impl), priv->save_widgets, FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (impl), priv->save_widgets, 0);
+ gtk_widget_show (priv->save_widgets);
}
-static GtkWidget *
-file_list_add_image_menu_item (GtkFileChooserDefault *impl,
- const char *stock_name,
- const char *mnemonic_label,
- GCallback callback)
+/* Destroys the widgets specific to Save mode */
+static void
+save_widgets_destroy (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkWidget *item;
- item = gtk_image_menu_item_new_with_mnemonic (mnemonic_label);
- gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
- gtk_image_new_from_stock (stock_name, GTK_ICON_SIZE_MENU));
- g_signal_connect (item, "activate", callback, impl);
- gtk_widget_show (item);
- gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item);
-
- return item;
+ if (priv->save_widgets == NULL)
+ return;
+
+ gtk_widget_destroy (priv->save_widgets);
+ priv->save_widgets = NULL;
+ priv->save_widgets_table = NULL;
+ priv->location_entry = NULL;
+ priv->save_folder_label = NULL;
}
-static GtkWidget *
-file_list_add_check_menu_item (GtkFileChooserDefault *impl,
- const char *mnemonic_label,
- GCallback callback)
+/* Turns on the path bar widget. Can be called even if we are already in that
+ * mode.
+ */
+static void
+location_switch_to_path_bar (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkWidget *item;
- item = gtk_check_menu_item_new_with_mnemonic (mnemonic_label);
- g_signal_connect (item, "toggled", callback, impl);
- gtk_widget_show (item);
- gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item);
+ if (priv->location_entry)
+ {
+ gtk_widget_destroy (priv->location_entry);
+ priv->location_entry = NULL;
+ }
- return item;
+ gtk_widget_hide (priv->location_entry_box);
}
-/* Constructs the popup menu for the file list if needed */
+/* Turns on the location entry. Can be called even if we are already in that
+ * mode.
+ */
static void
-file_list_build_popup_menu (GtkFileChooserDefault *impl)
+location_switch_to_filename_entry (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkWidget *item;
- if (priv->browse_files_popup_menu)
+ /* when in search or recent files mode, we are not showing the
+ * location_entry_box container, so there's no point in switching
+ * to it.
+ */
+ if (priv->operation_mode == OPERATION_MODE_SEARCH ||
+ priv->operation_mode == OPERATION_MODE_RECENT)
return;
- priv->browse_files_popup_menu = gtk_menu_new ();
- gtk_menu_attach_to_widget (GTK_MENU (priv->browse_files_popup_menu),
- priv->browse_files_tree_view,
- popup_menu_detach_cb);
+ /* Box */
- priv->browse_files_popup_menu_visit_file_item = file_list_add_image_menu_item (impl,
GTK_STOCK_DIRECTORY, _("_Visit this file"),
- G_CALLBACK
(visit_file_cb));
+ gtk_widget_show (priv->location_entry_box);
- priv->browse_files_popup_menu_copy_file_location_item = file_list_add_image_menu_item (impl,
GTK_STOCK_COPY, _("_Copy file’s location"),
- G_CALLBACK
(copy_file_location_cb));
+ /* Entry */
- priv->browse_files_popup_menu_add_shortcut_item = file_list_add_image_menu_item (impl, GTK_STOCK_ADD,
_("_Add to Bookmarks"),
- G_CALLBACK
(add_to_shortcuts_cb));
+ if (!priv->location_entry)
+ {
+ location_entry_create (impl);
+ gtk_box_pack_start (GTK_BOX (priv->location_entry_box), priv->location_entry, TRUE, TRUE, 0);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (priv->location_label), priv->location_entry);
+ }
- item = gtk_separator_menu_item_new ();
- gtk_widget_show (item);
- gtk_menu_shell_append (GTK_MENU_SHELL (priv->browse_files_popup_menu), item);
+ /* Configure the entry */
- priv->browse_files_popup_menu_hidden_files_item = file_list_add_check_menu_item (impl, _("Show
_Hidden Files"),
- G_CALLBACK
(show_hidden_toggled_cb));
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry),
priv->current_folder);
- priv->browse_files_popup_menu_size_column_item = file_list_add_check_menu_item (impl, _("Show _Size
Column"),
- G_CALLBACK
(show_size_column_toggled_cb));
+ /* Done */
- check_copy_file_location_sensitivity (impl);
+ gtk_widget_show (priv->location_entry);
+ gtk_widget_grab_focus (priv->location_entry);
}
-/* Updates the popup menu for the file list, creating it if necessary */
+/* Sets a new location mode. set_buttons determines whether the toggle button
+ * for the mode will also be changed.
+ */
static void
-file_list_update_popup_menu (GtkFileChooserDefault *impl)
+location_mode_set (GtkFileChooserDefault *impl,
+ LocationMode new_mode,
+ gboolean set_button)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- file_list_build_popup_menu (impl);
-
- /* The sensitivity of the Add to Bookmarks item is set in
- * bookmarks_check_add_sensitivity()
- */
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ GtkWindow *toplevel;
+ GtkWidget *current_focus;
+ gboolean button_active;
+ gboolean switch_to_file_list;
- /* 'Visit this file' */
- gtk_widget_set_visible (priv->browse_files_popup_menu_visit_file_item, (priv->operation_mode !=
OPERATION_MODE_BROWSE));
+ switch (new_mode)
+ {
+ case LOCATION_MODE_PATH_BAR:
+ button_active = FALSE;
- /* 'Show Hidden Files' */
- g_signal_handlers_block_by_func (priv->browse_files_popup_menu_hidden_files_item,
- G_CALLBACK (show_hidden_toggled_cb), impl);
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->browse_files_popup_menu_hidden_files_item),
- priv->show_hidden);
- g_signal_handlers_unblock_by_func (priv->browse_files_popup_menu_hidden_files_item,
- G_CALLBACK (show_hidden_toggled_cb), impl);
+ /* The location_entry will disappear when we switch to path bar mode. So,
+ * we'll focus the file list in that case, to avoid having a window with
+ * no focused widget.
+ */
+ toplevel = get_toplevel (GTK_WIDGET (impl));
+ switch_to_file_list = FALSE;
+ if (toplevel)
+ {
+ current_focus = gtk_window_get_focus (toplevel);
+ if (!current_focus || current_focus == priv->location_entry)
+ switch_to_file_list = TRUE;
+ }
- /* 'Show Size Column' */
- g_signal_handlers_block_by_func (priv->browse_files_popup_menu_size_column_item,
- G_CALLBACK (show_size_column_toggled_cb), impl);
- gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (priv->browse_files_popup_menu_size_column_item),
- priv->show_size_column);
- g_signal_handlers_unblock_by_func (priv->browse_files_popup_menu_size_column_item,
- G_CALLBACK (show_size_column_toggled_cb), impl);
-}
+ location_switch_to_path_bar (impl);
-static void
-popup_position_func (GtkMenu *menu,
- gint *x,
- gint *y,
- gboolean *push_in,
- gpointer user_data)
-{
- GtkAllocation allocation;
- GtkWidget *widget = GTK_WIDGET (user_data);
- GdkScreen *screen = gtk_widget_get_screen (widget);
- GtkRequisition req;
- gint monitor_num;
- GdkRectangle monitor;
+ if (switch_to_file_list)
+ gtk_widget_grab_focus (priv->browse_files_tree_view);
- g_return_if_fail (gtk_widget_get_realized (widget));
+ break;
- gdk_window_get_origin (gtk_widget_get_window (widget), x, y);
+ case LOCATION_MODE_FILENAME_ENTRY:
+ button_active = TRUE;
+ location_switch_to_filename_entry (impl);
+ break;
- gtk_widget_get_preferred_size (GTK_WIDGET (menu),
- &req, NULL);
+ default:
+ g_assert_not_reached ();
+ return;
+ }
- gtk_widget_get_allocation (widget, &allocation);
- *x += (allocation.width - req.width) / 2;
- *y += (allocation.height - req.height) / 2;
+ if (set_button)
+ {
+ g_signal_handlers_block_by_func (priv->location_button,
+ G_CALLBACK (location_button_toggled_cb), impl);
- monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
- gtk_menu_set_monitor (menu, monitor_num);
- gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->location_button), button_active);
- *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
- *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
+ g_signal_handlers_unblock_by_func (priv->location_button,
+ G_CALLBACK (location_button_toggled_cb), impl);
+ }
+ }
- *push_in = FALSE;
+ priv->location_mode = new_mode;
}
static void
-file_list_popup_menu (GtkFileChooserDefault *impl,
- GdkEventButton *event)
+location_toggle_popup_handler (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- file_list_update_popup_menu (impl);
- if (event)
- gtk_menu_popup (GTK_MENU (priv->browse_files_popup_menu),
- NULL, NULL, NULL, NULL,
- event->button, event->time);
- else
+ /* when in search or recent files mode, we are not showing the
+ * location_entry_box container, so there's no point in switching
+ * to it.
+ */
+ if (priv->operation_mode == OPERATION_MODE_SEARCH ||
+ priv->operation_mode == OPERATION_MODE_RECENT)
+ return;
+
+ /* If the file entry is not visible, show it.
+ * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry.
+ */
+ if (priv->location_mode == LOCATION_MODE_PATH_BAR)
{
- gtk_menu_popup (GTK_MENU (priv->browse_files_popup_menu),
- NULL, NULL,
- popup_position_func, priv->browse_files_tree_view,
- 0, GDK_CURRENT_TIME);
- gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->browse_files_popup_menu),
- FALSE);
+ location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
+ }
+ else if (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+ {
+ if (gtk_widget_has_focus (priv->location_entry))
+ {
+ location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
+ }
+ else
+ {
+ gtk_widget_grab_focus (priv->location_entry);
+ }
}
-
-}
-
-/* Callback used for the GtkWidget::popup-menu signal of the file list */
-static gboolean
-list_popup_menu_cb (GtkWidget *widget,
- GtkFileChooserDefault *impl)
-{
- file_list_popup_menu (impl, NULL);
- return TRUE;
}
-/* Callback used when a button is pressed on the file list. We trap button 3 to
- * bring up a popup menu.
- */
-static gboolean
-list_button_press_event_cb (GtkWidget *widget,
- GdkEventButton *event,
+/* Callback used when one of the location mode buttons is toggled */
+static void
+location_button_toggled_cb (GtkToggleButton *toggle,
GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ gboolean is_active;
+ LocationMode new_mode;
- static gboolean in_press = FALSE;
-
- if (in_press)
- return FALSE;
-
- if (!gdk_event_triggers_context_menu ((GdkEvent *) event))
- return FALSE;
+ is_active = gtk_toggle_button_get_active (toggle);
- in_press = TRUE;
- gtk_widget_event (priv->browse_files_tree_view, (GdkEvent *) event);
- in_press = FALSE;
+ if (is_active)
+ {
+ g_assert (priv->location_mode == LOCATION_MODE_PATH_BAR);
+ new_mode = LOCATION_MODE_FILENAME_ENTRY;
+ }
+ else
+ {
+ g_assert (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY);
+ new_mode = LOCATION_MODE_PATH_BAR;
+ }
- file_list_popup_menu (impl, event);
- return TRUE;
+ location_mode_set (impl, new_mode, FALSE);
}
-typedef struct {
- OperationMode operation_mode;
- gint general_column;
- gint model_column;
-} ColumnMap;
+typedef enum {
+ PATH_BAR_FOLDER_PATH,
+ PATH_BAR_SELECT_A_FOLDER,
+ PATH_BAR_ERROR_NO_FILENAME,
+ PATH_BAR_ERROR_NO_FOLDER,
+ PATH_BAR_RECENTLY_USED,
+ PATH_BAR_SEARCH
+} PathBarMode;
-/* Sets the sort column IDs for the file list; needs to be done whenever we
- * change the model on the treeview.
- */
+/* Sets the info bar to show the appropriate informational or warning message */
static void
-file_list_set_sort_column_ids (GtkFileChooserDefault *impl)
+info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ char *str;
+ gboolean free_str;
+ GtkMessageType message_type;
- gtk_tree_view_column_set_sort_column_id (priv->list_name_column, MODEL_COL_NAME);
- gtk_tree_view_column_set_sort_column_id (priv->list_mtime_column, MODEL_COL_MTIME);
- gtk_tree_view_column_set_sort_column_id (priv->list_size_column, MODEL_COL_SIZE);
-}
-
-static gboolean
-file_list_query_tooltip_cb (GtkWidget *widget,
- gint x,
- gint y,
- gboolean keyboard_tip,
- GtkTooltip *tooltip,
- gpointer user_data)
-{
- GtkFileChooserDefault *impl = user_data;
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeModel *model;
- GtkTreePath *path;
- GtkTreeIter iter;
- GFile *file;
- gchar *filename;
+ free_str = FALSE;
- if (priv->operation_mode == OPERATION_MODE_BROWSE)
- return FALSE;
+ switch (mode)
+ {
+ case PATH_BAR_SELECT_A_FOLDER:
+ str = g_strconcat ("<i>", _("Please select a folder below"), "</i>", NULL);
+ free_str = TRUE;
+ message_type = GTK_MESSAGE_OTHER;
+ break;
+ case PATH_BAR_ERROR_NO_FILENAME:
+ str = _("Please type a file name");
+ message_type = GTK_MESSAGE_WARNING;
+ break;
- if (!gtk_tree_view_get_tooltip_context (GTK_TREE_VIEW (priv->browse_files_tree_view),
- &x, &y,
- keyboard_tip,
- &model, &path, &iter))
- return FALSE;
-
- gtk_tree_model_get (model, &iter,
- MODEL_COL_FILE, &file,
- -1);
+ case PATH_BAR_ERROR_NO_FOLDER:
+ str = _("Please select a folder below");
+ message_type = GTK_MESSAGE_WARNING;
+ break;
- if (file == NULL)
- {
- gtk_tree_path_free (path);
- return FALSE;
+ default:
+ g_assert_not_reached ();
+ return;
}
- filename = g_file_get_path (file);
- gtk_tooltip_set_text (tooltip, filename);
- gtk_tree_view_set_tooltip_row (GTK_TREE_VIEW (priv->browse_files_tree_view),
- tooltip,
- path);
-
- g_free (filename);
- g_object_unref (file);
- gtk_tree_path_free (path);
+ gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->browse_select_a_folder_info_bar), message_type);
+ gtk_image_set_from_stock (GTK_IMAGE (priv->browse_select_a_folder_icon),
+ (message_type == GTK_MESSAGE_WARNING) ? GTK_STOCK_DIALOG_WARNING :
GTK_STOCK_DIRECTORY,
+ GTK_ICON_SIZE_MENU);
+ gtk_label_set_markup (GTK_LABEL (priv->browse_select_a_folder_label), str);
- return TRUE;
+ if (free_str)
+ g_free (str);
}
+/* Sets the path bar's mode to show a label, the actual folder path, or a
+ * warning message. You may call this function with PATH_BAR_ERROR_* directly
+ * if the pathbar is already showing the widgets you expect; otherwise, call
+ * path_bar_update() instead to set the appropriate widgets automatically.
+ */
static void
-set_icon_cell_renderer_fixed_size (GtkFileChooserDefault *impl)
+path_bar_set_mode (GtkFileChooserDefault *impl, PathBarMode mode)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gint xpad, ypad;
+ gboolean path_bar_visible = FALSE;
+ gboolean special_mode_widgets_visible = FALSE;
+ gboolean info_bar_visible = FALSE;
+ gboolean create_folder_visible = FALSE;
- gtk_cell_renderer_get_padding (priv->list_pixbuf_renderer, &xpad, &ypad);
- gtk_cell_renderer_set_fixed_size (priv->list_pixbuf_renderer,
- xpad * 2 + priv->icon_size,
- ypad * 2 + priv->icon_size);
-}
+ char *tmp;
+ switch (mode)
+ {
+ case PATH_BAR_FOLDER_PATH:
+ path_bar_visible = TRUE;
+ break;
-static void
-location_entry_create (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ case PATH_BAR_SELECT_A_FOLDER:
+ case PATH_BAR_ERROR_NO_FILENAME:
+ case PATH_BAR_ERROR_NO_FOLDER:
+ info_bar_set (impl, mode);
+ info_bar_visible = TRUE;
+ break;
- if (!priv->location_entry)
- priv->location_entry = _gtk_file_chooser_entry_new (TRUE);
+ case PATH_BAR_RECENTLY_USED:
+ gtk_image_set_from_icon_name (GTK_IMAGE (priv->browse_special_mode_icon), "document-open-recent",
GTK_ICON_SIZE_BUTTON);
- _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->local_only);
- _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action);
- gtk_entry_set_width_chars (GTK_ENTRY (priv->location_entry), 45);
- gtk_entry_set_activates_default (GTK_ENTRY (priv->location_entry), TRUE);
-}
+ tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used"));
+ gtk_label_set_markup (GTK_LABEL (priv->browse_special_mode_label), tmp);
+ g_free (tmp);
-/* Creates the widgets specific to Save mode */
-static void
-save_widgets_create (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkWidget *vbox;
- GtkWidget *widget;
+ special_mode_widgets_visible = TRUE;
+ break;
- if (priv->save_widgets != NULL)
- return;
+ case PATH_BAR_SEARCH:
+ gtk_image_set_from_stock (GTK_IMAGE (priv->browse_special_mode_icon), GTK_STOCK_FIND,
GTK_ICON_SIZE_BUTTON);
- location_switch_to_path_bar (impl);
+ tmp = g_strdup_printf ("<b>%s</b>", _("Search:"));
+ gtk_label_set_markup (GTK_LABEL (priv->browse_special_mode_label), tmp);
+ g_free (tmp);
- vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
+ special_mode_widgets_visible = TRUE;
+ break;
- priv->save_widgets_table = gtk_grid_new ();
- gtk_box_pack_start (GTK_BOX (vbox), priv->save_widgets_table, FALSE, FALSE, 0);
- gtk_widget_show (priv->save_widgets_table);
- gtk_grid_set_row_spacing (GTK_GRID (priv->save_widgets_table), 12);
- gtk_grid_set_column_spacing (GTK_GRID (priv->save_widgets_table), 12);
+ default:
+ g_assert_not_reached ();
+ }
- /* Label */
+ gtk_widget_set_visible (priv->browse_path_bar, path_bar_visible);
+ gtk_widget_set_visible (priv->browse_special_mode_icon, special_mode_widgets_visible);
+ gtk_widget_set_visible (priv->browse_special_mode_label, special_mode_widgets_visible);
+ gtk_widget_set_visible (priv->browse_select_a_folder_info_bar, info_bar_visible);
- widget = gtk_label_new_with_mnemonic (_("_Name:"));
- gtk_widget_set_halign (widget, GTK_ALIGN_START);
- gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
- gtk_grid_attach (GTK_GRID (priv->save_widgets_table), widget, 0, 0, 1, 1);
- gtk_widget_show (widget);
+ if (path_bar_visible)
+ {
+ if (priv->create_folders
+ && priv->action != GTK_FILE_CHOOSER_ACTION_OPEN
+ && priv->operation_mode != OPERATION_MODE_RECENT)
+ create_folder_visible = TRUE;
+ }
- /* Location entry */
+ gtk_widget_set_visible (priv->browse_new_folder_button, create_folder_visible);
+}
- location_entry_create (impl);
- gtk_widget_set_hexpand (priv->location_entry, TRUE);
- gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->location_entry, 1, 0, 1, 1);
- gtk_widget_show (priv->location_entry);
- gtk_label_set_mnemonic_widget (GTK_LABEL (widget), priv->location_entry);
- /* Folder combo */
- priv->save_folder_label = gtk_label_new (NULL);
- gtk_widget_set_halign (priv->save_folder_label, GTK_ALIGN_START);
- gtk_widget_set_valign (priv->save_folder_label, GTK_ALIGN_CENTER);
- gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->save_folder_label, 0, 1, 1, 1);
- gtk_widget_show (priv->save_folder_label);
+static GObject*
+gtk_file_chooser_default_constructor (GType type,
+ guint n_construct_properties,
+ GObjectConstructParam *construct_params)
+{
+ GtkFileChooserDefault *impl;
+ GtkFileChooserDefaultPrivate *priv;
+ GObject *object;
- priv->save_widgets = vbox;
- gtk_box_pack_start (GTK_BOX (impl), priv->save_widgets, FALSE, FALSE, 0);
- gtk_box_reorder_child (GTK_BOX (impl), priv->save_widgets, 0);
- gtk_widget_show (priv->save_widgets);
-}
+ profile_start ("start", NULL);
-/* Destroys the widgets specific to Save mode */
-static void
-save_widgets_destroy (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
+ n_construct_properties,
+ construct_params);
+ impl = GTK_FILE_CHOOSER_DEFAULT (object);
+ priv = impl->priv;
- if (priv->save_widgets == NULL)
- return;
+ g_assert (priv->file_system);
- gtk_widget_destroy (priv->save_widgets);
- priv->save_widgets = NULL;
- priv->save_widgets_table = NULL;
- priv->location_entry = NULL;
- priv->save_folder_label = NULL;
+ update_appearance (impl);
+
+ profile_end ("end", NULL);
+
+ return object;
}
-/* Turns on the path bar widget. Can be called even if we are already in that
- * mode.
- */
+/* Sets the extra_widget by packing it in the appropriate place */
static void
-location_switch_to_path_bar (GtkFileChooserDefault *impl)
+set_extra_widget (GtkFileChooserDefault *impl,
+ GtkWidget *extra_widget)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (priv->location_entry)
+ if (extra_widget)
{
- gtk_widget_destroy (priv->location_entry);
- priv->location_entry = NULL;
+ g_object_ref (extra_widget);
+ /* FIXME: is this right ? */
+ gtk_widget_show (extra_widget);
}
- gtk_widget_hide (priv->location_entry_box);
+ if (priv->extra_widget)
+ {
+ gtk_container_remove (GTK_CONTAINER (priv->extra_align), priv->extra_widget);
+ g_object_unref (priv->extra_widget);
+ }
+
+ priv->extra_widget = extra_widget;
+ if (priv->extra_widget)
+ {
+ gtk_container_add (GTK_CONTAINER (priv->extra_align), priv->extra_widget);
+ gtk_widget_show (priv->extra_align);
+ }
+ else
+ gtk_widget_hide (priv->extra_align);
}
-/* Turns on the location entry. Can be called even if we are already in that
- * mode.
- */
static void
-location_switch_to_filename_entry (GtkFileChooserDefault *impl)
+switch_to_home_dir (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ const gchar *home = g_get_home_dir ();
+ GFile *home_file;
- /* when in search or recent files mode, we are not showing the
- * location_entry_box container, so there's no point in switching
- * to it.
- */
- if (priv->operation_mode == OPERATION_MODE_SEARCH ||
- priv->operation_mode == OPERATION_MODE_RECENT)
+ if (home == NULL)
return;
- /* Box */
-
- gtk_widget_show (priv->location_entry_box);
-
- /* Entry */
-
- if (!priv->location_entry)
- {
- location_entry_create (impl);
- gtk_box_pack_start (GTK_BOX (priv->location_entry_box), priv->location_entry, TRUE, TRUE, 0);
- gtk_label_set_mnemonic_widget (GTK_LABEL (priv->location_label), priv->location_entry);
- }
-
- /* Configure the entry */
-
- _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry),
priv->current_folder);
+ home_file = g_file_new_for_path (home);
- /* Done */
+ gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL); /* NULL-GError */
- gtk_widget_show (priv->location_entry);
- gtk_widget_grab_focus (priv->location_entry);
+ g_object_unref (home_file);
}
-/* Sets a new location mode. set_buttons determines whether the toggle button
- * for the mode will also be changed.
- */
static void
-location_mode_set (GtkFileChooserDefault *impl,
- LocationMode new_mode,
- gboolean set_button)
+set_local_only (GtkFileChooserDefault *impl,
+ gboolean local_only)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ if (local_only != priv->local_only)
{
- GtkWindow *toplevel;
- GtkWidget *current_focus;
- gboolean button_active;
- gboolean switch_to_file_list;
+ priv->local_only = local_only;
- switch (new_mode)
- {
- case LOCATION_MODE_PATH_BAR:
- button_active = FALSE;
+ if (priv->location_entry)
+ _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), local_only);
- /* The location_entry will disappear when we switch to path bar mode. So,
- * we'll focus the file list in that case, to avoid having a window with
- * no focused widget.
+ if (local_only && priv->current_folder &&
+ !_gtk_file_has_native_path (priv->current_folder))
+ {
+ /* If we are pointing to a non-local folder, make an effort to change
+ * back to a local folder, but it's really up to the app to not cause
+ * such a situation, so we ignore errors.
*/
- toplevel = get_toplevel (GTK_WIDGET (impl));
- switch_to_file_list = FALSE;
- if (toplevel)
- {
- current_focus = gtk_window_get_focus (toplevel);
- if (!current_focus || current_focus == priv->location_entry)
- switch_to_file_list = TRUE;
- }
+ switch_to_home_dir (impl);
+ }
+ }
+}
- location_switch_to_path_bar (impl);
+/* Sets the file chooser to multiple selection mode */
+static void
+set_select_multiple (GtkFileChooserDefault *impl,
+ gboolean select_multiple,
+ gboolean property_notify)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeSelection *selection;
+ GtkSelectionMode mode;
- if (switch_to_file_list)
- gtk_widget_grab_focus (priv->browse_files_tree_view);
+ if (select_multiple == priv->select_multiple)
+ return;
- break;
+ mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
- case LOCATION_MODE_FILENAME_ENTRY:
- button_active = TRUE;
- location_switch_to_filename_entry (impl);
- break;
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ gtk_tree_selection_set_mode (selection, mode);
- default:
- g_assert_not_reached ();
- return;
- }
+ gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (priv->browse_files_tree_view), select_multiple);
- if (set_button)
- {
- g_signal_handlers_block_by_func (priv->location_button,
- G_CALLBACK (location_button_toggled_cb), impl);
+ priv->select_multiple = select_multiple;
+ g_object_notify (G_OBJECT (impl), "select-multiple");
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->location_button), button_active);
+ check_preview_change (impl);
+}
- g_signal_handlers_unblock_by_func (priv->location_button,
- G_CALLBACK (location_button_toggled_cb), impl);
- }
- }
+static void
+set_file_system_backend (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- priv->location_mode = new_mode;
+ profile_start ("start for backend", "default");
+
+ priv->file_system = _gtk_file_system_new ();
+
+ profile_end ("end", NULL);
}
static void
-location_toggle_popup_handler (GtkFileChooserDefault *impl)
+unset_file_system_backend (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- /* when in search or recent files mode, we are not showing the
- * location_entry_box container, so there's no point in switching
- * to it.
- */
- if (priv->operation_mode == OPERATION_MODE_SEARCH ||
- priv->operation_mode == OPERATION_MODE_RECENT)
- return;
+ g_object_unref (priv->file_system);
- /* If the file entry is not visible, show it.
- * If it is visible, turn it off only if it is focused. Otherwise, switch to the entry.
- */
- if (priv->location_mode == LOCATION_MODE_PATH_BAR)
- {
- location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
- }
- else if (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)
- {
- if (gtk_widget_has_focus (priv->location_entry))
- {
- location_mode_set (impl, LOCATION_MODE_PATH_BAR, TRUE);
- }
- else
- {
- gtk_widget_grab_focus (priv->location_entry);
- }
- }
+ priv->file_system = NULL;
}
-/* Callback used when one of the location mode buttons is toggled */
+/* Saves the widgets around the pathbar so they can be reparented later
+ * in the correct place. This function must be called paired with
+ * restore_path_bar().
+ */
static void
-location_button_toggled_cb (GtkToggleButton *toggle,
- GtkFileChooserDefault *impl)
+save_path_bar (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gboolean is_active;
- LocationMode new_mode;
+ GtkWidget *parent;
- is_active = gtk_toggle_button_get_active (toggle);
+ g_object_ref (priv->browse_path_bar_hbox);
- if (is_active)
+ parent = gtk_widget_get_parent (priv->browse_path_bar_hbox);
+ if (parent)
+ gtk_container_remove (GTK_CONTAINER (parent), priv->browse_path_bar_hbox);
+}
+
+/* Reparents the path bar and the "Create folder" button to the right place:
+ * Above the file list in Open mode, or to the right of the "Save in folder:"
+ * label in Save mode. The save_path_bar() function must be called before this
+ * one.
+ */
+static void
+restore_path_bar (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
{
- g_assert (priv->location_mode == LOCATION_MODE_PATH_BAR);
- new_mode = LOCATION_MODE_FILENAME_ENTRY;
+ gtk_box_pack_start (GTK_BOX (priv->browse_header_box), priv->browse_path_bar_hbox, FALSE, FALSE, 0);
+ gtk_box_reorder_child (GTK_BOX (priv->browse_header_box), priv->browse_path_bar_hbox, 0);
}
- else
+ else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
{
- g_assert (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY);
- new_mode = LOCATION_MODE_PATH_BAR;
+ gtk_widget_set_hexpand (priv->browse_path_bar_hbox, TRUE);
+ gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->browse_path_bar_hbox, 1, 1, 1, 1);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (priv->save_folder_label), priv->browse_path_bar);
}
+ else
+ g_assert_not_reached ();
- location_mode_set (impl, new_mode, FALSE);
+ g_object_unref (priv->browse_path_bar_hbox);
}
-typedef enum {
- PATH_BAR_FOLDER_PATH,
- PATH_BAR_SELECT_A_FOLDER,
- PATH_BAR_ERROR_NO_FILENAME,
- PATH_BAR_ERROR_NO_FOLDER,
- PATH_BAR_RECENTLY_USED,
- PATH_BAR_SEARCH
-} PathBarMode;
-
-/* Sets the info bar to show the appropriate informational or warning message */
+/* Takes the folder stored in a row in the recent_model, and puts it in the pathbar */
static void
-info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode)
+put_recent_folder_in_pathbar (GtkFileChooserDefault *impl, GtkTreeIter *iter)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- char *str;
- gboolean free_str;
- GtkMessageType message_type;
+ GFile *file;
- free_str = FALSE;
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->recent_model), iter,
+ MODEL_COL_FILE, &file,
+ -1);
+ _gtk_path_bar_set_file (GTK_PATH_BAR (priv->browse_path_bar), file, FALSE);
+ g_object_unref (file);
+}
- switch (mode)
+/* Sets the pathbar in the appropriate mode according to the current operation mode and action. This is the
central function for
+ * dealing with the pathbar's widgets; as long as impl->action and impl->operation_mode are set correctly,
then calling this
+ * function will update all the pathbar's widgets.
+ */
+static void
+path_bar_update (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ PathBarMode mode;
+
+ switch (priv->operation_mode)
{
- case PATH_BAR_SELECT_A_FOLDER:
- str = g_strconcat ("<i>", _("Please select a folder below"), "</i>", NULL);
- free_str = TRUE;
- message_type = GTK_MESSAGE_OTHER;
+ case OPERATION_MODE_BROWSE:
+ mode = PATH_BAR_FOLDER_PATH;
break;
- case PATH_BAR_ERROR_NO_FILENAME:
- str = _("Please type a file name");
- message_type = GTK_MESSAGE_WARNING;
+ case OPERATION_MODE_RECENT:
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ {
+ GtkTreeSelection *selection;
+ gboolean have_selected;
+ GtkTreeIter iter;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+
+ /* Save mode means single-selection mode, so the following is valid */
+ have_selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
+
+ if (have_selected)
+ {
+ mode = PATH_BAR_FOLDER_PATH;
+ put_recent_folder_in_pathbar (impl, &iter);
+ }
+ else
+ mode = PATH_BAR_SELECT_A_FOLDER;
+ }
+ else
+ mode = PATH_BAR_RECENTLY_USED;
+
break;
- case PATH_BAR_ERROR_NO_FOLDER:
- str = _("Please select a folder below");
- message_type = GTK_MESSAGE_WARNING;
+ case OPERATION_MODE_SEARCH:
+ mode = PATH_BAR_SEARCH;
break;
default:
@@ -2625,2534 +2576,2199 @@ info_bar_set (GtkFileChooserDefault *impl, PathBarMode mode)
return;
}
- gtk_info_bar_set_message_type (GTK_INFO_BAR (priv->browse_select_a_folder_info_bar), message_type);
- gtk_image_set_from_stock (GTK_IMAGE (priv->browse_select_a_folder_icon),
- (message_type == GTK_MESSAGE_WARNING) ? GTK_STOCK_DIALOG_WARNING :
GTK_STOCK_DIRECTORY,
- GTK_ICON_SIZE_MENU);
- gtk_label_set_markup (GTK_LABEL (priv->browse_select_a_folder_label), str);
-
- if (free_str)
- g_free (str);
+ path_bar_set_mode (impl, mode);
}
-/* Sets the path bar's mode to show a label, the actual folder path, or a
- * warning message. You may call this function with PATH_BAR_ERROR_* directly
- * if the pathbar is already showing the widgets you expect; otherwise, call
- * path_bar_update() instead to set the appropriate widgets automatically.
- */
static void
-path_bar_set_mode (GtkFileChooserDefault *impl, PathBarMode mode)
+operation_mode_discard_search_widgets (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gboolean path_bar_visible = FALSE;
- gboolean special_mode_widgets_visible = FALSE;
- gboolean info_bar_visible = FALSE;
- gboolean create_folder_visible = FALSE;
-
- char *tmp;
- switch (mode)
+ if (priv->search_hbox)
{
- case PATH_BAR_FOLDER_PATH:
- path_bar_visible = TRUE;
- break;
-
- case PATH_BAR_SELECT_A_FOLDER:
- case PATH_BAR_ERROR_NO_FILENAME:
- case PATH_BAR_ERROR_NO_FOLDER:
- info_bar_set (impl, mode);
- info_bar_visible = TRUE;
- break;
-
- case PATH_BAR_RECENTLY_USED:
- gtk_image_set_from_icon_name (GTK_IMAGE (priv->browse_special_mode_icon), "document-open-recent",
GTK_ICON_SIZE_BUTTON);
+ gtk_widget_destroy (priv->search_hbox);
- tmp = g_strdup_printf ("<b>%s</b>", _("Recently Used"));
- gtk_label_set_markup (GTK_LABEL (priv->browse_special_mode_label), tmp);
- g_free (tmp);
+ priv->search_hbox = NULL;
+ priv->search_entry = NULL;
+ }
+}
- special_mode_widgets_visible = TRUE;
+/* Stops running operations like populating the browse model, searches, and the recent-files model */
+static void
+operation_mode_stop (GtkFileChooserDefault *impl, OperationMode mode)
+{
+ switch (mode)
+ {
+ case OPERATION_MODE_BROWSE:
+ stop_loading_and_clear_list_model (impl, TRUE);
break;
- case PATH_BAR_SEARCH:
- gtk_image_set_from_stock (GTK_IMAGE (priv->browse_special_mode_icon), GTK_STOCK_FIND,
GTK_ICON_SIZE_BUTTON);
+ case OPERATION_MODE_SEARCH:
+ search_stop_searching (impl, FALSE);
+ search_clear_model (impl, TRUE);
- tmp = g_strdup_printf ("<b>%s</b>", _("Search:"));
- gtk_label_set_markup (GTK_LABEL (priv->browse_special_mode_label), tmp);
- g_free (tmp);
+ operation_mode_discard_search_widgets (impl);
+ break;
- special_mode_widgets_visible = TRUE;
+ case OPERATION_MODE_RECENT:
+ recent_stop_loading (impl);
+ recent_clear_model (impl, TRUE);
break;
default:
g_assert_not_reached ();
}
-
- gtk_widget_set_visible (priv->browse_path_bar, path_bar_visible);
- gtk_widget_set_visible (priv->browse_special_mode_icon, special_mode_widgets_visible);
- gtk_widget_set_visible (priv->browse_special_mode_label, special_mode_widgets_visible);
- gtk_widget_set_visible (priv->browse_select_a_folder_info_bar, info_bar_visible);
-
- if (path_bar_visible)
- {
- if (priv->create_folders
- && priv->action != GTK_FILE_CHOOSER_ACTION_OPEN
- && priv->operation_mode != OPERATION_MODE_RECENT)
- create_folder_visible = TRUE;
- }
-
- gtk_widget_set_visible (priv->browse_new_folder_button, create_folder_visible);
}
-
-static GObject*
-gtk_file_chooser_default_constructor (GType type,
- guint n_construct_properties,
- GObjectConstructParam *construct_params)
+static void
+operation_mode_set_browse (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl;
- GtkFileChooserDefaultPrivate *priv;
- GObject *object;
-
- profile_start ("start", NULL);
-
- object = G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->constructor (type,
- n_construct_properties,
- construct_params);
- impl = GTK_FILE_CHOOSER_DEFAULT (object);
- priv = impl->priv;
-
- g_assert (priv->file_system);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- update_appearance (impl);
+ path_bar_update (impl);
- profile_end ("end", NULL);
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ gtk_widget_show (priv->location_button);
+ location_mode_set (impl, priv->location_mode, TRUE);
- return object;
+ if (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)
+ gtk_widget_show (priv->location_entry_box);
+ }
}
-/* Sets the extra_widget by packing it in the appropriate place */
static void
-set_extra_widget (GtkFileChooserDefault *impl,
- GtkWidget *extra_widget)
+operation_mode_set_search (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (extra_widget)
- {
- g_object_ref (extra_widget);
- /* FIXME: is this right ? */
- gtk_widget_show (extra_widget);
- }
-
- if (priv->extra_widget)
- {
- gtk_container_remove (GTK_CONTAINER (priv->extra_align), priv->extra_widget);
- g_object_unref (priv->extra_widget);
- }
+ g_assert (priv->search_hbox == NULL);
+ g_assert (priv->search_entry == NULL);
+ g_assert (priv->search_model == NULL);
- priv->extra_widget = extra_widget;
- if (priv->extra_widget)
- {
- gtk_container_add (GTK_CONTAINER (priv->extra_align), priv->extra_widget);
- gtk_widget_show (priv->extra_align);
- }
- else
- gtk_widget_hide (priv->extra_align);
+ search_setup_widgets (impl);
}
static void
-switch_to_home_dir (GtkFileChooserDefault *impl)
+operation_mode_set_recent (GtkFileChooserDefault *impl)
{
- const gchar *home = g_get_home_dir ();
- GFile *home_file;
-
- if (home == NULL)
- return;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- home_file = g_file_new_for_path (home);
+ path_bar_update (impl);
- gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (impl), home_file, NULL); /* NULL-GError */
+ /* Hide the location widgets temporarily */
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ gtk_widget_hide (priv->location_button);
+ gtk_widget_hide (priv->location_entry_box);
+ }
- g_object_unref (home_file);
+ recent_start_loading (impl);
}
static void
-set_local_only (GtkFileChooserDefault *impl,
- gboolean local_only)
+operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GFile *file;
- if (local_only != priv->local_only)
+ operation_mode_stop (impl, priv->operation_mode);
+
+ priv->operation_mode = mode;
+
+ switch (priv->operation_mode)
{
- priv->local_only = local_only;
+ case OPERATION_MODE_BROWSE:
+ operation_mode_set_browse (impl);
+ break;
- if (priv->location_entry)
- _gtk_file_chooser_entry_set_local_only (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), local_only);
+ case OPERATION_MODE_SEARCH:
+ operation_mode_set_search (impl);
+ break;
- if (local_only && priv->current_folder &&
- !_gtk_file_has_native_path (priv->current_folder))
- {
- /* If we are pointing to a non-local folder, make an effort to change
- * back to a local folder, but it's really up to the app to not cause
- * such a situation, so we ignore errors.
- */
- switch_to_home_dir (impl);
- }
+ case OPERATION_MODE_RECENT:
+ operation_mode_set_recent (impl);
+ file = g_file_new_for_uri ("recent:///");
+ gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (priv->places_sidebar), file);
+ g_object_unref (file);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ return;
}
}
-/* Sets the file chooser to multiple selection mode */
+/* This function is basically a do_all function.
+ *
+ * It sets the visibility on all the widgets based on the current state, and
+ * moves the custom_widget if needed.
+ */
static void
-set_select_multiple (GtkFileChooserDefault *impl,
- gboolean select_multiple,
- gboolean property_notify)
+update_appearance (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeSelection *selection;
- GtkSelectionMode mode;
- if (select_multiple == priv->select_multiple)
- return;
+ save_path_bar (impl);
- mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ {
+ const char *text;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- gtk_tree_selection_set_mode (selection, mode);
+ gtk_widget_hide (priv->location_button);
+ save_widgets_create (impl);
- gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (priv->browse_files_tree_view), select_multiple);
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ text = _("Save in _folder:");
+ else
+ text = _("Create in _folder:");
- priv->select_multiple = select_multiple;
- g_object_notify (G_OBJECT (impl), "select-multiple");
+ gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->save_folder_label), text);
- check_preview_change (impl);
-}
+ if (priv->select_multiple)
+ {
+ g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
+ "Re-setting to single selection mode.");
+ set_select_multiple (impl, FALSE, TRUE);
+ }
+ }
+ else if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ gtk_widget_show (priv->location_button);
+ save_widgets_destroy (impl);
+ location_mode_set (impl, priv->location_mode, TRUE);
+ }
-static void
-set_file_system_backend (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (priv->location_entry)
+ _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action);
- profile_start ("start for backend", "default");
+ restore_path_bar (impl);
+ path_bar_update (impl);
- priv->file_system = _gtk_file_system_new ();
+ /* This *is* needed; we need to redraw the file list because the "sensitivity"
+ * of files may change depending whether we are in a file or folder-only mode.
+ */
+ gtk_widget_queue_draw (priv->browse_files_tree_view);
- profile_end ("end", NULL);
+ emit_default_size_changed (impl);
}
static void
-unset_file_system_backend (GtkFileChooserDefault *impl)
+gtk_file_chooser_default_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+
{
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
GtkFileChooserDefaultPrivate *priv = impl->priv;
- g_object_unref (priv->file_system);
-
- priv->file_system = NULL;
-}
-
-/* Saves the widgets around the pathbar so they can be reparented later
- * in the correct place. This function must be called paired with
- * restore_path_bar().
- */
-static void
-save_path_bar (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkWidget *parent;
-
- g_object_ref (priv->browse_path_bar_hbox);
-
- parent = gtk_widget_get_parent (priv->browse_path_bar_hbox);
- if (parent)
- gtk_container_remove (GTK_CONTAINER (parent), priv->browse_path_bar_hbox);
-}
+ switch (prop_id)
+ {
+ case GTK_FILE_CHOOSER_PROP_ACTION:
+ {
+ GtkFileChooserAction action = g_value_get_enum (value);
-/* Reparents the path bar and the "Create folder" button to the right place:
- * Above the file list in Open mode, or to the right of the "Save in folder:"
- * label in Save mode. The save_path_bar() function must be called before this
- * one.
- */
-static void
-restore_path_bar (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (action != priv->action)
+ {
+ gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
+
+ if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ && priv->select_multiple)
+ {
+ g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
+ "this is not allowed in multiple selection mode. Resetting the file chooser "
+ "to single selection mode.");
+ set_select_multiple (impl, FALSE, TRUE);
+ }
+ priv->action = action;
+ update_cell_renderer_attributes (impl);
+ update_appearance (impl);
+ settings_load (impl);
+ }
+ }
+ break;
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- {
- gtk_box_pack_start (GTK_BOX (priv->browse_header_box), priv->browse_path_bar_hbox, FALSE, FALSE, 0);
- gtk_box_reorder_child (GTK_BOX (priv->browse_header_box), priv->browse_path_bar_hbox, 0);
- }
- else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- {
- gtk_widget_set_hexpand (priv->browse_path_bar_hbox, TRUE);
- gtk_grid_attach (GTK_GRID (priv->save_widgets_table), priv->browse_path_bar_hbox, 1, 1, 1, 1);
- gtk_label_set_mnemonic_widget (GTK_LABEL (priv->save_folder_label), priv->browse_path_bar);
- }
- else
- g_assert_not_reached ();
+ case GTK_FILE_CHOOSER_PROP_FILTER:
+ set_current_filter (impl, g_value_get_object (value));
+ break;
- g_object_unref (priv->browse_path_bar_hbox);
-}
+ case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
+ set_local_only (impl, g_value_get_boolean (value));
+ break;
-/* Takes the folder stored in a row in the recent_model, and puts it in the pathbar */
-static void
-put_recent_folder_in_pathbar (GtkFileChooserDefault *impl, GtkTreeIter *iter)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GFile *file;
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
+ set_preview_widget (impl, g_value_get_object (value));
+ break;
- gtk_tree_model_get (GTK_TREE_MODEL (priv->recent_model), iter,
- MODEL_COL_FILE, &file,
- -1);
- _gtk_path_bar_set_file (GTK_PATH_BAR (priv->browse_path_bar), file, FALSE);
- g_object_unref (file);
-}
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
+ priv->preview_widget_active = g_value_get_boolean (value);
+ update_preview_widget_visibility (impl);
+ break;
-/* Sets the pathbar in the appropriate mode according to the current operation mode and action. This is the
central function for
- * dealing with the pathbar's widgets; as long as impl->action and impl->operation_mode are set correctly,
then calling this
- * function will update all the pathbar's widgets.
- */
-static void
-path_bar_update (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- PathBarMode mode;
+ case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
+ priv->use_preview_label = g_value_get_boolean (value);
+ update_preview_widget_visibility (impl);
+ break;
- switch (priv->operation_mode)
- {
- case OPERATION_MODE_BROWSE:
- mode = PATH_BAR_FOLDER_PATH;
+ case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
+ set_extra_widget (impl, g_value_get_object (value));
break;
- case OPERATION_MODE_RECENT:
- if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
- {
- GtkTreeSelection *selection;
- gboolean have_selected;
- GtkTreeIter iter;
+ case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
+ {
+ gboolean select_multiple = g_value_get_boolean (value);
+ if ((priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ && select_multiple)
+ {
+ g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
+ "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and "
+ "leaving the file chooser in single selection mode.");
+ return;
+ }
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ set_select_multiple (impl, select_multiple, FALSE);
+ }
+ break;
- /* Save mode means single-selection mode, so the following is valid */
- have_selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
+ case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
+ {
+ gboolean show_hidden = g_value_get_boolean (value);
+ if (show_hidden != priv->show_hidden)
+ {
+ priv->show_hidden = show_hidden;
- if (have_selected)
- {
- mode = PATH_BAR_FOLDER_PATH;
- put_recent_folder_in_pathbar (impl, &iter);
- }
- else
- mode = PATH_BAR_SELECT_A_FOLDER;
- }
- else
- mode = PATH_BAR_RECENTLY_USED;
+ if (priv->browse_files_model)
+ _gtk_file_system_model_set_show_hidden (priv->browse_files_model, show_hidden);
+ }
+ }
+ break;
+ case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
+ {
+ gboolean do_overwrite_confirmation = g_value_get_boolean (value);
+ priv->do_overwrite_confirmation = do_overwrite_confirmation;
+ }
break;
- case OPERATION_MODE_SEARCH:
- mode = PATH_BAR_SEARCH;
+ case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
+ {
+ gboolean create_folders = g_value_get_boolean (value);
+ priv->create_folders = create_folders;
+ update_appearance (impl);
+ }
break;
default:
- g_assert_not_reached ();
- return;
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
-
- path_bar_set_mode (impl, mode);
}
static void
-operation_mode_discard_search_widgets (GtkFileChooserDefault *impl)
+gtk_file_chooser_default_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (priv->search_hbox)
+ switch (prop_id)
{
- gtk_widget_destroy (priv->search_hbox);
+ case GTK_FILE_CHOOSER_PROP_ACTION:
+ g_value_set_enum (value, priv->action);
+ break;
- priv->search_hbox = NULL;
- priv->search_entry = NULL;
- }
-}
+ case GTK_FILE_CHOOSER_PROP_FILTER:
+ g_value_set_object (value, priv->current_filter);
+ break;
-/* Stops running operations like populating the browse model, searches, and the recent-files model */
-static void
-operation_mode_stop (GtkFileChooserDefault *impl, OperationMode mode)
-{
- switch (mode)
- {
- case OPERATION_MODE_BROWSE:
- stop_loading_and_clear_list_model (impl, TRUE);
+ case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
+ g_value_set_boolean (value, priv->local_only);
break;
- case OPERATION_MODE_SEARCH:
- search_stop_searching (impl, FALSE);
- search_clear_model (impl, TRUE);
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
+ g_value_set_object (value, priv->preview_widget);
+ break;
- operation_mode_discard_search_widgets (impl);
+ case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
+ g_value_set_boolean (value, priv->preview_widget_active);
break;
- case OPERATION_MODE_RECENT:
- recent_stop_loading (impl);
- recent_clear_model (impl, TRUE);
+ case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
+ g_value_set_boolean (value, priv->use_preview_label);
break;
- default:
- g_assert_not_reached ();
- }
-}
+ case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
+ g_value_set_object (value, priv->extra_widget);
+ break;
-static void
-operation_mode_set_browse (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
+ g_value_set_boolean (value, priv->select_multiple);
+ break;
- path_bar_update (impl);
+ case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
+ g_value_set_boolean (value, priv->show_hidden);
+ break;
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- {
- gtk_widget_show (priv->location_button);
- location_mode_set (impl, priv->location_mode, TRUE);
+ case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
+ g_value_set_boolean (value, priv->do_overwrite_confirmation);
+ break;
- if (priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)
- gtk_widget_show (priv->location_entry_box);
+ case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
+ g_value_set_boolean (value, priv->create_folders);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
}
+/* This cancels everything that may be going on in the background. */
static void
-operation_mode_set_search (GtkFileChooserDefault *impl)
+cancel_all_operations (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GSList *l;
- g_assert (priv->search_hbox == NULL);
- g_assert (priv->search_entry == NULL);
- g_assert (priv->search_model == NULL);
+ pending_select_files_free (impl);
- search_setup_widgets (impl);
+ if (priv->reload_icon_cancellables)
+ {
+ for (l = priv->reload_icon_cancellables; l; l = l->next)
+ {
+ GCancellable *cancellable = G_CANCELLABLE (l->data);
+ g_cancellable_cancel (cancellable);
+ }
+ g_slist_free (priv->reload_icon_cancellables);
+ priv->reload_icon_cancellables = NULL;
+ }
+
+ if (priv->loading_shortcuts)
+ {
+ for (l = priv->loading_shortcuts; l; l = l->next)
+ {
+ GCancellable *cancellable = G_CANCELLABLE (l->data);
+ g_cancellable_cancel (cancellable);
+ }
+ g_slist_free (priv->loading_shortcuts);
+ priv->loading_shortcuts = NULL;
+ }
+
+ if (priv->file_list_drag_data_received_cancellable)
+ {
+ g_cancellable_cancel (priv->file_list_drag_data_received_cancellable);
+ priv->file_list_drag_data_received_cancellable = NULL;
+ }
+
+ if (priv->update_current_folder_cancellable)
+ {
+ g_cancellable_cancel (priv->update_current_folder_cancellable);
+ priv->update_current_folder_cancellable = NULL;
+ }
+
+ if (priv->should_respond_get_info_cancellable)
+ {
+ g_cancellable_cancel (priv->should_respond_get_info_cancellable);
+ priv->should_respond_get_info_cancellable = NULL;
+ }
+
+ if (priv->file_exists_get_info_cancellable)
+ {
+ g_cancellable_cancel (priv->file_exists_get_info_cancellable);
+ priv->file_exists_get_info_cancellable = NULL;
+ }
+
+ if (priv->update_from_entry_cancellable)
+ {
+ g_cancellable_cancel (priv->update_from_entry_cancellable);
+ priv->update_from_entry_cancellable = NULL;
+ }
+
+ if (priv->shortcuts_activate_iter_cancellable)
+ {
+ g_cancellable_cancel (priv->shortcuts_activate_iter_cancellable);
+ priv->shortcuts_activate_iter_cancellable = NULL;
+ }
+
+ search_stop_searching (impl, TRUE);
+ recent_stop_loading (impl);
}
+/* Removes the settings signal handler. It's safe to call multiple times */
static void
-operation_mode_set_recent (GtkFileChooserDefault *impl)
+remove_settings_signal (GtkFileChooserDefault *impl,
+ GdkScreen *screen)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- path_bar_update (impl);
-
- /* Hide the location widgets temporarily */
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ if (priv->settings_signal_id)
{
- gtk_widget_hide (priv->location_button);
- gtk_widget_hide (priv->location_entry_box);
- }
+ GtkSettings *settings;
- recent_start_loading (impl);
+ settings = gtk_settings_get_for_screen (screen);
+ g_signal_handler_disconnect (settings,
+ priv->settings_signal_id);
+ priv->settings_signal_id = 0;
+ }
}
static void
-operation_mode_set (GtkFileChooserDefault *impl, OperationMode mode)
+gtk_file_chooser_default_dispose (GObject *object)
{
+ GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GFile *file;
-
- operation_mode_stop (impl, priv->operation_mode);
- priv->operation_mode = mode;
+ cancel_all_operations (impl);
- switch (priv->operation_mode)
+ if (priv->extra_widget)
{
- case OPERATION_MODE_BROWSE:
- operation_mode_set_browse (impl);
- break;
-
- case OPERATION_MODE_SEARCH:
- operation_mode_set_search (impl);
- break;
+ g_object_unref (priv->extra_widget);
+ priv->extra_widget = NULL;
+ }
- case OPERATION_MODE_RECENT:
- operation_mode_set_recent (impl);
- file = g_file_new_for_uri ("recent:///");
- gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (priv->places_sidebar), file);
- g_object_unref (file);
- break;
+ remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
- default:
- g_assert_not_reached ();
- return;
+ if (priv->bookmarks_manager)
+ {
+ _gtk_bookmarks_manager_free (priv->bookmarks_manager);
+ priv->bookmarks_manager = NULL;
}
+
+ G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
}
-/* This function is basically a do_all function.
- *
- * It sets the visibility on all the widgets based on the current state, and
- * moves the custom_widget if needed.
+/* We override show-all since we have internal widgets that
+ * shouldn't be shown when you call show_all(), like the filter
+ * combo box.
*/
static void
-update_appearance (GtkFileChooserDefault *impl)
+gtk_file_chooser_default_show_all (GtkWidget *widget)
{
+ GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
GtkFileChooserDefaultPrivate *priv = impl->priv;
- save_path_bar (impl);
+ gtk_widget_show (widget);
- if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- {
- const char *text;
+ if (priv->extra_widget)
+ gtk_widget_show_all (priv->extra_widget);
+}
- gtk_widget_hide (priv->location_button);
- save_widgets_create (impl);
+/* Handler for GtkWindow::set-focus; this is where we save the last-focused
+ * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
+ */
+static void
+toplevel_set_focus_cb (GtkWindow *window,
+ GtkWidget *focus,
+ GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
- text = _("Save in _folder:");
- else
- text = _("Create in _folder:");
+ priv->toplevel_last_focus_widget = gtk_window_get_focus (window);
+}
- gtk_label_set_text_with_mnemonic (GTK_LABEL (priv->save_folder_label), text);
+/* We monitor the focus widget on our toplevel to be able to know which widget
+ * was last focused at the time our "should_respond" method gets called.
+ */
+static void
+gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
+ GtkWidget *previous_toplevel)
+{
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkWidget *toplevel;
- if (priv->select_multiple)
- {
- g_warning ("Save mode cannot be set in conjunction with multiple selection mode. "
- "Re-setting to single selection mode.");
- set_select_multiple (impl, FALSE, TRUE);
- }
+ toplevel = gtk_widget_get_toplevel (widget);
+
+ if (previous_toplevel &&
+ priv->toplevel_set_focus_id != 0)
+ {
+ g_signal_handler_disconnect (previous_toplevel,
+ priv->toplevel_set_focus_id);
+ priv->toplevel_set_focus_id = 0;
+ priv->toplevel_last_focus_widget = NULL;
}
- else if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+
+ if (gtk_widget_is_toplevel (toplevel))
{
- gtk_widget_show (priv->location_button);
- save_widgets_destroy (impl);
- location_mode_set (impl, priv->location_mode, TRUE);
+ g_assert (priv->toplevel_set_focus_id == 0);
+ priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
+ G_CALLBACK (toplevel_set_focus_cb), impl);
+ priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
}
+}
- if (priv->location_entry)
- _gtk_file_chooser_entry_set_action (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), priv->action);
+/* Changes the icons wherever it is needed */
+static void
+change_icon_theme (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkSettings *settings;
+ gint width, height;
- restore_path_bar (impl);
- path_bar_update (impl);
+ profile_start ("start", NULL);
- /* This *is* needed; we need to redraw the file list because the "sensitivity"
- * of files may change depending whether we are in a file or folder-only mode.
- */
- gtk_widget_queue_draw (priv->browse_files_tree_view);
+ settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
- emit_default_size_changed (impl);
+ if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
+ priv->icon_size = MAX (width, height);
+ else
+ priv->icon_size = FALLBACK_ICON_SIZE;
+
+ /* the first cell in the first column is the icon column, and we have a fixed size there */
+ set_icon_cell_renderer_fixed_size (impl);
+
+ if (priv->browse_files_model)
+ _gtk_file_system_model_clear_cache (priv->browse_files_model, MODEL_COL_PIXBUF);
+ gtk_widget_queue_resize (priv->browse_files_tree_view);
+
+ profile_end ("end", NULL);
}
+/* Callback used when a GtkSettings value changes */
static void
-gtk_file_chooser_default_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+settings_notify_cb (GObject *object,
+ GParamSpec *pspec,
+ GtkFileChooserDefault *impl)
+{
+ const char *name;
+
+ profile_start ("start", NULL);
+
+ name = g_param_spec_get_name (pspec);
+
+ if (strcmp (name, "gtk-icon-theme-name") == 0 ||
+ strcmp (name, "gtk-icon-sizes") == 0)
+ change_icon_theme (impl);
+
+ profile_end ("end", NULL);
+}
+/* Installs a signal handler for GtkSettings so that we can monitor changes in
+ * the icon theme.
+ */
+static void
+check_icon_theme (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkSettings *settings;
- switch (prop_id)
+ profile_start ("start", NULL);
+
+ if (priv->settings_signal_id)
{
- case GTK_FILE_CHOOSER_PROP_ACTION:
- {
- GtkFileChooserAction action = g_value_get_enum (value);
+ profile_end ("end", NULL);
+ return;
+ }
- if (action != priv->action)
- {
- gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
-
- if ((action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- && priv->select_multiple)
- {
- g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
- "this is not allowed in multiple selection mode. Resetting the file chooser "
- "to single selection mode.");
- set_select_multiple (impl, FALSE, TRUE);
- }
- priv->action = action;
- update_cell_renderer_attributes (impl);
- update_appearance (impl);
- settings_load (impl);
- }
- }
- break;
+ if (gtk_widget_has_screen (GTK_WIDGET (impl)))
+ {
+ settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
+ priv->settings_signal_id = g_signal_connect (settings, "notify",
+ G_CALLBACK (settings_notify_cb), impl);
- case GTK_FILE_CHOOSER_PROP_FILTER:
- set_current_filter (impl, g_value_get_object (value));
- break;
+ change_icon_theme (impl);
+ }
- case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
- set_local_only (impl, g_value_get_boolean (value));
- break;
+ profile_end ("end", NULL);
+}
- case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
- set_preview_widget (impl, g_value_get_object (value));
- break;
+static void
+gtk_file_chooser_default_style_updated (GtkWidget *widget)
+{
+ GtkFileChooserDefault *impl;
- case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
- priv->preview_widget_active = g_value_get_boolean (value);
- update_preview_widget_visibility (impl);
- break;
+ profile_start ("start", NULL);
- case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
- priv->use_preview_label = g_value_get_boolean (value);
- update_preview_widget_visibility (impl);
- break;
+ impl = GTK_FILE_CHOOSER_DEFAULT (widget);
- case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
- set_extra_widget (impl, g_value_get_object (value));
- break;
+ profile_msg (" parent class style_udpated start", NULL);
+ GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_updated (widget);
+ profile_msg (" parent class style_updated end", NULL);
- case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
- {
- gboolean select_multiple = g_value_get_boolean (value);
- if ((priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- && select_multiple)
- {
- g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
- "not allowed in SAVE or CREATE_FOLDER modes. Ignoring the change and "
- "leaving the file chooser in single selection mode.");
- return;
- }
+ if (gtk_widget_has_screen (GTK_WIDGET (impl)))
+ change_icon_theme (impl);
- set_select_multiple (impl, select_multiple, FALSE);
- }
- break;
+ emit_default_size_changed (impl);
- case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
- {
- gboolean show_hidden = g_value_get_boolean (value);
- if (show_hidden != priv->show_hidden)
- {
- priv->show_hidden = show_hidden;
+ profile_end ("end", NULL);
+}
- if (priv->browse_files_model)
- _gtk_file_system_model_set_show_hidden (priv->browse_files_model, show_hidden);
- }
- }
- break;
+static void
+gtk_file_chooser_default_screen_changed (GtkWidget *widget,
+ GdkScreen *previous_screen)
+{
+ GtkFileChooserDefault *impl;
- case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
- {
- gboolean do_overwrite_confirmation = g_value_get_boolean (value);
- priv->do_overwrite_confirmation = do_overwrite_confirmation;
- }
- break;
+ profile_start ("start", NULL);
- case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
- {
- gboolean create_folders = g_value_get_boolean (value);
- priv->create_folders = create_folders;
- update_appearance (impl);
- }
- break;
+ impl = GTK_FILE_CHOOSER_DEFAULT (widget);
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
+ GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
+
+ remove_settings_signal (impl, previous_screen);
+ check_icon_theme (impl);
+
+ emit_default_size_changed (impl);
+
+ profile_end ("end", NULL);
}
static void
-gtk_file_chooser_default_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+set_sort_column (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeSortable *sortable;
- switch (prop_id)
- {
- case GTK_FILE_CHOOSER_PROP_ACTION:
- g_value_set_enum (value, priv->action);
- break;
+ sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)));
- case GTK_FILE_CHOOSER_PROP_FILTER:
- g_value_set_object (value, priv->current_filter);
- break;
+ /* can happen when we're still populating the model */
+ if (sortable == NULL)
+ return;
- case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
- g_value_set_boolean (value, priv->local_only);
- break;
+ gtk_tree_sortable_set_sort_column_id (sortable,
+ priv->sort_column,
+ priv->sort_order);
+}
- case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
- g_value_set_object (value, priv->preview_widget);
- break;
+static void
+settings_load (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ LocationMode location_mode;
+ gboolean show_hidden;
+ gboolean show_size_column;
+ gint sort_column;
+ GtkSortType sort_order;
+ StartupMode startup_mode;
+ gint sidebar_width;
+ GSettings *settings;
- case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
- g_value_set_boolean (value, priv->preview_widget_active);
- break;
+ settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl));
- case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
- g_value_set_boolean (value, priv->use_preview_label);
- break;
+ location_mode = g_settings_get_enum (settings, SETTINGS_KEY_LOCATION_MODE);
+ show_hidden = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_HIDDEN);
+ show_size_column = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN);
+ sort_column = g_settings_get_enum (settings, SETTINGS_KEY_SORT_COLUMN);
+ sort_order = g_settings_get_enum (settings, SETTINGS_KEY_SORT_ORDER);
+ sidebar_width = g_settings_get_int (settings, SETTINGS_KEY_SIDEBAR_WIDTH);
+ startup_mode = g_settings_get_enum (settings, SETTINGS_KEY_STARTUP_MODE);
- case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
- g_value_set_object (value, priv->extra_widget);
- break;
+ location_mode_set (impl, location_mode, TRUE);
- case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
- g_value_set_boolean (value, priv->select_multiple);
- break;
+ gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
- case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
- g_value_set_boolean (value, priv->show_hidden);
- break;
+ priv->show_size_column = show_size_column;
+ gtk_tree_view_column_set_visible (priv->list_size_column, show_size_column);
- case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
- g_value_set_boolean (value, priv->do_overwrite_confirmation);
- break;
+ priv->sort_column = sort_column;
+ priv->sort_order = sort_order;
+ priv->startup_mode = startup_mode;
- case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
- g_value_set_boolean (value, priv->create_folders);
- break;
+ /* We don't call set_sort_column() here as the models may not have been
+ * created yet. The individual functions that create and set the models will
+ * call set_sort_column() themselves.
+ */
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ gtk_paned_set_position (GTK_PANED (priv->browse_widgets_hpaned), sidebar_width);
}
-/* This cancels everything that may be going on in the background. */
static void
-cancel_all_operations (GtkFileChooserDefault *impl)
+settings_save (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GSList *l;
+ GSettings *settings;
- pending_select_files_free (impl);
+ settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl));
- if (priv->reload_icon_cancellables)
- {
- for (l = priv->reload_icon_cancellables; l; l = l->next)
- {
- GCancellable *cancellable = G_CANCELLABLE (l->data);
- g_cancellable_cancel (cancellable);
- }
- g_slist_free (priv->reload_icon_cancellables);
- priv->reload_icon_cancellables = NULL;
- }
+ /* All the other state */
- if (priv->loading_shortcuts)
- {
- for (l = priv->loading_shortcuts; l; l = l->next)
- {
- GCancellable *cancellable = G_CANCELLABLE (l->data);
- g_cancellable_cancel (cancellable);
- }
- g_slist_free (priv->loading_shortcuts);
- priv->loading_shortcuts = NULL;
- }
+ g_settings_set_enum (settings, SETTINGS_KEY_LOCATION_MODE, priv->location_mode);
+ g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_HIDDEN,
+ gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
+ g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN, priv->show_size_column);
+ g_settings_set_enum (settings, SETTINGS_KEY_SORT_COLUMN, priv->sort_column);
+ g_settings_set_enum (settings, SETTINGS_KEY_SORT_ORDER, priv->sort_order);
+ g_settings_set_int (settings, SETTINGS_KEY_SIDEBAR_WIDTH,
+ gtk_paned_get_position (GTK_PANED (priv->browse_widgets_hpaned)));
- if (priv->file_list_drag_data_received_cancellable)
- {
- g_cancellable_cancel (priv->file_list_drag_data_received_cancellable);
- priv->file_list_drag_data_received_cancellable = NULL;
- }
-
- if (priv->update_current_folder_cancellable)
- {
- g_cancellable_cancel (priv->update_current_folder_cancellable);
- priv->update_current_folder_cancellable = NULL;
- }
-
- if (priv->should_respond_get_info_cancellable)
- {
- g_cancellable_cancel (priv->should_respond_get_info_cancellable);
- priv->should_respond_get_info_cancellable = NULL;
- }
-
- if (priv->file_exists_get_info_cancellable)
- {
- g_cancellable_cancel (priv->file_exists_get_info_cancellable);
- priv->file_exists_get_info_cancellable = NULL;
- }
-
- if (priv->update_from_entry_cancellable)
- {
- g_cancellable_cancel (priv->update_from_entry_cancellable);
- priv->update_from_entry_cancellable = NULL;
- }
-
- if (priv->shortcuts_activate_iter_cancellable)
- {
- g_cancellable_cancel (priv->shortcuts_activate_iter_cancellable);
- priv->shortcuts_activate_iter_cancellable = NULL;
- }
-
- search_stop_searching (impl, TRUE);
- recent_stop_loading (impl);
+ /* Now apply the settings */
+ g_settings_apply (settings);
}
-/* Removes the settings signal handler. It's safe to call multiple times */
+/* GtkWidget::realize method */
static void
-remove_settings_signal (GtkFileChooserDefault *impl,
- GdkScreen *screen)
+gtk_file_chooser_default_realize (GtkWidget *widget)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkFileChooserDefault *impl;
- if (priv->settings_signal_id)
- {
- GtkSettings *settings;
+ impl = GTK_FILE_CHOOSER_DEFAULT (widget);
- settings = gtk_settings_get_for_screen (screen);
- g_signal_handler_disconnect (settings,
- priv->settings_signal_id);
- priv->settings_signal_id = 0;
- }
+ GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->realize (widget);
+
+ emit_default_size_changed (impl);
}
+/* Changes the current folder to $CWD */
static void
-gtk_file_chooser_default_dispose (GObject *object)
+switch_to_cwd (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = (GtkFileChooserDefault *) object;
- GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- cancel_all_operations (impl);
-
- if (priv->extra_widget)
- {
- g_object_unref (priv->extra_widget);
- priv->extra_widget = NULL;
- }
-
- remove_settings_signal (impl, gtk_widget_get_screen (GTK_WIDGET (impl)));
-
- if (priv->bookmarks_manager)
- {
- _gtk_bookmarks_manager_free (priv->bookmarks_manager);
- priv->bookmarks_manager = NULL;
- }
+ char *current_working_dir;
- G_OBJECT_CLASS (_gtk_file_chooser_default_parent_class)->dispose (object);
+ current_working_dir = g_get_current_dir ();
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), current_working_dir);
+ g_free (current_working_dir);
}
-/* We override show-all since we have internal widgets that
- * shouldn't be shown when you call show_all(), like the filter
- * combo box.
+/* Sets the file chooser to showing Recent Files or $CWD, depending on the
+ * user's settings.
*/
static void
-gtk_file_chooser_default_show_all (GtkWidget *widget)
+set_startup_mode (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefault *impl = (GtkFileChooserDefault *) widget;
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gtk_widget_show (widget);
-
- if (priv->extra_widget)
- gtk_widget_show_all (priv->extra_widget);
-}
+ switch (priv->startup_mode)
+ {
+ case STARTUP_MODE_RECENT:
+ operation_mode_set (impl, OPERATION_MODE_RECENT);
+ break;
-/* Handler for GtkWindow::set-focus; this is where we save the last-focused
- * widget on our toplevel. See gtk_file_chooser_default_hierarchy_changed()
- */
-static void
-toplevel_set_focus_cb (GtkWindow *window,
- GtkWidget *focus,
- GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ case STARTUP_MODE_CWD:
+ switch_to_cwd (impl);
+ break;
- priv->toplevel_last_focus_widget = gtk_window_get_focus (window);
+ default:
+ g_assert_not_reached ();
+ }
}
-/* We monitor the focus widget on our toplevel to be able to know which widget
- * was last focused at the time our "should_respond" method gets called.
- */
-static void
-gtk_file_chooser_default_hierarchy_changed (GtkWidget *widget,
- GtkWidget *previous_toplevel)
+static gboolean
+shortcut_exists (GtkFileChooserDefault *impl, GFile *needle)
{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget);
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkWidget *toplevel;
+ GSList *haystack;
+ GSList *l;
+ gboolean exists;
- toplevel = gtk_widget_get_toplevel (widget);
+ exists = FALSE;
- if (previous_toplevel &&
- priv->toplevel_set_focus_id != 0)
+ haystack = gtk_places_sidebar_list_shortcuts (GTK_PLACES_SIDEBAR (priv->places_sidebar));
+ for (l = haystack; l; l = l->next)
{
- g_signal_handler_disconnect (previous_toplevel,
- priv->toplevel_set_focus_id);
- priv->toplevel_set_focus_id = 0;
- priv->toplevel_last_focus_widget = NULL;
- }
+ GFile *hay;
- if (gtk_widget_is_toplevel (toplevel))
- {
- g_assert (priv->toplevel_set_focus_id == 0);
- priv->toplevel_set_focus_id = g_signal_connect (toplevel, "set-focus",
- G_CALLBACK (toplevel_set_focus_cb), impl);
- priv->toplevel_last_focus_widget = gtk_window_get_focus (GTK_WINDOW (toplevel));
+ hay = G_FILE (l->data);
+ if (g_file_equal (hay, needle))
+ {
+ exists = TRUE;
+ break;
+ }
}
+ g_slist_free_full (haystack, g_object_unref);
+
+ return exists;
}
-/* Changes the icons wherever it is needed */
static void
-change_icon_theme (GtkFileChooserDefault *impl)
+add_cwd_to_sidebar_if_needed (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkSettings *settings;
- gint width, height;
+ char *cwd;
+ GFile *cwd_file;
+ GFile *home_file;
- profile_start ("start", NULL);
+ cwd = g_get_current_dir ();
+ cwd_file = g_file_new_for_path (cwd);
+ g_free (cwd);
- settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
+ if (shortcut_exists (impl, cwd_file))
+ goto out;
- if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU, &width, &height))
- priv->icon_size = MAX (width, height);
- else
- priv->icon_size = FALLBACK_ICON_SIZE;
+ home_file = g_file_new_for_path (g_get_home_dir ());
- /* the first cell in the first column is the icon column, and we have a fixed size there */
- set_icon_cell_renderer_fixed_size (impl);
+ /* We only add an item for $CWD if it is different from $HOME. This way,
+ * applications which get launched from a shell in a terminal (by someone who
+ * knows what they are doing) will get an item for $CWD in the places sidebar,
+ * and "normal" applications launched from the desktop shell (whose $CWD is
+ * $HOME) won't get any extra clutter in the sidebar.
+ */
+ if (!g_file_equal (home_file, cwd_file))
+ gtk_places_sidebar_add_shortcut (GTK_PLACES_SIDEBAR (priv->places_sidebar), cwd_file);
- if (priv->browse_files_model)
- _gtk_file_system_model_clear_cache (priv->browse_files_model, MODEL_COL_PIXBUF);
- gtk_widget_queue_resize (priv->browse_files_tree_view);
+ g_object_unref (home_file);
- profile_end ("end", NULL);
+ out:
+ g_object_unref (cwd_file);
}
-/* Callback used when a GtkSettings value changes */
+/* GtkWidget::map method */
static void
-settings_notify_cb (GObject *object,
- GParamSpec *pspec,
- GtkFileChooserDefault *impl)
+gtk_file_chooser_default_map (GtkWidget *widget)
{
- const char *name;
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
profile_start ("start", NULL);
- name = g_param_spec_get_name (pspec);
-
- if (strcmp (name, "gtk-icon-theme-name") == 0 ||
- strcmp (name, "gtk-icon-sizes") == 0)
- change_icon_theme (impl);
-
- profile_end ("end", NULL);
-}
-
-/* Installs a signal handler for GtkSettings so that we can monitor changes in
- * the icon theme.
- */
-static void
-check_icon_theme (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkSettings *settings;
+ GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
- profile_start ("start", NULL);
+ settings_load (impl);
- if (priv->settings_signal_id)
- {
- profile_end ("end", NULL);
- return;
- }
+ add_cwd_to_sidebar_if_needed (impl);
- if (gtk_widget_has_screen (GTK_WIDGET (impl)))
+ if (priv->operation_mode == OPERATION_MODE_BROWSE)
{
- settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (impl)));
- priv->settings_signal_id = g_signal_connect (settings, "notify",
- G_CALLBACK (settings_notify_cb), impl);
+ switch (priv->reload_state)
+ {
+ case RELOAD_EMPTY:
+ set_startup_mode (impl);
+ break;
+
+ case RELOAD_HAS_FOLDER:
+ /* Nothing; we are already loading or loaded, so we
+ * don't need to reload
+ */
+ break;
- change_icon_theme (impl);
+ default:
+ g_assert_not_reached ();
+ }
}
profile_end ("end", NULL);
}
+/* GtkWidget::unmap method */
static void
-gtk_file_chooser_default_style_updated (GtkWidget *widget)
+gtk_file_chooser_default_unmap (GtkWidget *widget)
{
- GtkFileChooserDefault *impl;
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- profile_start ("start", NULL);
+ settings_save (impl);
- impl = GTK_FILE_CHOOSER_DEFAULT (widget);
-
- profile_msg (" parent class style_udpated start", NULL);
- GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->style_updated (widget);
- profile_msg (" parent class style_updated end", NULL);
-
- if (gtk_widget_has_screen (GTK_WIDGET (impl)))
- change_icon_theme (impl);
-
- emit_default_size_changed (impl);
+ cancel_all_operations (impl);
+ priv->reload_state = RELOAD_EMPTY;
- profile_end ("end", NULL);
+ GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
}
-static void
-gtk_file_chooser_default_screen_changed (GtkWidget *widget,
- GdkScreen *previous_screen)
+#define COMPARE_DIRECTORIES
\
+ GtkFileChooserDefault *impl = user_data;
\
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
\
+ GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
\
+ gboolean dir_a, dir_b;
\
+
\
+ dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_IS_FOLDER));
\
+ dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER));
\
+
\
+ if (dir_a != dir_b)
\
+ return priv->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first
*/
+
+/* Sort callback for the filename column */
+static gint
+name_sort_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
{
- GtkFileChooserDefault *impl;
+ COMPARE_DIRECTORIES;
+ else
+ {
+ const char *key_a, *key_b;
+ gint result;
- profile_start ("start", NULL);
+ key_a = g_value_get_string (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_NAME_COLLATED));
+ key_b = g_value_get_string (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_NAME_COLLATED));
- impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+ if (key_a && key_b)
+ result = strcmp (key_a, key_b);
+ else if (key_a)
+ result = 1;
+ else if (key_b)
+ result = -1;
+ else
+ result = 0;
- if (GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed)
- GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->screen_changed (widget, previous_screen);
+ return result;
+ }
+}
- remove_settings_signal (impl, previous_screen);
- check_icon_theme (impl);
+/* Sort callback for the size column */
+static gint
+size_sort_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ COMPARE_DIRECTORIES;
+ else
+ {
+ gint64 size_a, size_b;
- emit_default_size_changed (impl);
+ size_a = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_SIZE));
+ size_b = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_SIZE));
- profile_end ("end", NULL);
+ return size_a < size_b ? -1 : (size_a == size_b ? 0 : 1);
+ }
}
-static void
-set_sort_column (GtkFileChooserDefault *impl)
+/* Sort callback for the mtime column */
+static gint
+mtime_sort_func (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeSortable *sortable;
-
- sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)));
+ COMPARE_DIRECTORIES;
+ else
+ {
+ glong ta, tb;
- /* can happen when we're still populating the model */
- if (sortable == NULL)
- return;
+ ta = g_value_get_long (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_MTIME));
+ tb = g_value_get_long (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_MTIME));
- gtk_tree_sortable_set_sort_column_id (sortable,
- priv->sort_column,
- priv->sort_order);
+ return ta < tb ? -1 : (ta == tb ? 0 : 1);
+ }
}
+/* Callback used when the sort column changes. We cache the sort order for use
+ * in name_sort_func().
+ */
static void
-settings_load (GtkFileChooserDefault *impl)
+list_sort_column_changed_cb (GtkTreeSortable *sortable,
+ GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- LocationMode location_mode;
- gboolean show_hidden;
- gboolean show_size_column;
- gint sort_column;
- GtkSortType sort_order;
- StartupMode startup_mode;
- gint sidebar_width;
- GSettings *settings;
-
- settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl));
+ gint sort_column_id;
+ GtkSortType sort_type;
- location_mode = g_settings_get_enum (settings, SETTINGS_KEY_LOCATION_MODE);
- show_hidden = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_HIDDEN);
- show_size_column = g_settings_get_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN);
- sort_column = g_settings_get_enum (settings, SETTINGS_KEY_SORT_COLUMN);
- sort_order = g_settings_get_enum (settings, SETTINGS_KEY_SORT_ORDER);
- sidebar_width = g_settings_get_int (settings, SETTINGS_KEY_SIDEBAR_WIDTH);
- startup_mode = g_settings_get_enum (settings, SETTINGS_KEY_STARTUP_MODE);
+ if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type))
+ {
+ priv->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
+ priv->sort_column = sort_column_id;
+ priv->sort_order = sort_type;
+ }
+}
- location_mode_set (impl, location_mode, TRUE);
+static void
+set_busy_cursor (GtkFileChooserDefault *impl,
+ gboolean busy)
+{
+ GtkWidget *widget;
+ GtkWindow *toplevel;
+ GdkDisplay *display;
+ GdkCursor *cursor;
- gtk_file_chooser_set_show_hidden (GTK_FILE_CHOOSER (impl), show_hidden);
+ toplevel = get_toplevel (GTK_WIDGET (impl));
+ widget = GTK_WIDGET (toplevel);
+ if (!toplevel || !gtk_widget_get_realized (widget))
+ return;
- priv->show_size_column = show_size_column;
- gtk_tree_view_column_set_visible (priv->list_size_column, show_size_column);
+ display = gtk_widget_get_display (widget);
- priv->sort_column = sort_column;
- priv->sort_order = sort_order;
- priv->startup_mode = startup_mode;
+ if (busy)
+ cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+ else
+ cursor = NULL;
- /* We don't call set_sort_column() here as the models may not have been
- * created yet. The individual functions that create and set the models will
- * call set_sort_column() themselves.
- */
+ gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
+ gdk_display_flush (display);
- gtk_paned_set_position (GTK_PANED (priv->browse_widgets_hpaned), sidebar_width);
+ if (cursor)
+ g_object_unref (cursor);
}
+/* Creates a sort model to wrap the file system model and sets it on the tree view */
static void
-settings_save (GtkFileChooserDefault *impl)
+load_set_model (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GSettings *settings;
- settings = _gtk_file_chooser_get_settings_for_widget (GTK_WIDGET (impl));
+ profile_start ("start", NULL);
- /* All the other state */
+ g_assert (priv->browse_files_model != NULL);
- g_settings_set_enum (settings, SETTINGS_KEY_LOCATION_MODE, priv->location_mode);
- g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_HIDDEN,
- gtk_file_chooser_get_show_hidden (GTK_FILE_CHOOSER (impl)));
- g_settings_set_boolean (settings, SETTINGS_KEY_SHOW_SIZE_COLUMN, priv->show_size_column);
- g_settings_set_enum (settings, SETTINGS_KEY_SORT_COLUMN, priv->sort_column);
- g_settings_set_enum (settings, SETTINGS_KEY_SORT_ORDER, priv->sort_order);
- g_settings_set_int (settings, SETTINGS_KEY_SIDEBAR_WIDTH,
- gtk_paned_get_position (GTK_PANED (priv->browse_widgets_hpaned)));
+ profile_msg (" gtk_tree_view_set_model start", NULL);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ GTK_TREE_MODEL (priv->browse_files_model));
+ gtk_tree_view_columns_autosize (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ MODEL_COL_NAME);
+ file_list_set_sort_column_ids (impl);
+ set_sort_column (impl);
+ profile_msg (" gtk_tree_view_set_model end", NULL);
+ priv->list_sort_ascending = TRUE;
- /* Now apply the settings */
- g_settings_apply (settings);
+ profile_end ("end", NULL);
}
-/* GtkWidget::realize method */
-static void
-gtk_file_chooser_default_realize (GtkWidget *widget)
+/* Timeout callback used when the loading timer expires */
+static gboolean
+load_timeout_cb (gpointer data)
{
- GtkFileChooserDefault *impl;
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- impl = GTK_FILE_CHOOSER_DEFAULT (widget);
+ profile_start ("start", NULL);
- GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->realize (widget);
+ g_assert (priv->load_state == LOAD_PRELOAD);
+ g_assert (priv->load_timeout_id != 0);
+ g_assert (priv->browse_files_model != NULL);
- emit_default_size_changed (impl);
+ priv->load_timeout_id = 0;
+ priv->load_state = LOAD_LOADING;
+
+ load_set_model (impl);
+
+ profile_end ("end", NULL);
+
+ return FALSE;
}
-/* Changes the current folder to $CWD */
+/* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
static void
-switch_to_cwd (GtkFileChooserDefault *impl)
+load_setup_timer (GtkFileChooserDefault *impl)
{
- char *current_working_dir;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- current_working_dir = g_get_current_dir ();
- gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), current_working_dir);
- g_free (current_working_dir);
+ g_assert (priv->load_timeout_id == 0);
+ g_assert (priv->load_state != LOAD_PRELOAD);
+
+ priv->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
+ priv->load_state = LOAD_PRELOAD;
}
-/* Sets the file chooser to showing Recent Files or $CWD, depending on the
- * user's settings.
- */
+/* Removes the load timeout; changes the impl->load_state to the specified value. */
static void
-set_startup_mode (GtkFileChooserDefault *impl)
+load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- switch (priv->startup_mode)
+ if (priv->load_timeout_id != 0)
{
- case STARTUP_MODE_RECENT:
- operation_mode_set (impl, OPERATION_MODE_RECENT);
- break;
-
- case STARTUP_MODE_CWD:
- switch_to_cwd (impl);
- break;
+ g_assert (priv->load_state == LOAD_PRELOAD);
- default:
- g_assert_not_reached ();
+ g_source_remove (priv->load_timeout_id);
+ priv->load_timeout_id = 0;
}
-}
+ else
+ g_assert (priv->load_state == LOAD_EMPTY ||
+ priv->load_state == LOAD_LOADING ||
+ priv->load_state == LOAD_FINISHED);
-static gboolean
-shortcut_exists (GtkFileChooserDefault *impl, GFile *needle)
+ g_assert (new_load_state == LOAD_EMPTY ||
+ new_load_state == LOAD_LOADING ||
+ new_load_state == LOAD_FINISHED);
+ priv->load_state = new_load_state;
+}
+
+/* Selects the first row in the file list */
+static void
+browse_files_select_first_row (GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GSList *haystack;
- GSList *l;
- gboolean exists;
+ GtkTreePath *path;
+ GtkTreeIter dummy_iter;
+ GtkTreeModel *tree_model;
- exists = FALSE;
+ tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view));
- haystack = gtk_places_sidebar_list_shortcuts (GTK_PLACES_SIDEBAR (priv->places_sidebar));
- for (l = haystack; l; l = l->next)
- {
- GFile *hay;
+ if (!tree_model)
+ return;
- hay = G_FILE (l->data);
- if (g_file_equal (hay, needle))
- {
- exists = TRUE;
- break;
- }
- }
- g_slist_free_full (haystack, g_object_unref);
+ path = gtk_tree_path_new_from_indices (0, -1);
- return exists;
+ /* If the list is empty, do nothing. */
+ if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, NULL, FALSE);
+
+ gtk_tree_path_free (path);
}
+struct center_selected_row_closure {
+ GtkFileChooserDefault *impl;
+ gboolean already_centered;
+};
+
+/* Callback used from gtk_tree_selection_selected_foreach(); centers the
+ * selected row in the tree view.
+ */
static void
-add_cwd_to_sidebar_if_needed (GtkFileChooserDefault *impl)
+center_selected_row_foreach_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- char *cwd;
- GFile *cwd_file;
- GFile *home_file;
-
- cwd = g_get_current_dir ();
- cwd_file = g_file_new_for_path (cwd);
- g_free (cwd);
+ struct center_selected_row_closure *closure;
- if (shortcut_exists (impl, cwd_file))
- goto out;
+ closure = data;
+ if (closure->already_centered)
+ return;
- home_file = g_file_new_for_path (g_get_home_dir ());
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->priv->browse_files_tree_view), path, NULL,
TRUE, 0.5, 0.0);
+ closure->already_centered = TRUE;
+}
- /* We only add an item for $CWD if it is different from $HOME. This way,
- * applications which get launched from a shell in a terminal (by someone who
- * knows what they are doing) will get an item for $CWD in the places sidebar,
- * and "normal" applications launched from the desktop shell (whose $CWD is
- * $HOME) won't get any extra clutter in the sidebar.
- */
- if (!g_file_equal (home_file, cwd_file))
- gtk_places_sidebar_add_shortcut (GTK_PLACES_SIDEBAR (priv->places_sidebar), cwd_file);
+/* Centers the selected row in the tree view */
+static void
+browse_files_center_selected_row (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ struct center_selected_row_closure closure;
+ GtkTreeSelection *selection;
- g_object_unref (home_file);
+ closure.impl = impl;
+ closure.already_centered = FALSE;
- out:
- g_object_unref (cwd_file);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
}
-/* GtkWidget::map method */
-static void
-gtk_file_chooser_default_map (GtkWidget *widget)
+static gboolean
+show_and_select_files (GtkFileChooserDefault *impl,
+ GSList *files)
{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget);
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeSelection *selection;
+ GtkFileSystemModel *fsmodel;
+ gboolean enabled_hidden, removed_filters;
+ gboolean selected_a_file;
+ GSList *walk;
- profile_start ("start", NULL);
+ g_assert (priv->load_state == LOAD_FINISHED);
+ g_assert (priv->browse_files_model != NULL);
- GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->map (widget);
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)));
- settings_load (impl);
+ g_assert (fsmodel == priv->browse_files_model);
- add_cwd_to_sidebar_if_needed (impl);
+ enabled_hidden = priv->show_hidden;
+ removed_filters = (priv->current_filter == NULL);
- if (priv->operation_mode == OPERATION_MODE_BROWSE)
+ selected_a_file = FALSE;
+
+ for (walk = files; walk; walk = walk->next)
{
- switch (priv->reload_state)
- {
- case RELOAD_EMPTY:
- set_startup_mode (impl);
- break;
-
- case RELOAD_HAS_FOLDER:
- /* Nothing; we are already loading or loaded, so we
- * don't need to reload
- */
- break;
+ GFile *file = walk->data;
+ GtkTreeIter iter;
- default:
- g_assert_not_reached ();
- }
- }
+ /* Is it a hidden file? */
- profile_end ("end", NULL);
-}
+ if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
+ continue;
-/* GtkWidget::unmap method */
-static void
-gtk_file_chooser_default_unmap (GtkWidget *widget)
-{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (widget);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+ {
+ GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter);
- settings_save (impl);
+ if (!enabled_hidden &&
+ (g_file_info_get_is_hidden (info) ||
+ g_file_info_get_is_backup (info)))
+ {
+ g_object_set (impl, "show-hidden", TRUE, NULL);
+ enabled_hidden = TRUE;
+ }
+ }
- cancel_all_operations (impl);
- priv->reload_state = RELOAD_EMPTY;
+ /* Is it a filtered file? */
- GTK_WIDGET_CLASS (_gtk_file_chooser_default_parent_class)->unmap (widget);
-}
+ if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
+ continue; /* re-get the iter as it may change when the model refilters */
-#define COMPARE_DIRECTORIES
\
- GtkFileChooserDefault *impl = user_data;
\
- GtkFileChooserDefaultPrivate *priv = impl->priv;
\
- GtkFileSystemModel *fs_model = GTK_FILE_SYSTEM_MODEL (model);
\
- gboolean dir_a, dir_b;
\
-
\
- dir_a = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_IS_FOLDER));
\
- dir_b = g_value_get_boolean (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_IS_FOLDER));
\
-
\
- if (dir_a != dir_b)
\
- return priv->list_sort_ascending ? (dir_a ? -1 : 1) : (dir_a ? 1 : -1) /* Directories *always* go first
*/
+ if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+ {
+ /* Maybe we should have a way to ask the fsmodel if it had filtered a file */
+ if (!removed_filters)
+ {
+ set_current_filter (impl, NULL);
+ removed_filters = TRUE;
+ }
+ }
-/* Sort callback for the filename column */
-static gint
-name_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- COMPARE_DIRECTORIES;
- else
- {
- const char *key_a, *key_b;
- gint result;
+ /* Okay, can we select the file now? */
+
+ if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
+ continue;
- key_a = g_value_get_string (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_NAME_COLLATED));
- key_b = g_value_get_string (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_NAME_COLLATED));
+ if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+ {
+ GtkTreePath *path;
- if (key_a && key_b)
- result = strcmp (key_a, key_b);
- else if (key_a)
- result = 1;
- else if (key_b)
- result = -1;
- else
- result = 0;
+ gtk_tree_selection_select_iter (selection, &iter);
- return result;
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter);
+ gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view),
+ path, NULL, FALSE);
+ gtk_tree_path_free (path);
+
+ selected_a_file = TRUE;
+ }
}
+
+ browse_files_center_selected_row (impl);
+
+ return selected_a_file;
}
-/* Sort callback for the size column */
-static gint
-size_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
+/* Processes the pending operation when a folder is finished loading */
+static void
+pending_select_files_process (GtkFileChooserDefault *impl)
{
- COMPARE_DIRECTORIES;
- else
- {
- gint64 size_a, size_b;
-
- size_a = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_SIZE));
- size_b = g_value_get_int64 (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_SIZE));
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
- return size_a < size_b ? -1 : (size_a == size_b ? 0 : 1);
- }
-}
+ g_assert (priv->load_state == LOAD_FINISHED);
+ g_assert (priv->browse_files_model != NULL);
-/* Sort callback for the mtime column */
-static gint
-mtime_sort_func (GtkTreeModel *model,
- GtkTreeIter *a,
- GtkTreeIter *b,
- gpointer user_data)
-{
- COMPARE_DIRECTORIES;
+ if (priv->pending_select_files)
+ {
+ show_and_select_files (impl, priv->pending_select_files);
+ pending_select_files_free (impl);
+ browse_files_center_selected_row (impl);
+ }
else
{
- glong ta, tb;
-
- ta = g_value_get_long (_gtk_file_system_model_get_value (fs_model, a, MODEL_COL_MTIME));
- tb = g_value_get_long (_gtk_file_system_model_get_value (fs_model, b, MODEL_COL_MTIME));
-
- return ta < tb ? -1 : (ta == tb ? 0 : 1);
+ /* We only select the first row if the chooser is actually mapped ---
+ * selecting the first row is to help the user when he is interacting with
+ * the chooser, but sometimes a chooser works not on behalf of the user,
+ * but rather on behalf of something else like GtkFileChooserButton. In
+ * that case, the chooser's selection should be what the caller expects,
+ * as the user can't see that something else got selected. See bug #165264.
+ */
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
+ gtk_widget_get_mapped (GTK_WIDGET (impl)))
+ browse_files_select_first_row (impl);
}
+
+ g_assert (priv->pending_select_files == NULL);
}
-/* Callback used when the sort column changes. We cache the sort order for use
- * in name_sort_func().
- */
static void
-list_sort_column_changed_cb (GtkTreeSortable *sortable,
- GtkFileChooserDefault *impl)
+show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gint sort_column_id;
- GtkSortType sort_type;
+ GFileInfo *info;
+ char *msg;
- if (gtk_tree_sortable_get_sort_column_id (sortable, &sort_column_id, &sort_type))
+ info = g_file_query_info (priv->current_folder,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (info)
{
- priv->list_sort_ascending = (sort_type == GTK_SORT_ASCENDING);
- priv->sort_column = sort_column_id;
- priv->sort_order = sort_type;
+ msg = g_strdup_printf (_("Could not read the contents of %s"), g_file_info_get_display_name (info));
+ g_object_unref (info);
}
-}
-
-static void
-set_busy_cursor (GtkFileChooserDefault *impl,
- gboolean busy)
-{
- GtkWidget *widget;
- GtkWindow *toplevel;
- GdkDisplay *display;
- GdkCursor *cursor;
-
- toplevel = get_toplevel (GTK_WIDGET (impl));
- widget = GTK_WIDGET (toplevel);
- if (!toplevel || !gtk_widget_get_realized (widget))
- return;
-
- display = gtk_widget_get_display (widget);
-
- if (busy)
- cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
else
- cursor = NULL;
-
- gdk_window_set_cursor (gtk_widget_get_window (widget), cursor);
- gdk_display_flush (display);
+ msg = g_strdup (_("Could not read the contents of the folder"));
- if (cursor)
- g_object_unref (cursor);
+ error_message (impl, msg, error->message);
+ g_free (msg);
}
-/* Creates a sort model to wrap the file system model and sets it on the tree view */
+/* Callback used when the file system model finishes loading */
static void
-load_set_model (GtkFileChooserDefault *impl)
+browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
+ GError *error,
+ GtkFileChooserDefault *impl)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
profile_start ("start", NULL);
- g_assert (priv->browse_files_model != NULL);
-
- profile_msg (" gtk_tree_view_set_model start", NULL);
- gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view),
- GTK_TREE_MODEL (priv->browse_files_model));
- gtk_tree_view_columns_autosize (GTK_TREE_VIEW (priv->browse_files_tree_view));
- gtk_tree_view_set_search_column (GTK_TREE_VIEW (priv->browse_files_tree_view),
- MODEL_COL_NAME);
- file_list_set_sort_column_ids (impl);
- set_sort_column (impl);
- profile_msg (" gtk_tree_view_set_model end", NULL);
- priv->list_sort_ascending = TRUE;
-
- profile_end ("end", NULL);
-}
-
-/* Timeout callback used when the loading timer expires */
-static gboolean
-load_timeout_cb (gpointer data)
-{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (data);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (error)
+ show_error_on_reading_current_folder (impl, error);
- profile_start ("start", NULL);
+ if (priv->load_state == LOAD_PRELOAD)
+ {
+ load_remove_timer (impl, LOAD_FINISHED);
+ load_set_model (impl);
+ }
+ else if (priv->load_state == LOAD_LOADING)
+ {
+ /* Nothing */
+ }
+ else
+ {
+ /* We can't g_assert_not_reached(), as something other than us may have
+ * initiated a folder reload. See #165556.
+ */
+ profile_end ("end", NULL);
+ return;
+ }
- g_assert (priv->load_state == LOAD_PRELOAD);
- g_assert (priv->load_timeout_id != 0);
- g_assert (priv->browse_files_model != NULL);
+ g_assert (priv->load_timeout_id == 0);
- priv->load_timeout_id = 0;
- priv->load_state = LOAD_LOADING;
+ priv->load_state = LOAD_FINISHED;
- load_set_model (impl);
+ pending_select_files_process (impl);
+ set_busy_cursor (impl, FALSE);
+#ifdef PROFILE_FILE_CHOOSER
+ access ("MARK: *** FINISHED LOADING", F_OK);
+#endif
profile_end ("end", NULL);
-
- return FALSE;
-}
-
-/* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
-static void
-load_setup_timer (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- g_assert (priv->load_timeout_id == 0);
- g_assert (priv->load_state != LOAD_PRELOAD);
-
- priv->load_timeout_id = gdk_threads_add_timeout (MAX_LOADING_TIME, load_timeout_cb, impl);
- priv->load_state = LOAD_PRELOAD;
}
-/* Removes the load timeout; changes the impl->load_state to the specified value. */
static void
-load_remove_timer (GtkFileChooserDefault *impl, LoadState new_load_state)
+stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
+ gboolean remove_from_treeview)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- if (priv->load_timeout_id != 0)
+ load_remove_timer (impl, LOAD_EMPTY);
+
+ if (priv->browse_files_model)
{
- g_assert (priv->load_state == LOAD_PRELOAD);
-
- g_source_remove (priv->load_timeout_id);
- priv->load_timeout_id = 0;
+ g_object_unref (priv->browse_files_model);
+ priv->browse_files_model = NULL;
}
- else
- g_assert (priv->load_state == LOAD_EMPTY ||
- priv->load_state == LOAD_LOADING ||
- priv->load_state == LOAD_FINISHED);
- g_assert (new_load_state == LOAD_EMPTY ||
- new_load_state == LOAD_LOADING ||
- new_load_state == LOAD_FINISHED);
- priv->load_state = new_load_state;
+ if (remove_from_treeview)
+ gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL);
}
-/* Selects the first row in the file list */
-static void
-browse_files_select_first_row (GtkFileChooserDefault *impl)
+static char *
+my_g_format_time_for_display (glong secs)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreePath *path;
- GtkTreeIter dummy_iter;
- GtkTreeModel *tree_model;
+ GDate mtime, now;
+ gint days_diff;
+ struct tm tm_mtime;
+ time_t time_mtime, time_now;
+ const gchar *format;
+ gchar *locale_format = NULL;
+ gchar buf[256];
+ char *date_str = NULL;
+#ifdef G_OS_WIN32
+ const char *locale, *dot = NULL;
+ gint64 codepage = -1;
+ char charset[20];
+#endif
- tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ time_mtime = secs;
- if (!tree_model)
- return;
+#ifdef HAVE_LOCALTIME_R
+ localtime_r ((time_t *) &time_mtime, &tm_mtime);
+#else
+ {
+ struct tm *ptm = localtime ((time_t *) &time_mtime);
- path = gtk_tree_path_new_from_indices (0, -1);
+ if (!ptm)
+ {
+ g_warning ("ptm != NULL failed");
+
+ return g_strdup (_("Unknown"));
+ }
+ else
+ memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
+ }
+#endif /* HAVE_LOCALTIME_R */
- /* If the list is empty, do nothing. */
- if (gtk_tree_model_get_iter (tree_model, &dummy_iter, path))
- gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view), path, NULL, FALSE);
+ g_date_set_time_t (&mtime, time_mtime);
+ time_now = time (NULL);
+ g_date_set_time_t (&now, time_now);
- gtk_tree_path_free (path);
-}
+ days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
-struct center_selected_row_closure {
- GtkFileChooserDefault *impl;
- gboolean already_centered;
-};
+ /* Translators: %H means "hours" and %M means "minutes" */
+ if (days_diff == 0)
+ format = _("%H:%M");
+ else if (days_diff == 1)
+ format = _("Yesterday at %H:%M");
+ else
+ {
+ if (days_diff > 1 && days_diff < 7)
+ format = "%A"; /* Days from last week */
+ else
+ format = "%x"; /* Any other date */
+ }
-/* Callback used from gtk_tree_selection_selected_foreach(); centers the
- * selected row in the tree view.
- */
-static void
-center_selected_row_foreach_cb (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- struct center_selected_row_closure *closure;
+#ifdef G_OS_WIN32
+ /* g_locale_from_utf8() returns a string in the system
+ * code-page, which is not always the same as that used by the C
+ * library. For instance when running a GTK+ program with
+ * LANG=ko on an English version of Windows, the system
+ * code-page is 1252, but the code-page used by the C library is
+ * 949. (It's GTK+ itself that sets the C library locale when it
+ * notices the LANG environment variable. See gtkmain.c The
+ * Microsoft C library doesn't look at any locale environment
+ * variables.) We need to pass strftime() a string in the C
+ * library's code-page. See bug #509885.
+ */
+ locale = setlocale (LC_ALL, NULL);
+ if (locale != NULL)
+ dot = strchr (locale, '.');
+ if (dot != NULL)
+ {
+ codepage = g_ascii_strtoll (dot+1, NULL, 10);
+
+ /* All codepages should fit in 16 bits AFAIK */
+ if (codepage > 0 && codepage < 65536)
+ {
+ sprintf (charset, "CP%u", (guint) codepage);
+ locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
+ }
+ }
+#else
+ locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
+#endif
+ if (locale_format != NULL &&
+ strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
+ {
+#ifdef G_OS_WIN32
+ /* As above but in opposite direction... */
+ if (codepage > 0 && codepage < 65536)
+ date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
+#else
+ date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
+#endif
+ }
- closure = data;
- if (closure->already_centered)
- return;
+ if (date_str == NULL)
+ date_str = g_strdup (_("Unknown"));
- gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (closure->impl->priv->browse_files_tree_view), path, NULL,
TRUE, 0.5, 0.0);
- closure->already_centered = TRUE;
+ g_free (locale_format);
+ return date_str;
}
-/* Centers the selected row in the tree view */
static void
-browse_files_center_selected_row (GtkFileChooserDefault *impl)
+copy_attribute (GFileInfo *to, GFileInfo *from, const char *attribute)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- struct center_selected_row_closure closure;
- GtkTreeSelection *selection;
-
- closure.impl = impl;
- closure.already_centered = FALSE;
+ GFileAttributeType type;
+ gpointer value;
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- gtk_tree_selection_selected_foreach (selection, center_selected_row_foreach_cb, &closure);
+ if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
+ g_file_info_set_attribute (to, attribute, type, value);
}
-static gboolean
-show_and_select_files (GtkFileChooserDefault *impl,
- GSList *files)
+static void
+file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer data)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeSelection *selection;
- GtkFileSystemModel *fsmodel;
- gboolean enabled_hidden, removed_filters;
- gboolean selected_a_file;
- GSList *walk;
-
- g_assert (priv->load_state == LOAD_FINISHED);
- g_assert (priv->browse_files_model != NULL);
-
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- fsmodel = GTK_FILE_SYSTEM_MODEL (gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view)));
+ GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */
+ GFile *file = G_FILE (object);
+ GFileInfo *queried, *info;
+ GtkTreeIter iter;
- g_assert (fsmodel == priv->browse_files_model);
+ queried = g_file_query_info_finish (file, res, NULL);
+ if (queried == NULL)
+ return;
- enabled_hidden = priv->show_hidden;
- removed_filters = (priv->current_filter == NULL);
+ gdk_threads_enter ();
- selected_a_file = FALSE;
+ /* now we know model is valid */
- for (walk = files; walk; walk = walk->next)
+ /* file was deleted */
+ if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
{
- GFile *file = walk->data;
- GtkTreeIter iter;
-
- /* Is it a hidden file? */
+ gdk_threads_leave ();
+ return;
+ }
- if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
- continue;
+ info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter));
- if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
- {
- GFileInfo *info = _gtk_file_system_model_get_info (fsmodel, &iter);
+ copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
+ copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
+ copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
- if (!enabled_hidden &&
- (g_file_info_get_is_hidden (info) ||
- g_file_info_get_is_backup (info)))
- {
- g_object_set (impl, "show-hidden", TRUE, NULL);
- enabled_hidden = TRUE;
- }
- }
+ _gtk_file_system_model_update_file (model, file, info);
- /* Is it a filtered file? */
+ g_object_unref (info);
- if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
- continue; /* re-get the iter as it may change when the model refilters */
+ gdk_threads_leave ();
+}
- if (!_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+static gboolean
+file_system_model_set (GtkFileSystemModel *model,
+ GFile *file,
+ GFileInfo *info,
+ int column,
+ GValue *value,
+ gpointer data)
+{
+ GtkFileChooserDefault *impl = data;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ switch (column)
+ {
+ case MODEL_COL_FILE:
+ g_value_set_object (value, file);
+ break;
+ case MODEL_COL_NAME:
+ if (info == NULL)
+ g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME);
+ else
+ g_value_set_string (value, g_file_info_get_display_name (info));
+ break;
+ case MODEL_COL_NAME_COLLATED:
+ if (info == NULL)
+ g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1));
+ else
+ g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info),
-1));
+ break;
+ case MODEL_COL_IS_FOLDER:
+ g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info));
+ break;
+ case MODEL_COL_IS_SENSITIVE:
+ if (info)
{
- /* Maybe we should have a way to ask the fsmodel if it had filtered a file */
- if (!removed_filters)
- {
- set_current_filter (impl, NULL);
- removed_filters = TRUE;
- }
- }
+ gboolean sensitive = TRUE;
- /* Okay, can we select the file now? */
-
- if (!_gtk_file_system_model_get_iter_for_file (fsmodel, &iter, file))
- continue;
+ if (!(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
+ || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
+ {
+ sensitive = TRUE; /* for file modes... */
+ }
+ else if (!_gtk_file_info_consider_as_directory (info))
+ {
+ sensitive = FALSE; /* for folder modes, files are not sensitive... */
+ }
+ else
+ {
+ /* ... and for folder modes, folders are sensitive only if the filter says so */
+ GtkTreeIter iter;
+ if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
+ g_assert_not_reached ();
+ sensitive = !_gtk_file_system_model_iter_is_filtered_out (model, &iter);
+ }
- if (_gtk_file_system_model_iter_is_visible (fsmodel, &iter))
+ g_value_set_boolean (value, sensitive);
+ }
+ else
+ g_value_set_boolean (value, TRUE);
+ break;
+ case MODEL_COL_PIXBUF:
+ if (info)
{
- GtkTreePath *path;
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON))
+ {
+ g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl),
priv->icon_size));
+ }
+ else
+ {
+ GtkTreeModel *tree_model;
+ GtkTreePath *path, *start, *end;
+ GtkTreeIter iter;
- gtk_tree_selection_select_iter (selection, &iter);
+ if (priv->browse_files_tree_view == NULL ||
+ g_file_info_has_attribute (info, "filechooser::queried"))
+ return FALSE;
- path = gtk_tree_model_get_path (GTK_TREE_MODEL (fsmodel), &iter);
- gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->browse_files_tree_view),
- path, NULL, FALSE);
- gtk_tree_path_free (path);
+ tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ if (tree_model != GTK_TREE_MODEL (model))
+ return FALSE;
- selected_a_file = TRUE;
+ if (!_gtk_file_system_model_get_iter_for_file (model,
+ &iter,
+ file))
+ g_assert_not_reached ();
+ if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (priv->browse_files_tree_view), &start,
&end))
+ return FALSE;
+ path = gtk_tree_model_get_path (tree_model, &iter);
+ if (gtk_tree_path_compare (start, path) != 1 &&
+ gtk_tree_path_compare (path, end) != 1)
+ {
+ g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
+ g_file_query_info_async (file,
+ G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
+ G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
+ G_FILE_ATTRIBUTE_STANDARD_ICON,
+ G_FILE_QUERY_INFO_NONE,
+ G_PRIORITY_DEFAULT,
+ _gtk_file_system_model_get_cancellable (model),
+ file_system_model_got_thumbnail,
+ model);
+ }
+ gtk_tree_path_free (path);
+ gtk_tree_path_free (start);
+ gtk_tree_path_free (end);
+ return FALSE;
+ }
}
+ else
+ g_value_set_object (value, NULL);
+ break;
+ case MODEL_COL_SIZE:
+ g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0);
+ break;
+ case MODEL_COL_SIZE_TEXT:
+ if (info == NULL || _gtk_file_info_consider_as_directory (info))
+ g_value_set_string (value, NULL);
+ else
+ g_value_take_string (value, g_format_size (g_file_info_get_size (info)));
+ break;
+ case MODEL_COL_MTIME:
+ case MODEL_COL_MTIME_TEXT:
+ {
+ GTimeVal tv;
+ if (info == NULL)
+ break;
+ g_file_info_get_modification_time (info, &tv);
+ if (column == MODEL_COL_MTIME)
+ g_value_set_long (value, tv.tv_sec);
+ else if (tv.tv_sec == 0)
+ g_value_set_static_string (value, _("Unknown"));
+ else
+ g_value_take_string (value, my_g_format_time_for_display (tv.tv_sec));
+ break;
+ }
+ case MODEL_COL_ELLIPSIZE:
+ g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
}
- browse_files_center_selected_row (impl);
-
- return selected_a_file;
-}
-
-/* Processes the pending operation when a folder is finished loading */
-static void
-pending_select_files_process (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- g_assert (priv->load_state == LOAD_FINISHED);
- g_assert (priv->browse_files_model != NULL);
-
- if (priv->pending_select_files)
- {
- show_and_select_files (impl, priv->pending_select_files);
- pending_select_files_free (impl);
- browse_files_center_selected_row (impl);
- }
- else
- {
- /* We only select the first row if the chooser is actually mapped ---
- * selecting the first row is to help the user when he is interacting with
- * the chooser, but sometimes a chooser works not on behalf of the user,
- * but rather on behalf of something else like GtkFileChooserButton. In
- * that case, the chooser's selection should be what the caller expects,
- * as the user can't see that something else got selected. See bug #165264.
- */
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN &&
- gtk_widget_get_mapped (GTK_WIDGET (impl)))
- browse_files_select_first_row (impl);
- }
-
- g_assert (priv->pending_select_files == NULL);
+ return TRUE;
}
-static void
-show_error_on_reading_current_folder (GtkFileChooserDefault *impl, GError *error)
+/* Gets rid of the old list model and creates a new one for the current folder */
+static gboolean
+set_list_model (GtkFileChooserDefault *impl,
+ GError **error)
{
GtkFileChooserDefaultPrivate *priv = impl->priv;
- GFileInfo *info;
- char *msg;
- info = g_file_query_info (priv->current_folder,
- G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
- G_FILE_QUERY_INFO_NONE,
- NULL,
- NULL);
- if (info)
- {
- msg = g_strdup_printf (_("Could not read the contents of %s"), g_file_info_get_display_name (info));
- g_object_unref (info);
- }
- else
- msg = g_strdup (_("Could not read the contents of the folder"));
+ g_assert (priv->current_folder != NULL);
- error_message (impl, msg, error->message);
- g_free (msg);
-}
+ profile_start ("start", NULL);
-/* Callback used when the file system model finishes loading */
-static void
-browse_files_model_finished_loading_cb (GtkFileSystemModel *model,
- GError *error,
- GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ stop_loading_and_clear_list_model (impl, TRUE);
- profile_start ("start", NULL);
+ set_busy_cursor (impl, TRUE);
- if (error)
- show_error_on_reading_current_folder (impl, error);
+ priv->browse_files_model =
+ _gtk_file_system_model_new_for_directory (priv->current_folder,
+ MODEL_ATTRIBUTES,
+ file_system_model_set,
+ impl,
+ MODEL_COLUMN_TYPES);
- if (priv->load_state == LOAD_PRELOAD)
- {
- load_remove_timer (impl, LOAD_FINISHED);
- load_set_model (impl);
- }
- else if (priv->load_state == LOAD_LOADING)
- {
- /* Nothing */
- }
- else
- {
- /* We can't g_assert_not_reached(), as something other than us may have
- * initiated a folder reload. See #165556.
- */
- profile_end ("end", NULL);
- return;
- }
+ _gtk_file_system_model_set_show_hidden (priv->browse_files_model, priv->show_hidden);
- g_assert (priv->load_timeout_id == 0);
+ profile_msg (" set sort function", NULL);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_NAME,
name_sort_func, impl, NULL);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_SIZE,
size_sort_func, impl, NULL);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_MTIME,
mtime_sort_func, impl, NULL);
+ gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), NULL, NULL, NULL);
+ set_sort_column (impl);
+ priv->list_sort_ascending = TRUE;
+ g_signal_connect (priv->browse_files_model, "sort-column-changed",
+ G_CALLBACK (list_sort_column_changed_cb), impl);
- priv->load_state = LOAD_FINISHED;
+ load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
- pending_select_files_process (impl);
- set_busy_cursor (impl, FALSE);
-#ifdef PROFILE_FILE_CHOOSER
- access ("MARK: *** FINISHED LOADING", F_OK);
-#endif
+ g_signal_connect (priv->browse_files_model, "finished-loading",
+ G_CALLBACK (browse_files_model_finished_loading_cb), impl);
+
+ _gtk_file_system_model_set_filter (priv->browse_files_model, priv->current_filter);
profile_end ("end", NULL);
+
+ return TRUE;
}
-static void
-stop_loading_and_clear_list_model (GtkFileChooserDefault *impl,
- gboolean remove_from_treeview)
+struct update_chooser_entry_selected_foreach_closure {
+ int num_selected;
+ GtkTreeIter first_selected_iter;
+};
+
+static gint
+compare_utf8_filenames (const gchar *a,
+ const gchar *b)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ gchar *a_folded, *b_folded;
+ gint retval;
- load_remove_timer (impl, LOAD_EMPTY);
-
- if (priv->browse_files_model)
- {
- g_object_unref (priv->browse_files_model);
- priv->browse_files_model = NULL;
- }
+ a_folded = g_utf8_strdown (a, -1);
+ b_folded = g_utf8_strdown (b, -1);
- if (remove_from_treeview)
- gtk_tree_view_set_model (GTK_TREE_VIEW (priv->browse_files_tree_view), NULL);
+ retval = strcmp (a_folded, b_folded);
+
+ g_free (a_folded);
+ g_free (b_folded);
+
+ return retval;
}
-static char *
-my_g_format_time_for_display (glong secs)
+static void
+update_chooser_entry_selected_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
{
- GDate mtime, now;
- gint days_diff;
- struct tm tm_mtime;
- time_t time_mtime, time_now;
- const gchar *format;
- gchar *locale_format = NULL;
- gchar buf[256];
- char *date_str = NULL;
-#ifdef G_OS_WIN32
- const char *locale, *dot = NULL;
- gint64 codepage = -1;
- char charset[20];
-#endif
+ struct update_chooser_entry_selected_foreach_closure *closure;
- time_mtime = secs;
+ closure = data;
+ closure->num_selected++;
-#ifdef HAVE_LOCALTIME_R
- localtime_r ((time_t *) &time_mtime, &tm_mtime);
-#else
- {
- struct tm *ptm = localtime ((time_t *) &time_mtime);
+ if (closure->num_selected == 1)
+ closure->first_selected_iter = *iter;
+}
- if (!ptm)
- {
- g_warning ("ptm != NULL failed");
-
- return g_strdup (_("Unknown"));
- }
- else
- memcpy ((void *) &tm_mtime, (void *) ptm, sizeof (struct tm));
- }
-#endif /* HAVE_LOCALTIME_R */
+static void
+update_chooser_entry (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeSelection *selection;
+ struct update_chooser_entry_selected_foreach_closure closure;
- g_date_set_time_t (&mtime, time_mtime);
- time_now = time (NULL);
- g_date_set_time_t (&now, time_now);
+ /* no need to update the file chooser's entry if there's no entry */
+ if (priv->operation_mode == OPERATION_MODE_SEARCH ||
+ !priv->location_entry)
+ return;
- days_diff = g_date_get_julian (&now) - g_date_get_julian (&mtime);
+ if (!(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
+ || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
+ || ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN
+ || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ && priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
+ return;
- /* Translators: %H means "hours" and %M means "minutes" */
- if (days_diff == 0)
- format = _("%H:%M");
- else if (days_diff == 1)
- format = _("Yesterday at %H:%M");
- else
- {
- if (days_diff > 1 && days_diff < 7)
- format = "%A"; /* Days from last week */
- else
- format = "%x"; /* Any other date */
- }
+ g_assert (priv->location_entry != NULL);
-#ifdef G_OS_WIN32
- /* g_locale_from_utf8() returns a string in the system
- * code-page, which is not always the same as that used by the C
- * library. For instance when running a GTK+ program with
- * LANG=ko on an English version of Windows, the system
- * code-page is 1252, but the code-page used by the C library is
- * 949. (It's GTK+ itself that sets the C library locale when it
- * notices the LANG environment variable. See gtkmain.c The
- * Microsoft C library doesn't look at any locale environment
- * variables.) We need to pass strftime() a string in the C
- * library's code-page. See bug #509885.
- */
- locale = setlocale (LC_ALL, NULL);
- if (locale != NULL)
- dot = strchr (locale, '.');
- if (dot != NULL)
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
+ closure.num_selected = 0;
+ gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
+
+ if (closure.num_selected == 0)
{
- codepage = g_ascii_strtoll (dot+1, NULL, 10);
-
- /* All codepages should fit in 16 bits AFAIK */
- if (codepage > 0 && codepage < 65536)
+ if (priv->operation_mode == OPERATION_MODE_RECENT)
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), NULL);
+ else
+ goto maybe_clear_entry;
+ }
+ else if (closure.num_selected == 1)
+ {
+ if (priv->operation_mode == OPERATION_MODE_BROWSE)
{
- sprintf (charset, "CP%u", (guint) codepage);
- locale_format = g_convert (format, -1, charset, "UTF-8", NULL, NULL, NULL);
+ GFileInfo *info;
+ gboolean change_entry;
+
+ info = _gtk_file_system_model_get_info (priv->browse_files_model, &closure.first_selected_iter);
+
+ /* If the cursor moved to the row of the newly created folder,
+ * retrieving info will return NULL.
+ */
+ if (!info)
+ return;
+
+ g_free (priv->browse_files_last_selected_name);
+ priv->browse_files_last_selected_name =
+ g_strdup (g_file_info_get_display_name (info));
+
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ {
+ /* Don't change the name when clicking on a folder... */
+ change_entry = (! _gtk_file_info_consider_as_directory (info));
+ }
+ else
+ change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
+
+ if (change_entry)
+ {
+ gtk_entry_set_text (GTK_ENTRY (priv->location_entry), priv->browse_files_last_selected_name);
+
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (priv->location_entry));
+ }
+
+ return;
}
+ else if (priv->operation_mode == OPERATION_MODE_RECENT
+ && priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+ {
+ GFile *folder;
+
+ /* Set the base folder on the name entry, so it will do completion relative to the correct
recent-folder */
+
+ gtk_tree_model_get (GTK_TREE_MODEL (priv->recent_model), &closure.first_selected_iter,
+ MODEL_COL_FILE, &folder,
+ -1);
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), folder);
+ g_object_unref (folder);
+ return;
+ }
}
-#else
- locale_format = g_locale_from_utf8 (format, -1, NULL, NULL, NULL);
-#endif
- if (locale_format != NULL &&
- strftime (buf, sizeof (buf), locale_format, &tm_mtime) != 0)
+ else
{
-#ifdef G_OS_WIN32
- /* As above but in opposite direction... */
- if (codepage > 0 && codepage < 65536)
- date_str = g_convert (buf, -1, "UTF-8", charset, NULL, NULL, NULL);
-#else
- date_str = g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
-#endif
+ g_assert (!(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
+
+ /* Multiple selection, so just clear the entry. */
+ g_free (priv->browse_files_last_selected_name);
+ priv->browse_files_last_selected_name = NULL;
+
+ gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
+ return;
}
- if (date_str == NULL)
- date_str = g_strdup (_("Unknown"));
+ maybe_clear_entry:
- g_free (locale_format);
- return date_str;
+ if ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ && priv->browse_files_last_selected_name)
+ {
+ const char *entry_text;
+ int len;
+ gboolean clear_entry;
+
+ entry_text = gtk_entry_get_text (GTK_ENTRY (priv->location_entry));
+ len = strlen (entry_text);
+ if (len != 0)
+ {
+ /* The file chooser entry may have appended a "/" to its text.
+ * So take it out, and compare the result to the old selection.
+ */
+ if (entry_text[len - 1] == G_DIR_SEPARATOR)
+ {
+ gchar *tmp;
+
+ tmp = g_strndup (entry_text, len - 1);
+ clear_entry = (compare_utf8_filenames (priv->browse_files_last_selected_name, tmp) == 0);
+ g_free (tmp);
+ }
+ else
+ clear_entry = (compare_utf8_filenames (priv->browse_files_last_selected_name, entry_text) == 0);
+ }
+ else
+ clear_entry = FALSE;
+
+ if (clear_entry)
+ gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
+ }
}
-static void
-copy_attribute (GFileInfo *to, GFileInfo *from, const char *attribute)
+static gboolean
+gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
+ GFile *file,
+ GError **error)
{
- GFileAttributeType type;
- gpointer value;
-
- if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
- g_file_info_set_attribute (to, attribute, type, value);
+ return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
}
+
+struct UpdateCurrentFolderData
+{
+ GtkFileChooserDefault *impl;
+ GFile *file;
+ gboolean keep_trail;
+ gboolean clear_entry;
+ GFile *original_file;
+ GError *original_error;
+};
+
static void
-file_system_model_got_thumbnail (GObject *object, GAsyncResult *res, gpointer data)
+update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellable,
+ GtkFileSystemVolume *volume,
+ const GError *error,
+ gpointer user_data)
{
- GtkFileSystemModel *model = data; /* might be unreffed if operation was cancelled */
- GFile *file = G_FILE (object);
- GFileInfo *queried, *info;
- GtkTreeIter iter;
+ struct UpdateCurrentFolderData *data = user_data;
+ GtkFileChooserDefault *impl = data->impl;
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ gboolean cancelled = g_cancellable_is_cancelled (cancellable);
- queried = g_file_query_info_finish (file, res, NULL);
- if (queried == NULL)
- return;
+ if (cancellable != priv->update_current_folder_cancellable)
+ goto out;
- gdk_threads_enter ();
+ priv->update_current_folder_cancellable = NULL;
+ set_busy_cursor (impl, FALSE);
- /* now we know model is valid */
+ if (cancelled)
+ goto out;
- /* file was deleted */
- if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
+ if (error)
{
- gdk_threads_leave ();
- return;
+ error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
+ priv->reload_state = RELOAD_EMPTY;
+ goto out;
}
- info = g_file_info_dup (_gtk_file_system_model_get_info (model, &iter));
-
- copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
- copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
- copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
-
- _gtk_file_system_model_update_file (model, file, info);
+ change_folder_and_display_error (impl, data->file, data->clear_entry);
- g_object_unref (info);
+out:
+ g_object_unref (data->file);
+ g_free (data);
- gdk_threads_leave ();
+ g_object_unref (cancellable);
}
-static gboolean
-file_system_model_set (GtkFileSystemModel *model,
- GFile *file,
- GFileInfo *info,
- int column,
- GValue *value,
- gpointer data)
+static void
+update_current_folder_get_info_cb (GCancellable *cancellable,
+ GFileInfo *info,
+ const GError *error,
+ gpointer user_data)
{
- GtkFileChooserDefault *impl = data;
+ gboolean cancelled = g_cancellable_is_cancelled (cancellable);
+ struct UpdateCurrentFolderData *data = user_data;
+ GtkFileChooserDefault *impl = data->impl;
GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- switch (column)
- {
- case MODEL_COL_FILE:
- g_value_set_object (value, file);
- break;
- case MODEL_COL_NAME:
- if (info == NULL)
- g_value_set_string (value, DEFAULT_NEW_FOLDER_NAME);
- else
- g_value_set_string (value, g_file_info_get_display_name (info));
- break;
- case MODEL_COL_NAME_COLLATED:
- if (info == NULL)
- g_value_take_string (value, g_utf8_collate_key_for_filename (DEFAULT_NEW_FOLDER_NAME, -1));
- else
- g_value_take_string (value, g_utf8_collate_key_for_filename (g_file_info_get_display_name (info),
-1));
- break;
- case MODEL_COL_IS_FOLDER:
- g_value_set_boolean (value, info == NULL || _gtk_file_info_consider_as_directory (info));
- break;
- case MODEL_COL_IS_SENSITIVE:
- if (info)
- {
- gboolean sensitive = TRUE;
- if (!(priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER
- || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
- {
- sensitive = TRUE; /* for file modes... */
- }
- else if (!_gtk_file_info_consider_as_directory (info))
- {
- sensitive = FALSE; /* for folder modes, files are not sensitive... */
- }
- else
- {
- /* ... and for folder modes, folders are sensitive only if the filter says so */
- GtkTreeIter iter;
- if (!_gtk_file_system_model_get_iter_for_file (model, &iter, file))
- g_assert_not_reached ();
- sensitive = !_gtk_file_system_model_iter_is_filtered_out (model, &iter);
- }
+ if (cancellable != priv->update_current_folder_cancellable)
+ goto out;
- g_value_set_boolean (value, sensitive);
- }
- else
- g_value_set_boolean (value, TRUE);
- break;
- case MODEL_COL_PIXBUF:
- if (info)
- {
- if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_ICON))
- {
- g_value_take_object (value, _gtk_file_info_render_icon (info, GTK_WIDGET (impl),
priv->icon_size));
- }
- else
- {
- GtkTreeModel *tree_model;
- GtkTreePath *path, *start, *end;
- GtkTreeIter iter;
+ priv->update_current_folder_cancellable = NULL;
+ priv->reload_state = RELOAD_EMPTY;
- if (priv->browse_files_tree_view == NULL ||
- g_file_info_has_attribute (info, "filechooser::queried"))
- return FALSE;
+ set_busy_cursor (impl, FALSE);
- tree_model = gtk_tree_view_get_model (GTK_TREE_VIEW (priv->browse_files_tree_view));
- if (tree_model != GTK_TREE_MODEL (model))
- return FALSE;
+ if (cancelled)
+ goto out;
- if (!_gtk_file_system_model_get_iter_for_file (model,
- &iter,
- file))
- g_assert_not_reached ();
- if (!gtk_tree_view_get_visible_range (GTK_TREE_VIEW (priv->browse_files_tree_view), &start,
&end))
- return FALSE;
- path = gtk_tree_model_get_path (tree_model, &iter);
- if (gtk_tree_path_compare (start, path) != 1 &&
- gtk_tree_path_compare (path, end) != 1)
- {
- g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
- g_file_query_info_async (file,
- G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
- G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
- G_FILE_ATTRIBUTE_STANDARD_ICON,
- G_FILE_QUERY_INFO_NONE,
- G_PRIORITY_DEFAULT,
- _gtk_file_system_model_get_cancellable (model),
- file_system_model_got_thumbnail,
- model);
- }
- gtk_tree_path_free (path);
- gtk_tree_path_free (start);
- gtk_tree_path_free (end);
- return FALSE;
- }
- }
- else
- g_value_set_object (value, NULL);
- break;
- case MODEL_COL_SIZE:
- g_value_set_int64 (value, info ? g_file_info_get_size (info) : 0);
- break;
- case MODEL_COL_SIZE_TEXT:
- if (info == NULL || _gtk_file_info_consider_as_directory (info))
- g_value_set_string (value, NULL);
- else
- g_value_take_string (value, g_format_size (g_file_info_get_size (info)));
- break;
- case MODEL_COL_MTIME:
- case MODEL_COL_MTIME_TEXT:
- {
- GTimeVal tv;
- if (info == NULL)
- break;
- g_file_info_get_modification_time (info, &tv);
- if (column == MODEL_COL_MTIME)
- g_value_set_long (value, tv.tv_sec);
- else if (tv.tv_sec == 0)
- g_value_set_static_string (value, _("Unknown"));
- else
- g_value_take_string (value, my_g_format_time_for_display (tv.tv_sec));
- break;
- }
- case MODEL_COL_ELLIPSIZE:
- g_value_set_enum (value, info ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE);
- break;
- default:
- g_assert_not_reached ();
- break;
- }
+ if (error)
+ {
+ GFile *parent_file;
- return TRUE;
-}
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
+ {
+ GMountOperation *mount_operation;
+ GtkWidget *toplevel;
-/* Gets rid of the old list model and creates a new one for the current folder */
-static gboolean
-set_list_model (GtkFileChooserDefault *impl,
- GError **error)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ g_object_unref (cancellable);
+ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
- g_assert (priv->current_folder != NULL);
+ mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
- profile_start ("start", NULL);
+ set_busy_cursor (impl, TRUE);
- stop_loading_and_clear_list_model (impl, TRUE);
+ priv->update_current_folder_cancellable =
+ _gtk_file_system_mount_enclosing_volume (priv->file_system, data->file,
+ mount_operation,
+ update_current_folder_mount_enclosing_volume_cb,
+ data);
- set_busy_cursor (impl, TRUE);
+ return;
+ }
- priv->browse_files_model =
- _gtk_file_system_model_new_for_directory (priv->current_folder,
- MODEL_ATTRIBUTES,
- file_system_model_set,
- impl,
- MODEL_COLUMN_TYPES);
+ if (!data->original_file)
+ {
+ data->original_file = g_object_ref (data->file);
+ data->original_error = g_error_copy (error);
+ }
- _gtk_file_system_model_set_show_hidden (priv->browse_files_model, priv->show_hidden);
+ parent_file = g_file_get_parent (data->file);
- profile_msg (" set sort function", NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_NAME,
name_sort_func, impl, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_SIZE,
size_sort_func, impl, NULL);
- gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), MODEL_COL_MTIME,
mtime_sort_func, impl, NULL);
- gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (priv->browse_files_model), NULL, NULL, NULL);
- set_sort_column (impl);
- priv->list_sort_ascending = TRUE;
- g_signal_connect (priv->browse_files_model, "sort-column-changed",
- G_CALLBACK (list_sort_column_changed_cb), impl);
+ /* get parent path and try to change the folder to that */
+ if (parent_file)
+ {
+ g_object_unref (data->file);
+ data->file = parent_file;
- load_setup_timer (impl); /* This changes the state to LOAD_PRELOAD */
+ g_object_unref (cancellable);
- g_signal_connect (priv->browse_files_model, "finished-loading",
- G_CALLBACK (browse_files_model_finished_loading_cb), impl);
+ /* restart the update current folder operation */
+ priv->reload_state = RELOAD_HAS_FOLDER;
- _gtk_file_system_model_set_filter (priv->browse_files_model, priv->current_filter);
+ priv->update_current_folder_cancellable =
+ _gtk_file_system_get_info (priv->file_system, data->file,
+ "standard::type",
+ update_current_folder_get_info_cb,
+ data);
- profile_end ("end", NULL);
+ set_busy_cursor (impl, TRUE);
- return TRUE;
-}
+ return;
+ }
+ else
+ {
+ /* Error and bail out, ignoring "not found" errors since they're useless:
+ * they only happen when a program defaults to a folder that has been (re)moved.
+ */
+ if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ error_changing_folder_dialog (impl, data->original_file, data->original_error);
+ else
+ g_error_free (data->original_error);
-struct update_chooser_entry_selected_foreach_closure {
- int num_selected;
- GtkTreeIter first_selected_iter;
-};
+ g_object_unref (data->original_file);
-static gint
-compare_utf8_filenames (const gchar *a,
- const gchar *b)
-{
- gchar *a_folded, *b_folded;
- gint retval;
+ goto out;
+ }
+ }
- a_folded = g_utf8_strdown (a, -1);
- b_folded = g_utf8_strdown (b, -1);
+ if (data->original_file)
+ {
+ /* Error and bail out, ignoring "not found" errors since they're useless:
+ * they only happen when a program defaults to a folder that has been (re)moved.
+ */
+ if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
+ error_changing_folder_dialog (impl, data->original_file, data->original_error);
+ else
+ g_error_free (data->original_error);
- retval = strcmp (a_folded, b_folded);
+ g_object_unref (data->original_file);
+ }
- g_free (a_folded);
- g_free (b_folded);
+ if (! _gtk_file_info_consider_as_directory (info))
+ goto out;
- return retval;
-}
+ _gtk_path_bar_set_file (GTK_PATH_BAR (priv->browse_path_bar), data->file, data->keep_trail);
-static void
-update_chooser_entry_selected_foreach (GtkTreeModel *model,
- GtkTreePath *path,
- GtkTreeIter *iter,
- gpointer data)
-{
- struct update_chooser_entry_selected_foreach_closure *closure;
+ if (priv->current_folder != data->file)
+ {
+ if (priv->current_folder)
+ g_object_unref (priv->current_folder);
- closure = data;
- closure->num_selected++;
+ priv->current_folder = g_object_ref (data->file);
+ }
- if (closure->num_selected == 1)
- closure->first_selected_iter = *iter;
-}
+ priv->reload_state = RELOAD_HAS_FOLDER;
-static void
-update_chooser_entry (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GtkTreeSelection *selection;
- struct update_chooser_entry_selected_foreach_closure closure;
+ /* Set the folder on the save entry */
- /* no need to update the file chooser's entry if there's no entry */
- if (priv->operation_mode == OPERATION_MODE_SEARCH ||
- !priv->location_entry)
- return;
+ if (priv->location_entry)
+ {
+ _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry),
+ priv->current_folder);
- if (!(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE
- || priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
- || ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN
- || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- && priv->location_mode == LOCATION_MODE_FILENAME_ENTRY)))
- return;
+ if (data->clear_entry)
+ gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
+ }
- g_assert (priv->location_entry != NULL);
+ /* Create a new list model. This is slightly evil; we store the result value
+ * but perform more actions rather than returning immediately even if it
+ * generates an error.
+ */
+ set_list_model (impl, NULL);
- selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->browse_files_tree_view));
- closure.num_selected = 0;
- gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
+ /* Refresh controls */
- if (closure.num_selected == 0)
- {
- if (priv->operation_mode == OPERATION_MODE_RECENT)
- _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), NULL);
- else
- goto maybe_clear_entry;
- }
- else if (closure.num_selected == 1)
- {
- if (priv->operation_mode == OPERATION_MODE_BROWSE)
- {
- GFileInfo *info;
- gboolean change_entry;
+ gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (priv->places_sidebar), priv->current_folder);
- info = _gtk_file_system_model_get_info (priv->browse_files_model, &closure.first_selected_iter);
+ g_signal_emit_by_name (impl, "current-folder-changed", 0);
- /* If the cursor moved to the row of the newly created folder,
- * retrieving info will return NULL.
- */
- if (!info)
- return;
+ check_preview_change (impl);
- g_free (priv->browse_files_last_selected_name);
- priv->browse_files_last_selected_name =
- g_strdup (g_file_info_get_display_name (info));
+ g_signal_emit_by_name (impl, "selection-changed", 0);
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- {
- /* Don't change the name when clicking on a folder... */
- change_entry = (! _gtk_file_info_consider_as_directory (info));
- }
- else
- change_entry = TRUE; /* ... unless we are in SELECT_FOLDER mode */
+out:
+ g_object_unref (data->file);
+ g_free (data);
- if (change_entry)
- {
- gtk_entry_set_text (GTK_ENTRY (priv->location_entry), priv->browse_files_last_selected_name);
+ g_object_unref (cancellable);
+}
- if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
- _gtk_file_chooser_entry_select_filename (GTK_FILE_CHOOSER_ENTRY (priv->location_entry));
- }
+static gboolean
+gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
+ GFile *file,
+ gboolean keep_trail,
+ gboolean clear_entry,
+ GError **error)
+{
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ struct UpdateCurrentFolderData *data;
- return;
- }
- else if (priv->operation_mode == OPERATION_MODE_RECENT
- && priv->action == GTK_FILE_CHOOSER_ACTION_SAVE)
- {
- GFile *folder;
+ profile_start ("start", NULL);
- /* Set the base folder on the name entry, so it will do completion relative to the correct
recent-folder */
+ g_object_ref (file);
- gtk_tree_model_get (GTK_TREE_MODEL (priv->recent_model), &closure.first_selected_iter,
- MODEL_COL_FILE, &folder,
- -1);
- _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry), folder);
- g_object_unref (folder);
- return;
- }
- }
- else
- {
- g_assert (!(priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
+ operation_mode_set (impl, OPERATION_MODE_BROWSE);
- /* Multiple selection, so just clear the entry. */
- g_free (priv->browse_files_last_selected_name);
- priv->browse_files_last_selected_name = NULL;
+ if (priv->local_only && !_gtk_file_has_native_path (file))
+ {
+ g_set_error_literal (error,
+ GTK_FILE_CHOOSER_ERROR,
+ GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
+ _("Cannot change to folder because it is not local"));
- gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
- return;
+ g_object_unref (file);
+ profile_end ("end - not local", NULL);
+ return FALSE;
}
- maybe_clear_entry:
+ if (priv->update_current_folder_cancellable)
+ g_cancellable_cancel (priv->update_current_folder_cancellable);
- if ((priv->action == GTK_FILE_CHOOSER_ACTION_OPEN || priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- && priv->browse_files_last_selected_name)
- {
- const char *entry_text;
- int len;
- gboolean clear_entry;
+ /* Test validity of path here. */
+ data = g_new0 (struct UpdateCurrentFolderData, 1);
+ data->impl = impl;
+ data->file = g_object_ref (file);
+ data->keep_trail = keep_trail;
+ data->clear_entry = clear_entry;
- entry_text = gtk_entry_get_text (GTK_ENTRY (priv->location_entry));
- len = strlen (entry_text);
- if (len != 0)
- {
- /* The file chooser entry may have appended a "/" to its text.
- * So take it out, and compare the result to the old selection.
- */
- if (entry_text[len - 1] == G_DIR_SEPARATOR)
- {
- gchar *tmp;
+ priv->reload_state = RELOAD_HAS_FOLDER;
- tmp = g_strndup (entry_text, len - 1);
- clear_entry = (compare_utf8_filenames (priv->browse_files_last_selected_name, tmp) == 0);
- g_free (tmp);
- }
- else
- clear_entry = (compare_utf8_filenames (priv->browse_files_last_selected_name, entry_text) == 0);
- }
- else
- clear_entry = FALSE;
+ priv->update_current_folder_cancellable =
+ _gtk_file_system_get_info (priv->file_system, file,
+ "standard::type",
+ update_current_folder_get_info_cb,
+ data);
- if (clear_entry)
- gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
- }
+ set_busy_cursor (impl, TRUE);
+ g_object_unref (file);
+
+ profile_end ("end", NULL);
+ return TRUE;
}
-static gboolean
-gtk_file_chooser_default_set_current_folder (GtkFileChooser *chooser,
- GFile *file,
- GError **error)
+static GFile *
+gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
{
- return gtk_file_chooser_default_update_current_folder (chooser, file, FALSE, FALSE, error);
-}
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ if (priv->operation_mode == OPERATION_MODE_SEARCH ||
+ priv->operation_mode == OPERATION_MODE_RECENT)
+ return NULL;
+
+ if (priv->current_folder)
+ return g_object_ref (priv->current_folder);
-struct UpdateCurrentFolderData
-{
- GtkFileChooserDefault *impl;
- GFile *file;
- gboolean keep_trail;
- gboolean clear_entry;
- GFile *original_file;
- GError *original_error;
-};
+ return NULL;
+}
static void
-update_current_folder_mount_enclosing_volume_cb (GCancellable *cancellable,
- GtkFileSystemVolume *volume,
- const GError *error,
- gpointer user_data)
+gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
+ const gchar *name)
{
- struct UpdateCurrentFolderData *data = user_data;
- GtkFileChooserDefault *impl = data->impl;
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
GtkFileChooserDefaultPrivate *priv = impl->priv;
- gboolean cancelled = g_cancellable_is_cancelled (cancellable);
-
- if (cancellable != priv->update_current_folder_cancellable)
- goto out;
-
- priv->update_current_folder_cancellable = NULL;
- set_busy_cursor (impl, FALSE);
-
- if (cancelled)
- goto out;
-
- if (error)
- {
- error_changing_folder_dialog (data->impl, data->file, g_error_copy (error));
- priv->reload_state = RELOAD_EMPTY;
- goto out;
- }
-
- change_folder_and_display_error (impl, data->file, data->clear_entry);
-out:
- g_object_unref (data->file);
- g_free (data);
+ g_return_if_fail (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
- g_object_unref (cancellable);
+ pending_select_files_free (impl);
+ gtk_entry_set_text (GTK_ENTRY (priv->location_entry), name);
}
-static void
-update_current_folder_get_info_cb (GCancellable *cancellable,
- GFileInfo *info,
- const GError *error,
- gpointer user_data)
+static gboolean
+gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
+ GFile *file,
+ GError **error)
{
- gboolean cancelled = g_cancellable_is_cancelled (cancellable);
- struct UpdateCurrentFolderData *data = user_data;
- GtkFileChooserDefault *impl = data->impl;
+ GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GFile *parent_file;
+ gboolean same_path;
- if (cancellable != priv->update_current_folder_cancellable)
- goto out;
-
- priv->update_current_folder_cancellable = NULL;
- priv->reload_state = RELOAD_EMPTY;
-
- set_busy_cursor (impl, FALSE);
+ parent_file = g_file_get_parent (file);
- if (cancelled)
- goto out;
+ if (!parent_file)
+ return gtk_file_chooser_set_current_folder_file (chooser, file, error);
- if (error)
+ if (priv->operation_mode == OPERATION_MODE_SEARCH ||
+ priv->operation_mode == OPERATION_MODE_RECENT ||
+ priv->load_state == LOAD_EMPTY)
{
- GFile *parent_file;
-
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_MOUNTED))
- {
- GMountOperation *mount_operation;
- GtkWidget *toplevel;
-
- g_object_unref (cancellable);
- toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));
-
- mount_operation = gtk_mount_operation_new (GTK_WINDOW (toplevel));
-
- set_busy_cursor (impl, TRUE);
-
- priv->update_current_folder_cancellable =
- _gtk_file_system_mount_enclosing_volume (priv->file_system, data->file,
- mount_operation,
- update_current_folder_mount_enclosing_volume_cb,
- data);
-
- return;
- }
-
- if (!data->original_file)
- {
- data->original_file = g_object_ref (data->file);
- data->original_error = g_error_copy (error);
- }
-
- parent_file = g_file_get_parent (data->file);
-
- /* get parent path and try to change the folder to that */
- if (parent_file)
- {
- g_object_unref (data->file);
- data->file = parent_file;
-
- g_object_unref (cancellable);
-
- /* restart the update current folder operation */
- priv->reload_state = RELOAD_HAS_FOLDER;
-
- priv->update_current_folder_cancellable =
- _gtk_file_system_get_info (priv->file_system, data->file,
- "standard::type",
- update_current_folder_get_info_cb,
- data);
-
- set_busy_cursor (impl, TRUE);
-
- return;
- }
- else
- {
- /* Error and bail out, ignoring "not found" errors since they're useless:
- * they only happen when a program defaults to a folder that has been (re)moved.
- */
- if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- error_changing_folder_dialog (impl, data->original_file, data->original_error);
- else
- g_error_free (data->original_error);
-
- g_object_unref (data->original_file);
-
- goto out;
- }
- }
-
- if (data->original_file)
- {
- /* Error and bail out, ignoring "not found" errors since they're useless:
- * they only happen when a program defaults to a folder that has been (re)moved.
- */
- if (!g_error_matches (data->original_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
- error_changing_folder_dialog (impl, data->original_file, data->original_error);
- else
- g_error_free (data->original_error);
-
- g_object_unref (data->original_file);
- }
-
- if (! _gtk_file_info_consider_as_directory (info))
- goto out;
-
- _gtk_path_bar_set_file (GTK_PATH_BAR (priv->browse_path_bar), data->file, data->keep_trail);
-
- if (priv->current_folder != data->file)
- {
- if (priv->current_folder)
- g_object_unref (priv->current_folder);
-
- priv->current_folder = g_object_ref (data->file);
- }
-
- priv->reload_state = RELOAD_HAS_FOLDER;
-
- /* Set the folder on the save entry */
-
- if (priv->location_entry)
- {
- _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->location_entry),
- priv->current_folder);
-
- if (data->clear_entry)
- gtk_entry_set_text (GTK_ENTRY (priv->location_entry), "");
- }
-
- /* Create a new list model. This is slightly evil; we store the result value
- * but perform more actions rather than returning immediately even if it
- * generates an error.
- */
- set_list_model (impl, NULL);
-
- /* Refresh controls */
-
- gtk_places_sidebar_set_location (GTK_PLACES_SIDEBAR (priv->places_sidebar), priv->current_folder);
-
- g_signal_emit_by_name (impl, "current-folder-changed", 0);
-
- check_preview_change (impl);
-
- g_signal_emit_by_name (impl, "selection-changed", 0);
-
-out:
- g_object_unref (data->file);
- g_free (data);
-
- g_object_unref (cancellable);
-}
-
-static gboolean
-gtk_file_chooser_default_update_current_folder (GtkFileChooser *chooser,
- GFile *file,
- gboolean keep_trail,
- gboolean clear_entry,
- GError **error)
-{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- struct UpdateCurrentFolderData *data;
-
- profile_start ("start", NULL);
-
- g_object_ref (file);
-
- operation_mode_set (impl, OPERATION_MODE_BROWSE);
-
- if (priv->local_only && !_gtk_file_has_native_path (file))
- {
- g_set_error_literal (error,
- GTK_FILE_CHOOSER_ERROR,
- GTK_FILE_CHOOSER_ERROR_BAD_FILENAME,
- _("Cannot change to folder because it is not local"));
-
- g_object_unref (file);
- profile_end ("end - not local", NULL);
- return FALSE;
- }
-
- if (priv->update_current_folder_cancellable)
- g_cancellable_cancel (priv->update_current_folder_cancellable);
-
- /* Test validity of path here. */
- data = g_new0 (struct UpdateCurrentFolderData, 1);
- data->impl = impl;
- data->file = g_object_ref (file);
- data->keep_trail = keep_trail;
- data->clear_entry = clear_entry;
-
- priv->reload_state = RELOAD_HAS_FOLDER;
-
- priv->update_current_folder_cancellable =
- _gtk_file_system_get_info (priv->file_system, file,
- "standard::type",
- update_current_folder_get_info_cb,
- data);
-
- set_busy_cursor (impl, TRUE);
- g_object_unref (file);
-
- profile_end ("end", NULL);
- return TRUE;
-}
-
-static GFile *
-gtk_file_chooser_default_get_current_folder (GtkFileChooser *chooser)
-{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- if (priv->operation_mode == OPERATION_MODE_SEARCH ||
- priv->operation_mode == OPERATION_MODE_RECENT)
- return NULL;
-
- if (priv->current_folder)
- return g_object_ref (priv->current_folder);
-
- return NULL;
-}
-
-static void
-gtk_file_chooser_default_set_current_name (GtkFileChooser *chooser,
- const gchar *name)
-{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
-
- g_return_if_fail (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
-
- pending_select_files_free (impl);
- gtk_entry_set_text (GTK_ENTRY (priv->location_entry), name);
-}
-
-static gboolean
-gtk_file_chooser_default_select_file (GtkFileChooser *chooser,
- GFile *file,
- GError **error)
-{
- GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (chooser);
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GFile *parent_file;
- gboolean same_path;
-
- parent_file = g_file_get_parent (file);
-
- if (!parent_file)
- return gtk_file_chooser_set_current_folder_file (chooser, file, error);
-
- if (priv->operation_mode == OPERATION_MODE_SEARCH ||
- priv->operation_mode == OPERATION_MODE_RECENT ||
- priv->load_state == LOAD_EMPTY)
- {
- same_path = FALSE;
- }
- else
- {
- g_assert (priv->current_folder != NULL);
+ same_path = FALSE;
+ }
+ else
+ {
+ g_assert (priv->current_folder != NULL);
same_path = g_file_equal (parent_file, priv->current_folder);
}
@@ -7490,136 +7106,515 @@ location_set_user_text (GtkFileChooserDefault *impl,
gtk_editable_set_position (GTK_EDITABLE (priv->location_entry), -1);
}
-static void
-location_popup_handler (GtkFileChooserDefault *impl,
- const gchar *path)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+static void
+location_popup_handler (GtkFileChooserDefault *impl,
+ const gchar *path)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ if (priv->operation_mode != OPERATION_MODE_BROWSE)
+ {
+ GtkWidget *widget_to_focus;
+
+ operation_mode_set (impl, OPERATION_MODE_BROWSE);
+
+ if (priv->current_folder)
+ change_folder_and_display_error (impl, priv->current_folder, FALSE);
+
+ if (priv->location_mode == LOCATION_MODE_PATH_BAR)
+ widget_to_focus = priv->browse_files_tree_view;
+ else
+ widget_to_focus = priv->location_entry;
+
+ gtk_widget_grab_focus (widget_to_focus);
+ return;
+ }
+
+ if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+ {
+ if (!path)
+ return;
+
+ location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
+ location_set_user_text (impl, path);
+ }
+ else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
+ priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+ {
+ gtk_widget_grab_focus (priv->location_entry);
+ if (path != NULL)
+ location_set_user_text (impl, path);
+ }
+ else
+ g_assert_not_reached ();
+}
+
+/* Handler for the "up-folder" keybinding signal */
+static void
+up_folder_handler (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ _gtk_path_bar_up (GTK_PATH_BAR (priv->browse_path_bar));
+}
+
+/* Handler for the "down-folder" keybinding signal */
+static void
+down_folder_handler (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ _gtk_path_bar_down (GTK_PATH_BAR (priv->browse_path_bar));
+}
+
+/* Handler for the "home-folder" keybinding signal */
+static void
+home_folder_handler (GtkFileChooserDefault *impl)
+{
+ switch_to_home_dir (impl);
+}
+
+/* Handler for the "desktop-folder" keybinding signal */
+static void
+desktop_folder_handler (GtkFileChooserDefault *impl)
+{
+ const char *name;
+
+ name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
+ /* "To disable a directory, point it to the homedir."
+ * See http://freedesktop.org/wiki/Software/xdg-user-dirs
+ **/
+ if (!g_strcmp0 (name, g_get_home_dir ())) {
+ return;
+ }
+
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), name);
+}
+
+/* Handler for the "search-shortcut" keybinding signal */
+static void
+search_shortcut_handler (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ operation_mode_set (impl, OPERATION_MODE_SEARCH);
+
+ /* we want the entry widget to grab the focus the first
+ * time, not the browse_files_tree_view widget.
+ */
+ if (priv->search_entry)
+ gtk_widget_grab_focus (priv->search_entry);
+}
+
+/* Handler for the "recent-shortcut" keybinding signal */
+static void
+recent_shortcut_handler (GtkFileChooserDefault *impl)
+{
+ operation_mode_set (impl, OPERATION_MODE_RECENT);
+}
+
+static void
+quick_bookmark_handler (GtkFileChooserDefault *impl,
+ gint bookmark_index)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GFile *file;
+
+ file = gtk_places_sidebar_get_nth_bookmark (GTK_PLACES_SIDEBAR (priv->places_sidebar), bookmark_index);
+
+ if (file)
+ {
+ change_folder_and_display_error (impl, file, FALSE);
+ g_object_unref (file);
+ }
+}
+
+static void
+show_hidden_handler (GtkFileChooserDefault *impl)
+{
+ GtkFileChooserDefaultPrivate *priv = impl->priv;
+
+ g_object_set (impl,
+ "show-hidden", !priv->show_hidden,
+ NULL);
+}
+
+static void
+add_normal_and_shifted_binding (GtkBindingSet *binding_set,
+ guint keyval,
+ GdkModifierType modifiers,
+ const gchar *signal_name)
+{
+ gtk_binding_entry_add_signal (binding_set,
+ keyval, modifiers,
+ signal_name, 0);
+
+ gtk_binding_entry_add_signal (binding_set,
+ keyval, modifiers | GDK_SHIFT_MASK,
+ signal_name, 0);
+}
+
+static void
+_gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
+{
+ static const guint quick_bookmark_keyvals[10] = {
+ GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5, GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9,
GDK_KEY_0
+ };
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ GtkBindingSet *binding_set;
+ int i;
+
+ gobject_class->finalize = gtk_file_chooser_default_finalize;
+ gobject_class->constructor = gtk_file_chooser_default_constructor;
+ gobject_class->set_property = gtk_file_chooser_default_set_property;
+ gobject_class->get_property = gtk_file_chooser_default_get_property;
+ gobject_class->dispose = gtk_file_chooser_default_dispose;
+
+ widget_class->show_all = gtk_file_chooser_default_show_all;
+ widget_class->realize = gtk_file_chooser_default_realize;
+ widget_class->map = gtk_file_chooser_default_map;
+ widget_class->unmap = gtk_file_chooser_default_unmap;
+ widget_class->hierarchy_changed = gtk_file_chooser_default_hierarchy_changed;
+ widget_class->style_updated = gtk_file_chooser_default_style_updated;
+ widget_class->screen_changed = gtk_file_chooser_default_screen_changed;
+
+ signals[LOCATION_POPUP] =
+ g_signal_new_class_handler (I_("location-popup"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (location_popup_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__STRING,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[LOCATION_POPUP_ON_PASTE] =
+ g_signal_new_class_handler (I_("location-popup-on-paste"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (location_popup_on_paste_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[LOCATION_TOGGLE_POPUP] =
+ g_signal_new_class_handler (I_("location-toggle-popup"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (location_toggle_popup_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[UP_FOLDER] =
+ g_signal_new_class_handler (I_("up-folder"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (up_folder_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[DOWN_FOLDER] =
+ g_signal_new_class_handler (I_("down-folder"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (down_folder_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[HOME_FOLDER] =
+ g_signal_new_class_handler (I_("home-folder"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (home_folder_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[DESKTOP_FOLDER] =
+ g_signal_new_class_handler (I_("desktop-folder"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (desktop_folder_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[QUICK_BOOKMARK] =
+ g_signal_new_class_handler (I_("quick-bookmark"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (quick_bookmark_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ signals[SHOW_HIDDEN] =
+ g_signal_new_class_handler (I_("show-hidden"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (show_hidden_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[SEARCH_SHORTCUT] =
+ g_signal_new_class_handler (I_("search-shortcut"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (search_shortcut_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[RECENT_SHORTCUT] =
+ g_signal_new_class_handler (I_("recent-shortcut"),
+ G_OBJECT_CLASS_TYPE (class),
+ G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+ G_CALLBACK (recent_shortcut_handler),
+ NULL, NULL,
+ _gtk_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ binding_set = gtk_binding_set_by_class (class);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_l, GDK_CONTROL_MASK,
+ "location-toggle-popup",
+ 0);
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_slash, 0,
+ "location-popup",
+ 1, G_TYPE_STRING, "/");
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_KP_Divide, 0,
+ "location-popup",
+ 1, G_TYPE_STRING, "/");
+
+#ifdef G_OS_UNIX
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_asciitilde, 0,
+ "location-popup",
+ 1, G_TYPE_STRING, "~");
+#endif
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_v, GDK_CONTROL_MASK,
+ "location-popup-on-paste",
+ 0);
+
+ add_normal_and_shifted_binding (binding_set,
+ GDK_KEY_Up, GDK_MOD1_MASK,
+ "up-folder");
+
+ add_normal_and_shifted_binding (binding_set,
+ GDK_KEY_KP_Up, GDK_MOD1_MASK,
+ "up-folder");
+
+ add_normal_and_shifted_binding (binding_set,
+ GDK_KEY_Down, GDK_MOD1_MASK,
+ "down-folder");
+ add_normal_and_shifted_binding (binding_set,
+ GDK_KEY_KP_Down, GDK_MOD1_MASK,
+ "down-folder");
+
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_Home, GDK_MOD1_MASK,
+ "home-folder",
+ 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_KP_Home, GDK_MOD1_MASK,
+ "home-folder",
+ 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_d, GDK_MOD1_MASK,
+ "desktop-folder",
+ 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_h, GDK_CONTROL_MASK,
+ "show-hidden",
+ 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_s, GDK_MOD1_MASK,
+ "search-shortcut",
+ 0);
+ gtk_binding_entry_add_signal (binding_set,
+ GDK_KEY_r, GDK_MOD1_MASK,
+ "recent-shortcut",
+ 0);
+
+ for (i = 0; i < 10; i++)
+ gtk_binding_entry_add_signal (binding_set,
+ quick_bookmark_keyvals[i], GDK_MOD1_MASK,
+ "quick-bookmark",
+ 1, G_TYPE_INT, i);
- if (priv->operation_mode != OPERATION_MODE_BROWSE)
- {
- GtkWidget *widget_to_focus;
+ _gtk_file_chooser_install_properties (gobject_class);
- operation_mode_set (impl, OPERATION_MODE_BROWSE);
-
- if (priv->current_folder)
- change_folder_and_display_error (impl, priv->current_folder, FALSE);
+ g_type_class_add_private (gobject_class, sizeof (GtkFileChooserDefaultPrivate));
- if (priv->location_mode == LOCATION_MODE_PATH_BAR)
- widget_to_focus = priv->browse_files_tree_view;
- else
- widget_to_focus = priv->location_entry;
+ /* Bind class to template */
- gtk_widget_grab_focus (widget_to_focus);
- return;
- }
-
- if (priv->action == GTK_FILE_CHOOSER_ACTION_OPEN ||
- priv->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
- {
- if (!path)
- return;
+ gtk_widget_class_set_template_from_resource (widget_class,
+ "/org/gtk/libgtk/gtkfilechooserdefault.ui");
- location_mode_set (impl, LOCATION_MODE_FILENAME_ENTRY, TRUE);
- location_set_user_text (impl, path);
- }
- else if (priv->action == GTK_FILE_CHOOSER_ACTION_SAVE ||
- priv->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
- {
- gtk_widget_grab_focus (priv->location_entry);
- if (path != NULL)
- location_set_user_text (impl, path);
- }
- else
- g_assert_not_reached ();
-}
+ /* A *lot* of widgets that we need to handle .... */
-/* Handler for the "up-folder" keybinding signal */
-static void
-up_folder_handler (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_box);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_hpaned);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_header_box);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_widgets_box);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, places_sidebar);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_files_tree_view);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_new_folder_button);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar_hbox);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar_size_group);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_path_bar);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_special_mode_icon);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_special_mode_label);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_select_a_folder_info_bar);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_select_a_folder_label);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, browse_select_a_folder_icon);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, filter_combo_hbox);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, filter_combo);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, preview_box);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, extra_align);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_button);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_entry_box);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, location_label);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_name_column);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_pixbuf_renderer);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_name_renderer);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_mtime_column);
+ gtk_widget_class_bind_child (widget_class, GtkFileChooserDefaultPrivate, list_size_column);
- _gtk_path_bar_up (GTK_PATH_BAR (priv->browse_path_bar));
+ /* And a *lot* of callbacks to bind ... */
+ gtk_widget_class_bind_callback (widget_class, browse_files_key_press_event_cb);
+ gtk_widget_class_bind_callback (widget_class, file_list_drag_drop_cb);
+ gtk_widget_class_bind_callback (widget_class, file_list_drag_data_received_cb);
+ gtk_widget_class_bind_callback (widget_class, list_popup_menu_cb);
+ gtk_widget_class_bind_callback (widget_class, file_list_query_tooltip_cb);
+ gtk_widget_class_bind_callback (widget_class, list_button_press_event_cb);
+ gtk_widget_class_bind_callback (widget_class, list_row_activated);
+ gtk_widget_class_bind_callback (widget_class, file_list_drag_motion_cb);
+ gtk_widget_class_bind_callback (widget_class, list_selection_changed);
+ gtk_widget_class_bind_callback (widget_class, renderer_editing_canceled_cb);
+ gtk_widget_class_bind_callback (widget_class, renderer_edited_cb);
+ gtk_widget_class_bind_callback (widget_class, filter_combo_changed);
+ gtk_widget_class_bind_callback (widget_class, location_button_toggled_cb);
+ gtk_widget_class_bind_callback (widget_class, new_folder_button_clicked);
+ gtk_widget_class_bind_callback (widget_class, path_bar_clicked);
+ gtk_widget_class_bind_callback (widget_class, places_sidebar_open_location_cb);
+ gtk_widget_class_bind_callback (widget_class, places_sidebar_show_error_message_cb);
}
-/* Handler for the "down-folder" keybinding signal */
static void
-down_folder_handler (GtkFileChooserDefault *impl)
+post_process_ui (GtkFileChooserDefault *impl)
{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ GtkTreeSelection *selection;
+ GtkCellRenderer *cell;
+ GList *cells;
- _gtk_path_bar_down (GTK_PATH_BAR (priv->browse_path_bar));
-}
+ /* Some qdata, qdata can't be set with GtkBuilder */
+ g_object_set_data (G_OBJECT (impl->priv->browse_files_tree_view), "fmq-name", "file_list");
+ g_object_set_data (G_OBJECT (impl->priv->browse_files_tree_view), I_("GtkFileChooserDefault"), impl);
-/* Handler for the "home-folder" keybinding signal */
-static void
-home_folder_handler (GtkFileChooserDefault *impl)
-{
- switch_to_home_dir (impl);
-}
+ /* Setup file list treeview */
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->priv->browse_files_tree_view));
+ gtk_tree_selection_set_select_function (selection,
+ list_select_func,
+ impl, NULL);
+ gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (impl->priv->browse_files_tree_view),
+ GDK_BUTTON1_MASK,
+ NULL, 0,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ gtk_drag_source_add_uri_targets (impl->priv->browse_files_tree_view);
-/* Handler for the "desktop-folder" keybinding signal */
-static void
-desktop_folder_handler (GtkFileChooserDefault *impl)
-{
- const char *name;
+ gtk_drag_dest_set (impl->priv->browse_files_tree_view,
+ GTK_DEST_DEFAULT_ALL,
+ NULL, 0,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+ gtk_drag_dest_add_uri_targets (impl->priv->browse_files_tree_view);
- name = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
- /* "To disable a directory, point it to the homedir."
- * See http://freedesktop.org/wiki/Software/xdg-user-dirs
- **/
- if (!g_strcmp0 (name, g_get_home_dir ())) {
- return;
- }
+ /* File browser treemodel columns are shared between GtkFileChooser implementations,
+ * so we don't set cell renderer attributes in GtkBuilder, but rather keep that
+ * in code.
+ */
+ file_list_set_sort_column_ids (impl);
+ update_cell_renderer_attributes (impl);
- gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (impl), name);
-}
+ /* Get the combo's text renderer and set ellipsize parameters,
+ * perhaps GtkComboBoxText should declare the cell renderer
+ * as an 'internal-child', then we could configure it in GtkBuilder
+ * instead of hard coding it here.
+ */
+ cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (impl->priv->filter_combo));
+ g_assert (cells);
+ cell = cells->data;
+ g_object_set (G_OBJECT (cell),
+ "ellipsize", PANGO_ELLIPSIZE_END,
+ NULL);
-/* Handler for the "search-shortcut" keybinding signal */
-static void
-search_shortcut_handler (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ g_list_free (cells);
- operation_mode_set (impl, OPERATION_MODE_SEARCH);
+ /* Set the GtkPathBar file system backend */
+ _gtk_path_bar_set_file_system (GTK_PATH_BAR (impl->priv->browse_path_bar), impl->priv->file_system);
- /* we want the entry widget to grab the focus the first
- * time, not the browse_files_tree_view widget.
+ /* Set the fixed size icon renderer, this requires
+ * that priv->icon_size be already setup.
*/
- if (priv->search_entry)
- gtk_widget_grab_focus (priv->search_entry);
+ set_icon_cell_renderer_fixed_size (impl);
}
-/* Handler for the "recent-shortcut" keybinding signal */
static void
-recent_shortcut_handler (GtkFileChooserDefault *impl)
+_gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
{
- operation_mode_set (impl, OPERATION_MODE_RECENT);
-}
+ GtkFileChooserDefaultPrivate *priv;
-static void
-quick_bookmark_handler (GtkFileChooserDefault *impl,
- gint bookmark_index)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
- GFile *file;
+ profile_start ("start", NULL);
+#ifdef PROFILE_FILE_CHOOSER
+ access ("MARK: *** CREATE FILE CHOOSER", F_OK);
+#endif
+ impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (impl,
+ GTK_TYPE_FILE_CHOOSER_DEFAULT,
+ GtkFileChooserDefaultPrivate);
+ priv = impl->priv;
- file = gtk_places_sidebar_get_nth_bookmark (GTK_PLACES_SIDEBAR (priv->places_sidebar), bookmark_index);
+ priv->local_only = TRUE;
+ priv->preview_widget_active = TRUE;
+ priv->use_preview_label = TRUE;
+ priv->select_multiple = FALSE;
+ priv->show_hidden = FALSE;
+ priv->show_size_column = TRUE;
+ priv->icon_size = FALLBACK_ICON_SIZE;
+ priv->load_state = LOAD_EMPTY;
+ priv->reload_state = RELOAD_EMPTY;
+ priv->pending_select_files = NULL;
+ priv->location_mode = LOCATION_MODE_PATH_BAR;
+ priv->operation_mode = OPERATION_MODE_BROWSE;
+ priv->sort_column = MODEL_COL_NAME;
+ priv->sort_order = GTK_SORT_ASCENDING;
+ priv->recent_manager = gtk_recent_manager_get_default ();
+ priv->create_folders = TRUE;
- if (file)
- {
- change_folder_and_display_error (impl, file, FALSE);
- g_object_unref (file);
- }
-}
+ /* Ensure GTK+ private types used by the template
+ * definition before calling gtk_widget_init_template()
+ */
+ g_type_ensure (GTK_TYPE_PATH_BAR);
+ gtk_widget_init_template (GTK_WIDGET (impl));
-static void
-show_hidden_handler (GtkFileChooserDefault *impl)
-{
- GtkFileChooserDefaultPrivate *priv = impl->priv;
+ set_file_system_backend (impl);
- g_object_set (impl,
- "show-hidden", !priv->show_hidden,
- NULL);
+ priv->bookmarks_manager = _gtk_bookmarks_manager_new (NULL, NULL);
+
+ /* Setup various attributes and callbacks in the UI
+ * which cannot be done with GtkBuilder.
+ */
+ post_process_ui (impl);
+
+ profile_end ("end", NULL);
}
diff --git a/gtk/gtkfilechooserdefault.ui b/gtk/gtkfilechooserdefault.ui
index 0e4f1f5..5cf2ef3 100644
--- a/gtk/gtkfilechooserdefault.ui
+++ b/gtk/gtkfilechooserdefault.ui
@@ -412,11 +412,4 @@
<widget name="browse_new_folder_button"/>
</widgets>
</object>
- <object class="GtkSizeGroup" id="paned_size_group">
- <property name="mode">vertical</property>
- <widgets>
- <widget name="browse_shortcuts_swin"/>
- <widget name="browse_files_swin"/>
- </widgets>
- </object>
</interface>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]