1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
3 /* GTK+: gtkfilechooserbutton.c
5 * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #include <sys/types.h>
34 #include "gtkbutton.h"
35 #include "gtkcelllayout.h"
36 #include "gtkcellrenderertext.h"
37 #include "gtkcellrendererpixbuf.h"
38 #include "gtkcombobox.h"
40 #include "gtkicontheme.h"
41 #include "gtkiconfactory.h"
44 #include "gtkliststore.h"
46 #include "gtktreemodelfilter.h"
47 #include "gtkvseparator.h"
48 #include "gtkfilechooserdialog.h"
49 #include "gtkfilechooserprivate.h"
50 #include "gtkfilechooserutils.h"
52 #include "gtkfilechooserbutton.h"
55 #include "gtkfilesystemwin32.h"
58 #include "gtkprivate.h"
65 #define GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FILE_CHOOSER_BUTTON, GtkFileChooserButtonPrivate))
67 #define DEFAULT_TITLE N_("Select A File")
68 #define DESKTOP_DISPLAY_NAME N_("Desktop")
69 #define FALLBACK_DISPLAY_NAME N_("(None)")
70 #define FALLBACK_ICON_NAME "stock_unknown"
71 #define FALLBACK_ICON_SIZE 16
74 /* ********************** *
75 * Private Enumerations *
76 * ********************** */
89 /* TreeModel Columns */
99 /* TreeModel Row Types */
105 ROW_TYPE_BOOKMARK_SEPARATOR,
107 ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
108 ROW_TYPE_CURRENT_FOLDER,
109 ROW_TYPE_OTHER_SEPARATOR,
112 ROW_TYPE_INVALID = -1
117 /* ******************** *
118 * Private Structures *
119 * ******************** */
121 struct _GtkFileChooserButtonPrivate
127 GtkWidget *combo_box;
128 GtkCellRenderer *icon_cell;
129 GtkCellRenderer *name_cell;
132 GtkTreeModel *filter_model;
136 GtkFilePath *old_path;
138 gulong combo_box_changed_id;
139 gulong dialog_file_activated_id;
140 gulong dialog_folder_changed_id;
141 gulong dialog_selection_changed_id;
142 gulong fs_volumes_changed_id;
143 gulong fs_bookmarks_changed_id;
151 guint8 has_bookmark_separator : 1;
152 guint8 has_current_folder_separator : 1;
153 guint8 has_current_folder : 1;
154 guint8 has_other_separator : 1;
156 /* Used for hiding/showing the dialog when the button is hidden */
159 /* Used to track whether we need to set a default current folder on ::map() */
160 guint8 folder_has_been_set : 1;
162 guint8 focus_on_click : 1;
177 /* ********************* *
178 * Function Prototypes *
179 * ********************* */
181 /* GtkFileChooserIface Functions */
182 static void gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface);
183 static gboolean gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser,
184 const GtkFilePath *path,
186 static gboolean gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser *chooser,
187 const GtkFilePath *path,
190 /* GObject Functions */
191 static GObject *gtk_file_chooser_button_constructor (GType type,
193 GObjectConstructParam *params);
194 static void gtk_file_chooser_button_set_property (GObject *object,
198 static void gtk_file_chooser_button_get_property (GObject *object,
202 static void gtk_file_chooser_button_finalize (GObject *object);
204 /* GtkObject Functions */
205 static void gtk_file_chooser_button_destroy (GtkObject *object);
207 /* GtkWidget Functions */
208 static void gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
209 GdkDragContext *context,
212 GtkSelectionData *data,
215 static void gtk_file_chooser_button_show_all (GtkWidget *widget);
216 static void gtk_file_chooser_button_hide_all (GtkWidget *widget);
217 static void gtk_file_chooser_button_show (GtkWidget *widget);
218 static void gtk_file_chooser_button_hide (GtkWidget *widget);
219 static void gtk_file_chooser_button_map (GtkWidget *widget);
220 static gboolean gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
221 gboolean group_cycling);
222 static void gtk_file_chooser_button_style_set (GtkWidget *widget,
223 GtkStyle *old_style);
224 static void gtk_file_chooser_button_screen_changed (GtkWidget *widget,
225 GdkScreen *old_screen);
227 /* Utility Functions */
228 static GtkIconTheme *get_icon_theme (GtkWidget *widget);
229 static gchar *get_display_name_for_path (GtkFileSystem *fs,
230 const GtkFilePath *path);
232 static gint model_get_type_position (GtkFileChooserButton *button,
234 static void model_free_row_data (GtkFileChooserButton *button,
236 static inline void model_add_special (GtkFileChooserButton *button);
237 static inline void model_add_other (GtkFileChooserButton *button);
238 static void model_add_volumes (GtkFileChooserButton *button,
240 static void model_add_bookmarks (GtkFileChooserButton *button,
242 static void model_update_current_folder (GtkFileChooserButton *button,
243 const GtkFilePath *path);
244 static void model_remove_rows (GtkFileChooserButton *button,
248 static gboolean filter_model_visible_func (GtkTreeModel *model,
252 static gboolean combo_box_row_separator_func (GtkTreeModel *model,
255 static void name_cell_data_func (GtkCellLayout *layout,
256 GtkCellRenderer *cell,
260 static void open_dialog (GtkFileChooserButton *button);
261 static void update_combo_box (GtkFileChooserButton *button);
262 static void update_label_and_image (GtkFileChooserButton *button);
264 /* Child Object Callbacks */
265 static void fs_volumes_changed_cb (GtkFileSystem *fs,
267 static void fs_bookmarks_changed_cb (GtkFileSystem *fs,
270 static void combo_box_changed_cb (GtkComboBox *combo_box,
273 static void button_clicked_cb (GtkButton *real_button,
276 static void dialog_update_preview_cb (GtkFileChooser *dialog,
278 static void dialog_selection_changed_cb (GtkFileChooser *dialog,
280 static void dialog_file_activated_cb (GtkFileChooser *dialog,
282 static void dialog_current_folder_changed_cb (GtkFileChooser *dialog,
284 static void dialog_notify_cb (GObject *dialog,
287 static gboolean dialog_delete_event_cb (GtkWidget *dialog,
290 static void dialog_response_cb (GtkDialog *dialog,
295 /* ******************* *
296 * GType Declaration *
297 * ******************* */
299 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \
300 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
304 /* ***************** *
306 * ***************** */
309 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
311 GObjectClass *gobject_class;
312 GtkObjectClass *gtkobject_class;
313 GtkWidgetClass *widget_class;
315 gobject_class = G_OBJECT_CLASS (class);
316 gtkobject_class = GTK_OBJECT_CLASS (class);
317 widget_class = GTK_WIDGET_CLASS (class);
319 gobject_class->constructor = gtk_file_chooser_button_constructor;
320 gobject_class->set_property = gtk_file_chooser_button_set_property;
321 gobject_class->get_property = gtk_file_chooser_button_get_property;
322 gobject_class->finalize = gtk_file_chooser_button_finalize;
324 gtkobject_class->destroy = gtk_file_chooser_button_destroy;
326 widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
327 widget_class->show_all = gtk_file_chooser_button_show_all;
328 widget_class->hide_all = gtk_file_chooser_button_hide_all;
329 widget_class->show = gtk_file_chooser_button_show;
330 widget_class->hide = gtk_file_chooser_button_hide;
331 widget_class->map = gtk_file_chooser_button_map;
332 widget_class->style_set = gtk_file_chooser_button_style_set;
333 widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
334 widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
337 * GtkFileChooserButton:dialog:
339 * Instance of the #GtkFileChooserDialog associated with the button.
343 g_object_class_install_property (gobject_class, PROP_DIALOG,
344 g_param_spec_object ("dialog",
346 P_("The file chooser dialog to use."),
347 GTK_TYPE_FILE_CHOOSER_DIALOG,
348 (GTK_PARAM_WRITABLE |
349 G_PARAM_CONSTRUCT_ONLY)));
352 * GtkFileChooserButton:focus-on-click:
354 * Whether the #GtkFileChooserButton button grabs focus when it is clicked
359 g_object_class_install_property (gobject_class,
361 g_param_spec_boolean ("focus-on-click",
362 P_("Focus on click"),
363 P_("Whether the button grabs focus when it is clicked with the mouse"),
365 GTK_PARAM_READWRITE));
368 * GtkFileChooserButton:title:
370 * Title to put on the #GtkFileChooserDialog associated with the button.
374 g_object_class_install_property (gobject_class, PROP_TITLE,
375 g_param_spec_string ("title",
377 P_("The title of the file chooser dialog."),
379 GTK_PARAM_READWRITE));
382 * GtkFileChooserButton:width-chars:
384 * The width of the entry and label inside the button, in characters.
388 g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
389 g_param_spec_int ("width-chars",
390 P_("Width In Characters"),
391 P_("The desired width of the button widget, in characters."),
393 GTK_PARAM_READWRITE));
395 _gtk_file_chooser_install_properties (gobject_class);
397 g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
401 gtk_file_chooser_button_init (GtkFileChooserButton *button)
403 GtkFileChooserButtonPrivate *priv;
404 GtkWidget *box, *image, *sep;
405 GtkTargetList *target_list;
407 priv = button->priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
409 priv->icon_size = FALLBACK_ICON_SIZE;
410 priv->focus_on_click = TRUE;
412 gtk_widget_push_composite_child ();
415 priv->button = gtk_button_new ();
416 g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb),
418 gtk_container_add (GTK_CONTAINER (button), priv->button);
419 gtk_widget_show (priv->button);
421 box = gtk_hbox_new (FALSE, 4);
422 gtk_container_add (GTK_CONTAINER (priv->button), box);
423 gtk_widget_show (box);
425 priv->image = gtk_image_new ();
426 gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
427 gtk_widget_show (priv->image);
429 priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
430 gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
431 gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
432 gtk_container_add (GTK_CONTAINER (box), priv->label);
433 gtk_widget_show (priv->label);
435 sep = gtk_vseparator_new ();
436 gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
437 gtk_widget_show (sep);
439 image = gtk_image_new_from_stock (GTK_STOCK_OPEN,
441 gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
442 gtk_widget_show (image);
445 /* Keep in sync with columns enum, line 88 */
447 GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
448 GDK_TYPE_PIXBUF, /* Icon */
449 G_TYPE_STRING, /* Display Name */
450 G_TYPE_CHAR, /* Row Type */
451 G_TYPE_POINTER /* Volume || Path */));
453 priv->combo_box = gtk_combo_box_new ();
454 priv->combo_box_changed_id =
455 g_signal_connect (priv->combo_box, "changed",
456 G_CALLBACK (combo_box_changed_cb), button);
457 gtk_container_add (GTK_CONTAINER (button), priv->combo_box);
459 priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
460 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
461 priv->icon_cell, FALSE);
462 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
463 priv->icon_cell, "pixbuf", ICON_COLUMN);
465 priv->name_cell = gtk_cell_renderer_text_new ();
466 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
467 priv->name_cell, TRUE);
468 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
469 priv->name_cell, "text", DISPLAY_NAME_COLUMN);
470 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box),
471 priv->name_cell, name_cell_data_func,
474 gtk_widget_pop_composite_child ();
477 gtk_drag_dest_set (GTK_WIDGET (button),
478 (GTK_DEST_DEFAULT_ALL),
481 target_list = gtk_target_list_new (NULL, 0);
482 gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
483 gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
484 gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
485 gtk_target_list_unref (target_list);
489 /* ******************************* *
490 * GtkFileChooserIface Functions *
491 * ******************************* */
493 gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
495 _gtk_file_chooser_delegate_iface_init (iface);
497 iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder;
498 iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
502 gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser *chooser,
503 const GtkFilePath *path,
506 GtkFileChooser *delegate;
509 delegate = g_object_get_qdata (G_OBJECT (chooser),
510 GTK_FILE_CHOOSER_DELEGATE_QUARK);
511 retval = _gtk_file_chooser_add_shortcut_folder (delegate, path, error);
515 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
516 GtkFileChooserButtonPrivate *priv = button->priv;
522 pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
523 pos += priv->n_shortcuts;
525 pixbuf = gtk_file_system_render_icon (priv->fs, path,
526 GTK_WIDGET (chooser),
527 priv->icon_size, NULL);
528 display_name = get_display_name_for_path (priv->fs, path);
530 gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
531 gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
533 DISPLAY_NAME_COLUMN, display_name,
534 TYPE_COLUMN, ROW_TYPE_SHORTCUT,
535 DATA_COLUMN, gtk_file_path_copy (path),
540 g_object_unref (pixbuf);
541 g_free (display_name);
543 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
550 gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser *chooser,
551 const GtkFilePath *path,
554 GtkFileChooser *delegate;
557 delegate = g_object_get_qdata (G_OBJECT (chooser),
558 GTK_FILE_CHOOSER_DELEGATE_QUARK);
560 retval = _gtk_file_chooser_remove_shortcut_folder (delegate, path, error);
564 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
565 GtkFileChooserButtonPrivate *priv = button->priv;
570 pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
571 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
577 gtk_tree_model_get (priv->model, &iter,
582 if (type == ROW_TYPE_SHORTCUT &&
584 gtk_file_path_compare (data, path) == 0)
586 model_free_row_data (GTK_FILE_CHOOSER_BUTTON (chooser), &iter);
587 gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
589 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
590 update_combo_box (GTK_FILE_CHOOSER_BUTTON (chooser));
594 while (type == ROW_TYPE_SHORTCUT &&
595 gtk_tree_model_iter_next (priv->model, &iter));
602 /* ******************* *
603 * GObject Functions *
604 * ******************* */
607 gtk_file_chooser_button_constructor (GType type,
609 GObjectConstructParam *params)
612 GtkFileChooserButton *button;
613 GtkFileChooserButtonPrivate *priv;
615 char *current_folder;
617 object = (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor) (type,
620 button = GTK_FILE_CHOOSER_BUTTON (object);
626 priv->dialog = gtk_file_chooser_dialog_new_with_backend (NULL, NULL,
627 GTK_FILE_CHOOSER_ACTION_OPEN,
635 priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
636 GTK_FILE_CHOOSER_ACTION_OPEN,
643 gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
644 GTK_RESPONSE_ACCEPT);
645 gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
651 current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (priv->dialog));
652 if (current_folder != NULL)
654 priv->folder_has_been_set = TRUE;
655 g_free (current_folder);
658 g_free (priv->backend);
659 priv->backend = NULL;
661 g_signal_connect (priv->dialog, "delete-event",
662 G_CALLBACK (dialog_delete_event_cb), object);
663 g_signal_connect (priv->dialog, "response",
664 G_CALLBACK (dialog_response_cb), object);
666 /* This is used, instead of the standard delegate, to ensure that signals are only
667 * delegated when the OK button is pressed. */
668 g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
669 priv->dialog_folder_changed_id =
670 g_signal_connect (priv->dialog, "current-folder-changed",
671 G_CALLBACK (dialog_current_folder_changed_cb), object);
672 priv->dialog_file_activated_id =
673 g_signal_connect (priv->dialog, "file-activated",
674 G_CALLBACK (dialog_file_activated_cb), object);
675 priv->dialog_selection_changed_id =
676 g_signal_connect (priv->dialog, "selection-changed",
677 G_CALLBACK (dialog_selection_changed_cb), object);
678 g_signal_connect (priv->dialog, "update-preview",
679 G_CALLBACK (dialog_update_preview_cb), object);
680 g_signal_connect (priv->dialog, "notify",
681 G_CALLBACK (dialog_notify_cb), object);
682 g_object_add_weak_pointer (G_OBJECT (priv->dialog),
683 (gpointer *) (&priv->dialog));
686 g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
688 model_add_special (button);
690 list = gtk_file_system_list_volumes (priv->fs);
691 model_add_volumes (button, list);
694 list = gtk_file_system_list_bookmarks (priv->fs);
695 model_add_bookmarks (button, list);
696 gtk_file_paths_free (list);
698 model_add_other (button);
700 priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
701 gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
702 filter_model_visible_func,
705 gtk_combo_box_set_model (GTK_COMBO_BOX (priv->combo_box), priv->filter_model);
706 gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->combo_box),
707 combo_box_row_separator_func,
710 /* set up the action for a user-provided dialog, this also updates
711 * the label, image and combobox
713 g_object_set (object,
714 "action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)),
717 priv->fs_volumes_changed_id =
718 g_signal_connect (priv->fs, "volumes-changed",
719 G_CALLBACK (fs_volumes_changed_cb), object);
720 priv->fs_bookmarks_changed_id =
721 g_signal_connect (priv->fs, "bookmarks-changed",
722 G_CALLBACK (fs_bookmarks_changed_cb), object);
728 gtk_file_chooser_button_set_property (GObject *object,
733 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
734 GtkFileChooserButtonPrivate *priv = button->priv;
740 priv->dialog = g_value_get_object (value);
742 case PROP_FOCUS_ON_CLICK:
743 gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
745 case PROP_WIDTH_CHARS:
746 gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
747 g_value_get_int (value));
749 case GTK_FILE_CHOOSER_PROP_ACTION:
750 switch (g_value_get_enum (value))
752 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
753 case GTK_FILE_CHOOSER_ACTION_SAVE:
758 eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
759 eval = g_enum_get_value (eclass, g_value_get_enum (value));
760 g_warning ("%s: Choosers of type `%s' do not support `%s'.",
761 G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
763 g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
768 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
769 update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
770 update_combo_box (GTK_FILE_CHOOSER_BUTTON (object));
772 switch (g_value_get_enum (value))
774 case GTK_FILE_CHOOSER_ACTION_OPEN:
775 gtk_widget_hide (priv->combo_box);
776 gtk_widget_show (priv->button);
778 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
779 gtk_widget_hide (priv->button);
780 gtk_widget_show (priv->combo_box);
783 g_assert_not_reached ();
789 case GTK_FILE_CHOOSER_PROP_FILTER:
790 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
791 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
792 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
793 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
794 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
795 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
796 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
797 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
800 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
802 priv->backend = g_value_dup_string (value);
805 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
806 g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
807 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
810 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
816 gtk_file_chooser_button_get_property (GObject *object,
821 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
822 GtkFileChooserButtonPrivate *priv = button->priv;
826 case PROP_WIDTH_CHARS:
827 g_value_set_int (value,
828 gtk_label_get_width_chars (GTK_LABEL (priv->label)));
830 case PROP_FOCUS_ON_CLICK:
831 g_value_set_boolean (value,
832 gtk_file_chooser_button_get_focus_on_click (button));
836 case GTK_FILE_CHOOSER_PROP_ACTION:
837 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
838 case GTK_FILE_CHOOSER_PROP_FILTER:
839 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
840 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
841 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
842 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
843 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
844 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
845 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
846 case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
847 g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
851 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
857 gtk_file_chooser_button_finalize (GObject *object)
859 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
860 GtkFileChooserButtonPrivate *priv = button->priv;
864 gtk_file_path_free (priv->old_path);
866 gtk_tree_model_get_iter_first (priv->model, &iter);
870 model_free_row_data (button, &iter);
872 while (gtk_tree_model_iter_next (priv->model, &iter));
874 g_object_unref (priv->model);
875 g_object_unref (priv->filter_model);
877 g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
878 g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
879 g_object_unref (priv->fs);
881 if (G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize != NULL)
882 (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize) (object);
885 /* ********************* *
886 * GtkObject Functions *
887 * ********************* */
890 gtk_file_chooser_button_destroy (GtkObject *object)
892 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
893 GtkFileChooserButtonPrivate *priv = button->priv;
895 if (priv->dialog != NULL)
896 gtk_widget_destroy (priv->dialog);
898 if (GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy != NULL)
899 (*GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy) (object);
903 /* ********************* *
904 * GtkWidget Functions *
905 * ********************* */
908 gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
909 GdkDragContext *context,
912 GtkSelectionData *data,
916 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
917 GtkFileChooserButtonPrivate *priv = button->priv;
921 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
922 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received) (widget,
928 if (widget == NULL || context == NULL || data == NULL || data->length < 0)
936 GtkFilePath *base_path;
940 uris = gtk_selection_data_get_uris (data);
946 for (i = 0; !selected && uris[i] != NULL; i++)
948 path = gtk_file_system_uri_to_path (priv->fs, uris[i]);
952 gtk_file_system_get_parent (priv->fs, path, &base_path, NULL))
954 GtkFileFolder *folder;
957 folder = gtk_file_system_get_folder (priv->fs, base_path,
958 GTK_FILE_INFO_IS_FOLDER,
961 info = gtk_file_folder_get_info (folder, path, NULL);
965 GtkFileChooserAction action;
967 g_object_get (priv->dialog, "action", &action, NULL);
970 (((action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
971 gtk_file_info_get_is_folder (info)) ||
972 (action == GTK_FILE_CHOOSER_ACTION_OPEN &&
973 !gtk_file_info_get_is_folder (info))) &&
974 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
977 gtk_file_info_free (info);
982 gtk_file_path_free (base_path);
985 gtk_file_path_free (path);
993 text = (char*) gtk_selection_data_get_text (data);
994 path = gtk_file_path_new_steal (text);
995 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog), path,
997 gtk_file_path_free (path);
1004 gtk_drag_finish (context, TRUE, FALSE, drag_time);
1008 gtk_file_chooser_button_show_all (GtkWidget *widget)
1010 gtk_widget_show (widget);
1014 gtk_file_chooser_button_hide_all (GtkWidget *widget)
1016 gtk_widget_hide (widget);
1020 gtk_file_chooser_button_show (GtkWidget *widget)
1022 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1023 GtkFileChooserButtonPrivate *priv = button->priv;
1025 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
1026 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show) (widget);
1029 open_dialog (GTK_FILE_CHOOSER_BUTTON (widget));
1033 gtk_file_chooser_button_hide (GtkWidget *widget)
1035 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1036 GtkFileChooserButtonPrivate *priv = button->priv;
1038 gtk_widget_hide (priv->dialog);
1040 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
1041 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide) (widget);
1045 gtk_file_chooser_button_map (GtkWidget *widget)
1047 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1048 GtkFileChooserButtonPrivate *priv = button->priv;
1050 if (!priv->folder_has_been_set)
1052 char *current_working_dir;
1054 current_working_dir = g_get_current_dir ();
1055 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), current_working_dir);
1056 g_free (current_working_dir);
1058 priv->folder_has_been_set = TRUE;
1061 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map)
1062 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map) (widget);
1066 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
1067 gboolean group_cycling)
1069 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1070 GtkFileChooserButtonPrivate *priv = button->priv;
1072 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1074 case GTK_FILE_CHOOSER_ACTION_OPEN:
1075 gtk_widget_grab_focus (priv->button);
1077 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1078 return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
1081 g_assert_not_reached ();
1088 /* Changes the icons wherever it is needed */
1090 change_icon_theme (GtkFileChooserButton *button)
1092 GtkFileChooserButtonPrivate *priv = button->priv;
1093 GtkSettings *settings;
1094 GtkIconTheme *theme;
1098 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1100 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
1102 priv->icon_size = MAX (width, height);
1104 priv->icon_size = FALLBACK_ICON_SIZE;
1106 update_label_and_image (button);
1108 gtk_tree_model_get_iter_first (priv->model, &iter);
1110 theme = get_icon_theme (GTK_WIDGET (button));
1118 type = ROW_TYPE_INVALID;
1119 gtk_tree_model_get (priv->model, &iter,
1126 case ROW_TYPE_SPECIAL:
1127 case ROW_TYPE_SHORTCUT:
1128 case ROW_TYPE_BOOKMARK:
1129 case ROW_TYPE_CURRENT_FOLDER:
1131 pixbuf = gtk_file_system_render_icon (priv->fs, data,
1132 GTK_WIDGET (button),
1133 priv->icon_size, NULL);
1135 pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1136 priv->icon_size, 0, NULL);
1138 case ROW_TYPE_VOLUME:
1140 pixbuf = gtk_file_system_volume_render_icon (priv->fs, data,
1141 GTK_WIDGET (button),
1145 pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1146 priv->icon_size, 0, NULL);
1154 width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1156 gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
1157 ICON_COLUMN, pixbuf,
1161 g_object_unref (pixbuf);
1163 while (gtk_tree_model_iter_next (priv->model, &iter));
1165 g_object_set (button->priv->icon_cell,
1171 gtk_file_chooser_button_style_set (GtkWidget *widget,
1172 GtkStyle *old_style)
1174 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set)
1175 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set) (widget,
1178 if (gtk_widget_has_screen (widget))
1179 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1183 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
1184 GdkScreen *old_screen)
1186 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
1187 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed) (widget,
1190 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1194 /* ******************* *
1195 * Utility Functions *
1196 * ******************* */
1199 static GtkIconTheme *
1200 get_icon_theme (GtkWidget *widget)
1202 if (gtk_widget_has_screen (widget))
1203 return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1205 return gtk_icon_theme_get_default ();
1209 get_display_name_for_path (GtkFileSystem *fs,
1210 const GtkFilePath *path)
1212 GtkFilePath *parent_path;
1213 GtkFileFolder *folder;
1219 gtk_file_system_get_parent (fs, path, &parent_path, NULL);
1221 folder = gtk_file_system_get_folder (fs, parent_path ? parent_path : path,
1222 GTK_FILE_INFO_DISPLAY_NAME, NULL);
1228 info = gtk_file_folder_get_info (folder, path, NULL);
1229 g_object_unref (folder);
1233 retval = g_strdup (gtk_file_info_get_display_name (info));
1234 gtk_file_info_free (info);
1239 gtk_file_path_free (parent_path);
1242 retval = g_strdup (_(FALLBACK_DISPLAY_NAME));
1247 /* Shortcuts Model */
1249 model_get_type_position (GtkFileChooserButton *button,
1254 if (row_type == ROW_TYPE_SPECIAL)
1257 retval += button->priv->n_special;
1259 if (row_type == ROW_TYPE_VOLUME)
1262 retval += button->priv->n_volumes;
1264 if (row_type == ROW_TYPE_SHORTCUT)
1267 retval += button->priv->n_shortcuts;
1269 if (row_type == ROW_TYPE_BOOKMARK_SEPARATOR)
1272 retval += button->priv->has_bookmark_separator;
1274 if (row_type == ROW_TYPE_BOOKMARK)
1277 retval += button->priv->n_bookmarks;
1279 if (row_type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR)
1282 retval += button->priv->has_current_folder_separator;
1284 if (row_type == ROW_TYPE_CURRENT_FOLDER)
1287 retval += button->priv->has_current_folder;
1289 if (row_type == ROW_TYPE_OTHER_SEPARATOR)
1292 retval += button->priv->has_other_separator;
1294 if (row_type == ROW_TYPE_OTHER)
1297 g_assert_not_reached ();
1302 model_free_row_data (GtkFileChooserButton *button,
1308 gtk_tree_model_get (button->priv->model, iter,
1315 case ROW_TYPE_SPECIAL:
1316 case ROW_TYPE_SHORTCUT:
1317 case ROW_TYPE_BOOKMARK:
1318 case ROW_TYPE_CURRENT_FOLDER:
1319 gtk_file_path_free (data);
1321 case ROW_TYPE_VOLUME:
1322 gtk_file_system_volume_free (button->priv->fs, data);
1330 model_add_special (GtkFileChooserButton *button)
1332 const gchar *homedir;
1333 const gchar *display_name;
1334 gchar *desktopdir = NULL;
1335 GtkListStore *store;
1341 store = GTK_LIST_STORE (button->priv->model);
1342 pos = model_get_type_position (button, ROW_TYPE_SPECIAL);
1344 homedir = g_get_home_dir ();
1348 path = gtk_file_system_filename_to_path (button->priv->fs, homedir);
1349 display_name = get_display_name_for_path (button->priv->fs, path);
1350 pixbuf = gtk_file_system_render_icon (button->priv->fs, path,
1351 GTK_WIDGET (button),
1352 button->priv->icon_size, NULL);
1353 gtk_list_store_insert (store, &iter, pos);
1355 gtk_list_store_set (store, &iter,
1356 ICON_COLUMN, pixbuf,
1357 DISPLAY_NAME_COLUMN, display_name,
1358 TYPE_COLUMN, ROW_TYPE_SPECIAL,
1363 g_object_unref (pixbuf);
1364 g_free (display_name);
1365 button->priv->n_special++;
1368 desktopdir = g_build_filename (homedir, DESKTOP_DISPLAY_NAME, NULL);
1373 desktopdir = _gtk_file_system_win32_get_desktop ();
1378 path = gtk_file_system_filename_to_path (button->priv->fs, desktopdir);
1379 g_free (desktopdir);
1380 pixbuf = gtk_file_system_render_icon (button->priv->fs, path,
1381 GTK_WIDGET (button),
1382 button->priv->icon_size, NULL);
1383 gtk_list_store_insert (store, &iter, pos);
1385 gtk_list_store_set (store, &iter,
1386 TYPE_COLUMN, ROW_TYPE_SPECIAL,
1387 ICON_COLUMN, pixbuf,
1388 DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
1393 g_object_unref (pixbuf);
1394 button->priv->n_special++;
1399 model_add_volumes (GtkFileChooserButton *button,
1402 GtkListStore *store;
1408 store = GTK_LIST_STORE (button->priv->model);
1409 pos = model_get_type_position (button, ROW_TYPE_VOLUME);
1415 gchar *display_name;
1417 pixbuf = gtk_file_system_volume_render_icon (button->priv->fs,
1419 GTK_WIDGET (button),
1420 button->priv->icon_size,
1422 display_name = gtk_file_system_volume_get_display_name (button->priv->fs,
1425 gtk_list_store_insert (store, &iter, pos);
1426 gtk_list_store_set (store, &iter,
1427 ICON_COLUMN, pixbuf,
1428 DISPLAY_NAME_COLUMN, display_name,
1429 TYPE_COLUMN, ROW_TYPE_VOLUME,
1430 DATA_COLUMN, volumes->data,
1434 g_object_unref (pixbuf);
1435 g_free (display_name);
1437 button->priv->n_volumes++;
1439 volumes = volumes->next;
1445 model_add_bookmarks (GtkFileChooserButton *button,
1448 GtkListStore *store;
1455 store = GTK_LIST_STORE (button->priv->model);
1456 pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR);
1458 if (!button->priv->has_bookmark_separator)
1460 gtk_list_store_insert (store, &iter, pos);
1461 gtk_list_store_set (store, &iter,
1463 DISPLAY_NAME_COLUMN, NULL,
1464 TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
1467 button->priv->has_bookmark_separator = TRUE;
1473 gchar *display_name;
1476 pixbuf = gtk_file_system_render_icon (button->priv->fs, bookmarks->data,
1477 GTK_WIDGET (button),
1478 button->priv->icon_size, NULL);
1479 display_name = get_display_name_for_path (button->priv->fs,
1482 gtk_list_store_insert (store, &iter, pos);
1483 gtk_list_store_set (store, &iter,
1484 ICON_COLUMN, pixbuf,
1485 DISPLAY_NAME_COLUMN, display_name,
1486 TYPE_COLUMN, ROW_TYPE_BOOKMARK,
1487 DATA_COLUMN, gtk_file_path_copy (bookmarks->data),
1490 g_object_unref (pixbuf);
1491 g_free (display_name);
1493 button->priv->n_bookmarks++;
1494 bookmarks = bookmarks->next;
1500 model_update_current_folder (GtkFileChooserButton *button,
1501 const GtkFilePath *path)
1503 GtkListStore *store;
1507 gchar *display_name;
1512 store = GTK_LIST_STORE (button->priv->model);
1514 if (!button->priv->has_current_folder_separator)
1516 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER_SEPARATOR);
1517 gtk_list_store_insert (store, &iter, pos);
1518 gtk_list_store_set (store, &iter,
1520 DISPLAY_NAME_COLUMN, NULL,
1521 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
1524 button->priv->has_current_folder_separator = TRUE;
1527 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
1528 if (!button->priv->has_current_folder)
1530 gtk_list_store_insert (store, &iter, pos);
1531 button->priv->has_current_folder = TRUE;
1535 gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos);
1536 model_free_row_data (button, &iter);
1539 pixbuf = gtk_file_system_render_icon (button->priv->fs, path,
1540 GTK_WIDGET (button),
1541 button->priv->icon_size, NULL);
1542 display_name = get_display_name_for_path (button->priv->fs, path);
1543 gtk_list_store_set (store, &iter,
1544 ICON_COLUMN, pixbuf,
1545 DISPLAY_NAME_COLUMN, display_name,
1546 TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
1547 DATA_COLUMN, gtk_file_path_copy (path),
1550 g_object_unref (pixbuf);
1551 g_free (display_name);
1555 model_add_other (GtkFileChooserButton *button)
1557 GtkListStore *store;
1561 store = GTK_LIST_STORE (button->priv->model);
1562 pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
1564 gtk_list_store_insert (store, &iter, pos);
1565 gtk_list_store_set (store, &iter,
1567 DISPLAY_NAME_COLUMN, NULL,
1568 TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
1571 button->priv->has_other_separator = TRUE;
1574 gtk_list_store_insert (store, &iter, pos);
1575 gtk_list_store_set (store, &iter,
1577 DISPLAY_NAME_COLUMN, _("Other..."),
1578 TYPE_COLUMN, ROW_TYPE_OTHER,
1584 model_remove_rows (GtkFileChooserButton *button,
1588 GtkListStore *store;
1593 store = GTK_LIST_STORE (button->priv->model);
1599 if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
1600 g_assert_not_reached ();
1602 model_free_row_data (button, &iter);
1603 gtk_list_store_remove (store, &iter);
1610 static inline gboolean
1611 test_if_path_is_visible (GtkFileSystem *fs,
1612 const GtkFilePath *path,
1613 gboolean local_only)
1615 GtkFilePath *parent_path;
1616 GtkFileFolder *folder;
1622 if (local_only && !gtk_file_system_path_is_local (fs, path))
1626 gtk_file_system_get_parent (fs, path, &parent_path, NULL);
1628 folder = gtk_file_system_get_folder (fs, parent_path ? parent_path : path,
1629 GTK_FILE_INFO_IS_FOLDER, NULL);
1630 gtk_file_path_free (parent_path);
1634 info = gtk_file_folder_get_info (folder, path, NULL);
1635 g_object_unref (folder);
1642 else if (!gtk_file_info_get_is_folder (info))
1644 gtk_file_info_free (info);
1648 gtk_file_info_free (info);
1654 filter_model_visible_func (GtkTreeModel *model,
1658 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
1659 GtkFileChooserButtonPrivate *priv = button->priv;
1662 gboolean local_only, retval;
1664 type = ROW_TYPE_INVALID;
1666 local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
1668 gtk_tree_model_get (model, iter,
1675 case ROW_TYPE_CURRENT_FOLDER:
1678 case ROW_TYPE_SPECIAL:
1679 case ROW_TYPE_SHORTCUT:
1680 case ROW_TYPE_BOOKMARK:
1681 retval = test_if_path_is_visible (priv->fs, data, local_only);
1683 case ROW_TYPE_VOLUME:
1685 GtkFilePath *base_path;
1687 base_path = gtk_file_system_volume_get_base_path (priv->fs, data);
1690 retval = (!local_only ||
1691 gtk_file_system_path_is_local (priv->fs, base_path));
1692 gtk_file_path_free (base_path);
1708 name_cell_data_func (GtkCellLayout *layout,
1709 GtkCellRenderer *cell,
1710 GtkTreeModel *model,
1717 gtk_tree_model_get (model, iter,
1721 if (type == ROW_TYPE_CURRENT_FOLDER)
1722 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
1724 g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
1728 combo_box_row_separator_func (GtkTreeModel *model,
1732 gchar type = ROW_TYPE_INVALID;
1734 gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
1736 return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
1737 type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
1738 type == ROW_TYPE_OTHER_SEPARATOR);
1742 update_combo_box (GtkFileChooserButton *button)
1744 GtkFileChooserButtonPrivate *priv = button->priv;
1749 gtk_tree_model_get_iter_first (priv->filter_model, &iter);
1751 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
1760 type = ROW_TYPE_INVALID;
1763 gtk_tree_model_get (priv->filter_model, &iter,
1770 case ROW_TYPE_SPECIAL:
1771 case ROW_TYPE_SHORTCUT:
1772 case ROW_TYPE_BOOKMARK:
1773 case ROW_TYPE_CURRENT_FOLDER:
1774 row_found = (paths &&
1776 gtk_file_path_compare (data, paths->data) == 0);
1778 case ROW_TYPE_VOLUME:
1780 GtkFilePath *base_path;
1782 base_path = gtk_file_system_volume_get_base_path (priv->fs, data);
1783 row_found = (paths &&
1785 gtk_file_path_compare (base_path, paths->data) == 0);
1786 gtk_file_path_free (base_path);
1796 g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
1797 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
1799 g_signal_handler_unblock (priv->combo_box,
1800 priv->combo_box_changed_id);
1803 while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
1805 /* If it hasn't been found already, update & select the current-folder row. */
1806 if (!row_found && paths && paths->data)
1808 GtkTreeIter filter_iter;
1811 model_update_current_folder (button, paths->data);
1812 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
1814 pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
1815 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
1817 gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
1818 &filter_iter, &iter);
1820 g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
1821 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
1822 g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
1825 gtk_file_paths_free (paths);
1830 update_label_and_image (GtkFileChooserButton *button)
1832 GtkFileChooserButtonPrivate *priv = button->priv;
1837 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (button->priv->dialog));
1843 GtkFilePath *path, *parent_path;
1844 GtkFileSystemVolume *volume;
1845 GtkFileFolder *folder;
1849 volume = gtk_file_system_get_volume_for_path (priv->fs, path);
1852 GtkFilePath *base_path;
1854 base_path = gtk_file_system_volume_get_base_path (priv->fs, volume);
1855 if (base_path && gtk_file_path_compare (base_path, path) == 0)
1857 label_text = gtk_file_system_volume_get_display_name (priv->fs,
1859 pixbuf = gtk_file_system_volume_render_icon (priv->fs, volume,
1860 GTK_WIDGET (button),
1866 gtk_file_path_free (base_path);
1868 gtk_file_system_volume_free (priv->fs, volume);
1875 pixbuf = gtk_file_system_render_icon (priv->fs, path,
1876 GTK_WIDGET (button),
1877 priv->icon_size, NULL);
1880 gtk_file_system_get_parent (priv->fs, path, &parent_path, NULL);
1882 folder = gtk_file_system_get_folder (priv->fs,
1883 parent_path ? parent_path : path,
1884 GTK_FILE_INFO_DISPLAY_NAME, NULL);
1885 gtk_file_path_free (parent_path);
1891 info = gtk_file_folder_get_info (folder, path, NULL);
1892 g_object_unref (folder);
1896 label_text = g_strdup (gtk_file_info_get_display_name (info));
1897 gtk_file_info_free (info);
1902 gtk_file_paths_free (paths);
1907 gtk_label_set_text (GTK_LABEL (priv->label), label_text);
1908 g_free (label_text);
1911 gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
1914 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
1916 priv->icon_size, 0, NULL);
1918 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
1920 g_object_unref (pixbuf);
1924 /* ************************ *
1925 * Child Object Callbacks *
1926 * ************************ */
1930 fs_volumes_changed_cb (GtkFileSystem *fs,
1933 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
1934 GtkFileChooserButtonPrivate *priv = button->priv;
1937 model_remove_rows (user_data,
1938 model_get_type_position (user_data, ROW_TYPE_VOLUME),
1941 priv->n_volumes = 0;
1943 volumes = gtk_file_system_list_volumes (fs);
1944 model_add_volumes (user_data, volumes);
1945 g_slist_free (volumes);
1947 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
1949 update_label_and_image (user_data);
1950 update_combo_box (user_data);
1954 fs_bookmarks_changed_cb (GtkFileSystem *fs,
1957 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
1958 GtkFileChooserButtonPrivate *priv = button->priv;
1961 bookmarks = gtk_file_system_list_bookmarks (fs);
1964 model_remove_rows (user_data,
1965 model_get_type_position (user_data,
1966 ROW_TYPE_BOOKMARK_SEPARATOR),
1967 (priv->n_bookmarks + priv->has_bookmark_separator));
1968 priv->has_bookmark_separator = FALSE;
1971 model_remove_rows (user_data,
1972 model_get_type_position (user_data, ROW_TYPE_BOOKMARK),
1975 priv->n_bookmarks = 0;
1976 model_add_bookmarks (user_data, bookmarks);
1977 gtk_file_paths_free (bookmarks);
1979 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
1981 update_label_and_image (user_data);
1982 update_combo_box (user_data);
1987 open_dialog (GtkFileChooserButton *button)
1989 GtkFileChooserButtonPrivate *priv = button->priv;
1991 /* Setup the dialog parent to be chooser button's toplevel, and be modal
1993 if (!GTK_WIDGET_VISIBLE (priv->dialog))
1995 GtkWidget *toplevel;
1997 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
1999 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
2001 if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2002 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2003 GTK_WINDOW (toplevel));
2005 gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2006 gtk_window_get_modal (GTK_WINDOW (toplevel)));
2014 g_signal_handler_block (priv->dialog,
2015 priv->dialog_folder_changed_id);
2016 g_signal_handler_block (priv->dialog,
2017 priv->dialog_file_activated_id);
2018 g_signal_handler_block (priv->dialog,
2019 priv->dialog_selection_changed_id);
2020 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
2024 priv->old_path = gtk_file_path_copy (paths->data);
2026 gtk_file_paths_free (paths);
2029 priv->active = TRUE;
2032 gtk_widget_set_sensitive (priv->combo_box, FALSE);
2033 gtk_window_present (GTK_WINDOW (priv->dialog));
2038 combo_box_changed_cb (GtkComboBox *combo_box,
2043 if (gtk_combo_box_get_active_iter (combo_box, &iter))
2045 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2046 GtkFileChooserButtonPrivate *priv = button->priv;
2050 type = ROW_TYPE_INVALID;
2053 gtk_tree_model_get (priv->filter_model, &iter,
2060 case ROW_TYPE_SPECIAL:
2061 case ROW_TYPE_SHORTCUT:
2062 case ROW_TYPE_BOOKMARK:
2063 case ROW_TYPE_CURRENT_FOLDER:
2064 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2066 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
2069 case ROW_TYPE_VOLUME:
2071 GtkFilePath *base_path;
2073 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2074 base_path = gtk_file_system_volume_get_base_path (priv->fs, data);
2077 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog),
2079 gtk_file_path_free (base_path);
2083 case ROW_TYPE_OTHER:
2084 open_dialog (user_data);
2094 button_clicked_cb (GtkButton *real_button,
2097 open_dialog (user_data);
2102 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
2105 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2106 GtkFileChooserButtonPrivate *priv = button->priv;
2108 priv->folder_has_been_set = TRUE;
2110 g_signal_emit_by_name (button, "current-folder-changed");
2114 dialog_file_activated_cb (GtkFileChooser *dialog,
2117 g_signal_emit_by_name (user_data, "file-activated");
2121 dialog_selection_changed_cb (GtkFileChooser *dialog,
2124 update_label_and_image (user_data);
2125 update_combo_box (user_data);
2126 g_signal_emit_by_name (user_data, "selection-changed");
2130 dialog_update_preview_cb (GtkFileChooser *dialog,
2133 g_signal_emit_by_name (user_data, "update-preview");
2137 dialog_notify_cb (GObject *dialog,
2143 iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2144 GTK_TYPE_FILE_CHOOSER);
2145 if (g_object_interface_find_property (iface, pspec->name))
2146 g_object_notify (user_data, pspec->name);
2148 if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
2150 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2151 GtkFileChooserButtonPrivate *priv = button->priv;
2153 if (priv->has_current_folder)
2159 pos = model_get_type_position (user_data,
2160 ROW_TYPE_CURRENT_FOLDER);
2161 gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2164 gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
2166 /* If the path isn't local but we're in local-only mode now, remove
2167 * the custom-folder row */
2169 (!gtk_file_system_path_is_local (priv->fs, data) &&
2170 gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog))))
2173 model_remove_rows (user_data, pos, 2);
2177 gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2178 update_combo_box (user_data);
2183 dialog_delete_event_cb (GtkWidget *dialog,
2187 g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
2193 dialog_response_cb (GtkDialog *dialog,
2197 GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2198 GtkFileChooserButtonPrivate *priv = button->priv;
2200 if (response == GTK_RESPONSE_ACCEPT)
2202 g_signal_emit_by_name (user_data, "current-folder-changed");
2203 g_signal_emit_by_name (user_data, "selection-changed");
2205 else if (priv->old_path)
2207 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)))
2209 case GTK_FILE_CHOOSER_ACTION_OPEN:
2210 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (dialog), priv->old_path,
2213 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
2214 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (dialog),
2215 priv->old_path, NULL);
2218 g_assert_not_reached ();
2223 gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (dialog));
2227 gtk_file_path_free (priv->old_path);
2228 priv->old_path = NULL;
2231 update_label_and_image (user_data);
2232 update_combo_box (user_data);
2236 g_signal_handler_unblock (priv->dialog,
2237 priv->dialog_folder_changed_id);
2238 g_signal_handler_unblock (priv->dialog,
2239 priv->dialog_file_activated_id);
2240 g_signal_handler_unblock (priv->dialog,
2241 priv->dialog_selection_changed_id);
2242 priv->active = FALSE;
2245 gtk_widget_set_sensitive (priv->combo_box, TRUE);
2246 gtk_widget_hide (priv->dialog);
2250 /* ************************************************************************** *
2252 * ************************************************************************** */
2255 * gtk_file_chooser_button_new:
2256 * @title: the title of the browse dialog.
2257 * @action: the open mode for the widget.
2259 * Creates a new file-selecting button widget.
2261 * Returns: a new button widget.
2266 gtk_file_chooser_button_new (const gchar *title,
2267 GtkFileChooserAction action)
2269 g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2270 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2272 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2274 "title", (title ? title : _(DEFAULT_TITLE)),
2279 * gtk_file_chooser_button_new_with_backend:
2280 * @title: the title of the browse dialog.
2281 * @action: the open mode for the widget.
2282 * @backend: the name of the #GtkFileSystem backend to use.
2284 * Creates a new file-selecting button widget using @backend.
2286 * Returns: a new button widget.
2291 gtk_file_chooser_button_new_with_backend (const gchar *title,
2292 GtkFileChooserAction action,
2293 const gchar *backend)
2295 g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2296 action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2298 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2300 "title", (title ? title : _(DEFAULT_TITLE)),
2301 "file-system-backend", backend,
2306 * gtk_file_chooser_button_new_with_dialog:
2307 * @dialog: the #GtkFileChooserDialog widget to use.
2309 * Creates a #GtkFileChooserButton widget which uses @dialog as it's
2310 * file-picking window. Note that @dialog must be a #GtkFileChooserDialog (or
2311 * subclass) and must not have %GTK_DIALOG_DESTROY_WITH_PARENT set.
2313 * Returns: a new button widget.
2318 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
2320 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL);
2322 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2328 * gtk_file_chooser_button_set_title:
2329 * @button: the button widget to modify.
2330 * @title: the new browse dialog title.
2332 * Modifies the @title of the browse dialog used by @button.
2337 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
2340 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2342 gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
2343 g_object_notify (G_OBJECT (button), "title");
2347 * gtk_file_chooser_button_get_title:
2348 * @button: the button widget to examine.
2350 * Retrieves the title of the browse dialog used by @button. The returned value
2351 * should not be modified or freed.
2353 * Returns: a pointer to the browse dialog's title.
2357 G_CONST_RETURN gchar *
2358 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
2360 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
2362 return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
2366 * gtk_file_chooser_button_get_width_chars:
2367 * @button: the button widget to examine.
2369 * Retrieves the width in characters of the @button widget's entry and/or label.
2371 * Returns: an integer width (in characters) that the button will use to size itself.
2376 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
2378 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
2380 return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
2384 * gtk_file_chooser_button_set_width_chars:
2385 * @button: the button widget to examine.
2386 * @n_chars: the new width, in characters.
2388 * Sets the width (in characters) that @button will use to @n_chars.
2393 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
2396 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2398 gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
2399 g_object_notify (G_OBJECT (button), "width-chars");
2403 * gtk_file_chooser_button_set_focus_on_click:
2404 * @button: a #GtkFileChooserButton
2405 * @focus_on_click: whether the button grabs focus when clicked with the mouse
2407 * Sets whether the button will grab focus when it is clicked with the mouse.
2408 * Making mouse clicks not grab focus is useful in places like toolbars where
2409 * you don't want the keyboard focus removed from the main area of the
2415 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
2416 gboolean focus_on_click)
2418 GtkFileChooserButtonPrivate *priv;
2420 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
2422 priv = button->priv;
2424 focus_on_click = focus_on_click != FALSE;
2426 if (priv->focus_on_click != focus_on_click)
2428 priv->focus_on_click = focus_on_click;
2429 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
2430 gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
2432 g_object_notify (G_OBJECT (button), "focus-on-click");
2437 * gtk_file_chooser_button_get_focus_on_click:
2438 * @button: a #GtkFileChooserButton
2440 * Returns whether the button grabs focus when it is clicked with the mouse.
2441 * See gtk_file_chooser_button_set_focus_on_click().
2443 * Return value: %TRUE if the button grabs focus when it is clicked with
2449 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
2451 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
2453 return button->priv->focus_on_click;
2456 #define __GTK_FILE_CHOOSER_BUTTON_C__
2457 #include "gtkaliasdef.c"