1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 
3 /* GTK+: gtkfilechooserbutton.c
4  *
5  * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
6  *
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.
11  *
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.
16  *
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.
21  */
22 
23 #include "config.h"
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 
31 #include <string.h>
32 
33 #include "gtkintl.h"
34 #include "gtkbutton.h"
35 #include "gtkcelllayout.h"
36 #include "gtkcellrenderertext.h"
37 #include "gtkcellrendererpixbuf.h"
38 #include "gtkcombobox.h"
39 #include "gtkdnd.h"
40 #include "gtkicontheme.h"
41 #include "gtkiconfactory.h"
42 #include "gtkimage.h"
43 #include "gtklabel.h"
44 #include "gtkliststore.h"
45 #include "gtkstock.h"
46 #include "gtktreemodelfilter.h"
47 #include "gtkvseparator.h"
48 #include "gtkfilechooserdialog.h"
49 #include "gtkfilechooserprivate.h"
50 #include "gtkfilechooserutils.h"
51 #include "gtkmarshalers.h"
52 
53 #include "gtkfilechooserbutton.h"
54 
55 #include "gtkprivate.h"
56 #include "gtkalias.h"
57 
58 /* **************** *
59  *  Private Macros  *
60  * **************** */
61 
62 #define GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_FILE_CHOOSER_BUTTON, GtkFileChooserButtonPrivate))
63 
64 #define DEFAULT_TITLE		N_("Select a File")
65 #define DESKTOP_DISPLAY_NAME	N_("Desktop")
66 #define FALLBACK_DISPLAY_NAME	N_("(None)") /* this string is used in gtk+/gtk/tests/filechooser.c - change it there if you change it here */
67 #define FALLBACK_ICON_NAME	"stock_unknown"
68 #define FALLBACK_ICON_SIZE	16
69 
70 
71 /* ********************** *
72  *  Private Enumerations  *
73  * ********************** */
74 
75 /* Property IDs */
76 enum
77 {
78   PROP_0,
79 
80   PROP_DIALOG,
81   PROP_FOCUS_ON_CLICK,
82   PROP_TITLE,
83   PROP_WIDTH_CHARS
84 };
85 
86 /* Signals */
87 enum
88 {
89   FILE_SET,
90   LAST_SIGNAL
91 };
92 
93 /* TreeModel Columns */
94 enum
95 {
96   ICON_COLUMN,
97   DISPLAY_NAME_COLUMN,
98   TYPE_COLUMN,
99   DATA_COLUMN,
100   IS_FOLDER_COLUMN,
101   CANCELLABLE_COLUMN,
102   NUM_COLUMNS
103 };
104 
105 /* TreeModel Row Types */
106 typedef enum
107 {
108   ROW_TYPE_SPECIAL,
109   ROW_TYPE_VOLUME,
110   ROW_TYPE_SHORTCUT,
111   ROW_TYPE_BOOKMARK_SEPARATOR,
112   ROW_TYPE_BOOKMARK,
113   ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
114   ROW_TYPE_CURRENT_FOLDER,
115   ROW_TYPE_OTHER_SEPARATOR,
116   ROW_TYPE_OTHER,
117   ROW_TYPE_EMPTY_SELECTION,
118 
119   ROW_TYPE_INVALID = -1
120 }
121 RowType;
122 
123 
124 /* ******************** *
125  *  Private Structures  *
126  * ******************** */
127 
128 struct _GtkFileChooserButtonPrivate
129 {
130   GtkWidget *dialog;
131   GtkWidget *button;
132   GtkWidget *image;
133   GtkWidget *label;
134   GtkWidget *combo_box;
135   GtkCellRenderer *icon_cell;
136   GtkCellRenderer *name_cell;
137 
138   GtkTreeModel *model;
139   GtkTreeModel *filter_model;
140 
141   GtkFileSystem *fs;
142   GFile *selection_while_inactive;
143   GFile *current_folder_while_inactive;
144 
145   gulong combo_box_changed_id;
146   gulong fs_volumes_changed_id;
147   gulong fs_bookmarks_changed_id;
148 
149   GCancellable *dnd_select_folder_cancellable;
150   GCancellable *update_button_cancellable;
151   GSList *change_icon_theme_cancellables;
152 
153   gint icon_size;
154 
155   guint8 n_special;
156   guint8 n_volumes;
157   guint8 n_shortcuts;
158   guint8 n_bookmarks;
159   guint  has_bookmark_separator       : 1;
160   guint  has_current_folder_separator : 1;
161   guint  has_current_folder           : 1;
162   guint  has_other_separator          : 1;
163 
164   /* Used for hiding/showing the dialog when the button is hidden */
165   guint  active                       : 1;
166 
167   guint  focus_on_click               : 1;
168 
169   /* Whether the next async callback from GIO should emit the "selection-changed" signal */
170   guint  is_changing_selection        : 1;
171 };
172 
173 
174 /* ************* *
175  *  DnD Support  *
176  * ************* */
177 
178 enum
179 {
180   TEXT_PLAIN,
181   TEXT_URI_LIST
182 };
183 
184 
185 /* ********************* *
186  *  Function Prototypes  *
187  * ********************* */
188 
189 /* GtkFileChooserIface Functions */
190 static void     gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface);
191 static gboolean gtk_file_chooser_button_set_current_folder (GtkFileChooser    *chooser,
192 							    GFile             *file,
193 							    GError           **error);
194 static GFile *gtk_file_chooser_button_get_current_folder (GtkFileChooser    *chooser);
195 static gboolean gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
196 						     GFile          *file,
197 						     GError        **error);
198 static void gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
199 						   GFile          *file);
200 static void gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser);
201 static GSList *gtk_file_chooser_button_get_files (GtkFileChooser *chooser);
202 static gboolean gtk_file_chooser_button_add_shortcut_folder     (GtkFileChooser      *chooser,
203 								 GFile               *file,
204 								 GError             **error);
205 static gboolean gtk_file_chooser_button_remove_shortcut_folder  (GtkFileChooser      *chooser,
206 								 GFile               *file,
207 								 GError             **error);
208 
209 /* GObject Functions */
210 static GObject *gtk_file_chooser_button_constructor        (GType             type,
211 							    guint             n_params,
212 							    GObjectConstructParam *params);
213 static void     gtk_file_chooser_button_set_property       (GObject          *object,
214 							    guint             param_id,
215 							    const GValue     *value,
216 							    GParamSpec       *pspec);
217 static void     gtk_file_chooser_button_get_property       (GObject          *object,
218 							    guint             param_id,
219 							    GValue           *value,
220 							    GParamSpec       *pspec);
221 static void     gtk_file_chooser_button_finalize           (GObject          *object);
222 
223 /* GtkObject Functions */
224 static void     gtk_file_chooser_button_destroy            (GtkObject        *object);
225 
226 /* GtkWidget Functions */
227 static void     gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
228 							    GdkDragContext   *context,
229 							    gint              x,
230 							    gint              y,
231 							    GtkSelectionData *data,
232 							    guint             type,
233 							    guint             drag_time);
234 static void     gtk_file_chooser_button_show_all           (GtkWidget        *widget);
235 static void     gtk_file_chooser_button_hide_all           (GtkWidget        *widget);
236 static void     gtk_file_chooser_button_show               (GtkWidget        *widget);
237 static void     gtk_file_chooser_button_hide               (GtkWidget        *widget);
238 static void     gtk_file_chooser_button_map                (GtkWidget        *widget);
239 static gboolean gtk_file_chooser_button_mnemonic_activate  (GtkWidget        *widget,
240 							    gboolean          group_cycling);
241 static void     gtk_file_chooser_button_style_set          (GtkWidget        *widget,
242 							    GtkStyle         *old_style);
243 static void     gtk_file_chooser_button_screen_changed     (GtkWidget        *widget,
244 							    GdkScreen        *old_screen);
245 
246 /* Utility Functions */
247 static GtkIconTheme *get_icon_theme               (GtkWidget            *widget);
248 static void          set_info_for_file_at_iter         (GtkFileChooserButton *fs,
249 							GFile                *file,
250 							GtkTreeIter          *iter);
251 
252 static gint          model_get_type_position      (GtkFileChooserButton *button,
253 						   RowType               row_type);
254 static void          model_free_row_data          (GtkFileChooserButton *button,
255 						   GtkTreeIter          *iter);
256 static void          model_add_special            (GtkFileChooserButton *button);
257 static void          model_add_other              (GtkFileChooserButton *button);
258 static void          model_add_empty_selection    (GtkFileChooserButton *button);
259 static void          model_add_volumes            (GtkFileChooserButton *button,
260 						   GSList               *volumes);
261 static void          model_add_bookmarks          (GtkFileChooserButton *button,
262 						   GSList               *bookmarks);
263 static void          model_update_current_folder  (GtkFileChooserButton *button,
264 						   GFile                *file);
265 static void          model_remove_rows            (GtkFileChooserButton *button,
266 						   gint                  pos,
267 						   gint                  n_rows);
268 
269 static gboolean      filter_model_visible_func    (GtkTreeModel         *model,
270 						   GtkTreeIter          *iter,
271 						   gpointer              user_data);
272 
273 static gboolean      combo_box_row_separator_func (GtkTreeModel         *model,
274 						   GtkTreeIter          *iter,
275 						   gpointer              user_data);
276 static void          name_cell_data_func          (GtkCellLayout        *layout,
277 						   GtkCellRenderer      *cell,
278 						   GtkTreeModel         *model,
279 						   GtkTreeIter          *iter,
280 						   gpointer              user_data);
281 static void          open_dialog                  (GtkFileChooserButton *button);
282 static void          update_combo_box             (GtkFileChooserButton *button);
283 static void          update_label_and_image       (GtkFileChooserButton *button);
284 
285 /* Child Object Callbacks */
286 static void     fs_volumes_changed_cb            (GtkFileSystem  *fs,
287 						  gpointer        user_data);
288 static void     fs_bookmarks_changed_cb          (GtkFileSystem  *fs,
289 						  gpointer        user_data);
290 
291 static void     combo_box_changed_cb             (GtkComboBox    *combo_box,
292 						  gpointer        user_data);
293 static void     combo_box_notify_popup_shown_cb  (GObject        *object,
294 						  GParamSpec     *pspec,
295 						  gpointer        user_data);
296 
297 static void     button_clicked_cb                (GtkButton      *real_button,
298 						  gpointer        user_data);
299 
300 static void     dialog_update_preview_cb         (GtkFileChooser *dialog,
301 						  gpointer        user_data);
302 static void     dialog_notify_cb                 (GObject        *dialog,
303 						  GParamSpec     *pspec,
304 						  gpointer        user_data);
305 static gboolean dialog_delete_event_cb           (GtkWidget      *dialog,
306 						  GdkEvent       *event,
307 						  gpointer        user_data);
308 static void     dialog_response_cb               (GtkDialog      *dialog,
309 						  gint            response,
310 						  gpointer        user_data);
311 
312 static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 };
313 
314 /* ******************* *
315  *  GType Declaration  *
316  * ******************* */
317 
318 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \
319     G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
320 })
321 
322 
323 /* ***************** *
324  *  GType Functions  *
325  * ***************** */
326 
327 static void
gtk_file_chooser_button_class_init(GtkFileChooserButtonClass * class)328 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
329 {
330   GObjectClass *gobject_class;
331   GtkObjectClass *gtkobject_class;
332   GtkWidgetClass *widget_class;
333 
334   gobject_class = G_OBJECT_CLASS (class);
335   gtkobject_class = GTK_OBJECT_CLASS (class);
336   widget_class = GTK_WIDGET_CLASS (class);
337 
338   gobject_class->constructor = gtk_file_chooser_button_constructor;
339   gobject_class->set_property = gtk_file_chooser_button_set_property;
340   gobject_class->get_property = gtk_file_chooser_button_get_property;
341   gobject_class->finalize = gtk_file_chooser_button_finalize;
342 
343   gtkobject_class->destroy = gtk_file_chooser_button_destroy;
344 
345   widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
346   widget_class->show_all = gtk_file_chooser_button_show_all;
347   widget_class->hide_all = gtk_file_chooser_button_hide_all;
348   widget_class->show = gtk_file_chooser_button_show;
349   widget_class->hide = gtk_file_chooser_button_hide;
350   widget_class->map = gtk_file_chooser_button_map;
351   widget_class->style_set = gtk_file_chooser_button_style_set;
352   widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
353   widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
354 
355   /**
356    * GtkFileChooserButton::file-set:
357    * @widget: the object which received the signal.
358    *
359    * The ::file-set signal is emitted when the user selects a file.
360    *
361    * Note that this signal is only emitted when the <emphasis>user</emphasis>
362    * changes the file.
363    *
364    * Since: 2.12
365    */
366   file_chooser_button_signals[FILE_SET] =
367     g_signal_new (I_("file-set"),
368 		  G_TYPE_FROM_CLASS (gobject_class),
369 		  G_SIGNAL_RUN_FIRST,
370 		  G_STRUCT_OFFSET (GtkFileChooserButtonClass, file_set),
371 		  NULL, NULL,
372 		  _gtk_marshal_VOID__VOID,
373 		  G_TYPE_NONE, 0);
374 
375   /**
376    * GtkFileChooserButton:dialog:
377    *
378    * Instance of the #GtkFileChooserDialog associated with the button.
379    *
380    * Since: 2.6
381    */
382   g_object_class_install_property (gobject_class, PROP_DIALOG,
383 				   g_param_spec_object ("dialog",
384 							P_("Dialog"),
385 							P_("The file chooser dialog to use."),
386 							GTK_TYPE_FILE_CHOOSER,
387 							(GTK_PARAM_WRITABLE |
388 							 G_PARAM_CONSTRUCT_ONLY)));
389 
390   /**
391    * GtkFileChooserButton:focus-on-click:
392    *
393    * Whether the #GtkFileChooserButton button grabs focus when it is clicked
394    * with the mouse.
395    *
396    * Since: 2.10
397    */
398   g_object_class_install_property (gobject_class,
399                                    PROP_FOCUS_ON_CLICK,
400                                    g_param_spec_boolean ("focus-on-click",
401 							 P_("Focus on click"),
402 							 P_("Whether the button grabs focus when it is clicked with the mouse"),
403 							 TRUE,
404 							 GTK_PARAM_READWRITE));
405 
406   /**
407    * GtkFileChooserButton:title:
408    *
409    * Title to put on the #GtkFileChooserDialog associated with the button.
410    *
411    * Since: 2.6
412    */
413   g_object_class_install_property (gobject_class, PROP_TITLE,
414 				   g_param_spec_string ("title",
415 							P_("Title"),
416 							P_("The title of the file chooser dialog."),
417 							_(DEFAULT_TITLE),
418 							GTK_PARAM_READWRITE));
419 
420   /**
421    * GtkFileChooserButton:width-chars:
422    *
423    * The width of the entry and label inside the button, in characters.
424    *
425    * Since: 2.6
426    */
427   g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
428 				   g_param_spec_int ("width-chars",
429 						     P_("Width In Characters"),
430 						     P_("The desired width of the button widget, in characters."),
431 						     -1, G_MAXINT, -1,
432 						     GTK_PARAM_READWRITE));
433 
434   _gtk_file_chooser_install_properties (gobject_class);
435 
436   g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
437 }
438 
439 static void
gtk_file_chooser_button_init(GtkFileChooserButton * button)440 gtk_file_chooser_button_init (GtkFileChooserButton *button)
441 {
442   GtkFileChooserButtonPrivate *priv;
443   GtkWidget *box, *image, *sep;
444   GtkTargetList *target_list;
445 
446   priv = button->priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
447 
448   priv->icon_size = FALLBACK_ICON_SIZE;
449   priv->focus_on_click = TRUE;
450 
451   gtk_widget_push_composite_child ();
452 
453   /* Button */
454   priv->button = gtk_button_new ();
455   g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb),
456 		    button);
457   gtk_container_add (GTK_CONTAINER (button), priv->button);
458   gtk_widget_show (priv->button);
459 
460   box = gtk_hbox_new (FALSE, 4);
461   gtk_container_add (GTK_CONTAINER (priv->button), box);
462   gtk_widget_show (box);
463 
464   priv->image = gtk_image_new ();
465   gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
466   gtk_widget_show (priv->image);
467 
468   priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
469   gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
470   gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
471   gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0);
472   //gtk_container_add (GTK_CONTAINER (box), priv->label);
473   gtk_widget_show (priv->label);
474 
475   sep = gtk_vseparator_new ();
476   gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
477   gtk_widget_show (sep);
478 
479   image = gtk_image_new_from_stock (GTK_STOCK_OPEN,
480 				    GTK_ICON_SIZE_MENU);
481   gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
482   gtk_widget_show (image);
483 
484   /* Combo Box */
485   /* Keep in sync with columns enum, line 88 */
486   priv->model =
487     GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
488 					GDK_TYPE_PIXBUF, /* ICON_COLUMN */
489 					G_TYPE_STRING,	 /* DISPLAY_NAME_COLUMN */
490 					G_TYPE_CHAR,	 /* TYPE_COLUMN */
491 					G_TYPE_POINTER	 /* DATA_COLUMN (Volume || Path) */,
492 					G_TYPE_BOOLEAN   /* IS_FOLDER_COLUMN */,
493 					G_TYPE_POINTER	 /* CANCELLABLE_COLUMN */));
494 
495   priv->combo_box = gtk_combo_box_new ();
496   priv->combo_box_changed_id = g_signal_connect (priv->combo_box, "changed",
497 						 G_CALLBACK (combo_box_changed_cb), button);
498 
499   g_signal_connect (priv->combo_box, "notify::popup-shown",
500 		    G_CALLBACK (combo_box_notify_popup_shown_cb), button);
501 
502   gtk_container_add (GTK_CONTAINER (button), priv->combo_box);
503 
504   priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
505   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
506 			      priv->icon_cell, FALSE);
507   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
508 				 priv->icon_cell, "pixbuf", ICON_COLUMN);
509 
510   priv->name_cell = gtk_cell_renderer_text_new ();
511   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
512 			      priv->name_cell, TRUE);
513   gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
514 				 priv->name_cell, "text", DISPLAY_NAME_COLUMN);
515   gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box),
516 				      priv->name_cell, name_cell_data_func,
517 				      NULL, NULL);
518 
519   gtk_widget_pop_composite_child ();
520 
521   /* DnD */
522   gtk_drag_dest_set (GTK_WIDGET (button),
523                      (GTK_DEST_DEFAULT_ALL),
524 		     NULL, 0,
525 		     GDK_ACTION_COPY);
526   target_list = gtk_target_list_new (NULL, 0);
527   gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
528   gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
529   gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
530   gtk_target_list_unref (target_list);
531 }
532 
533 
534 /* ******************************* *
535  *  GtkFileChooserIface Functions  *
536  * ******************************* */
537 static void
gtk_file_chooser_button_file_chooser_iface_init(GtkFileChooserIface * iface)538 gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
539 {
540   _gtk_file_chooser_delegate_iface_init (iface);
541 
542   iface->set_current_folder = gtk_file_chooser_button_set_current_folder;
543   iface->get_current_folder = gtk_file_chooser_button_get_current_folder;
544   iface->select_file = gtk_file_chooser_button_select_file;
545   iface->unselect_file = gtk_file_chooser_button_unselect_file;
546   iface->unselect_all = gtk_file_chooser_button_unselect_all;
547   iface->get_files = gtk_file_chooser_button_get_files;
548   iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder;
549   iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
550 }
551 
552 static void
emit_selection_changed_if_changing_selection(GtkFileChooserButton * button)553 emit_selection_changed_if_changing_selection (GtkFileChooserButton *button)
554 {
555   GtkFileChooserButtonPrivate *priv = button->priv;
556 
557   if (priv->is_changing_selection)
558     {
559       priv->is_changing_selection = FALSE;
560       g_signal_emit_by_name (button, "selection-changed");
561     }
562 }
563 
564 static gboolean
gtk_file_chooser_button_set_current_folder(GtkFileChooser * chooser,GFile * file,GError ** error)565 gtk_file_chooser_button_set_current_folder (GtkFileChooser    *chooser,
566 					    GFile             *file,
567 					    GError           **error)
568 {
569   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
570   GtkFileChooserButtonPrivate *priv = button->priv;
571 
572   if (priv->current_folder_while_inactive)
573     g_object_unref (priv->current_folder_while_inactive);
574 
575   priv->current_folder_while_inactive = g_object_ref (file);
576 
577   update_combo_box (button);
578 
579   g_signal_emit_by_name (button, "current-folder-changed");
580 
581   if (priv->active)
582     gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL);
583 
584   return TRUE;
585 }
586 
587 static GFile *
gtk_file_chooser_button_get_current_folder(GtkFileChooser * chooser)588 gtk_file_chooser_button_get_current_folder (GtkFileChooser *chooser)
589 {
590   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
591   GtkFileChooserButtonPrivate *priv = button->priv;
592 
593   if (priv->current_folder_while_inactive)
594     return g_object_ref (priv->current_folder_while_inactive);
595   else
596     return NULL;
597 }
598 
599 static gboolean
gtk_file_chooser_button_select_file(GtkFileChooser * chooser,GFile * file,GError ** error)600 gtk_file_chooser_button_select_file (GtkFileChooser *chooser,
601 				     GFile          *file,
602 				     GError        **error)
603 {
604   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
605   GtkFileChooserButtonPrivate *priv = button->priv;
606 
607   if (priv->selection_while_inactive)
608     g_object_unref (priv->selection_while_inactive);
609 
610   priv->selection_while_inactive = g_object_ref (file);
611 
612   priv->is_changing_selection = TRUE;
613 
614   update_label_and_image (button);
615   update_combo_box (button);
616 
617   if (priv->active)
618     gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file, NULL);
619 
620   return TRUE;
621 }
622 
623 static void
unselect_current_file(GtkFileChooserButton * button)624 unselect_current_file (GtkFileChooserButton *button)
625 {
626   GtkFileChooserButtonPrivate *priv = button->priv;
627 
628   if (priv->selection_while_inactive)
629     {
630       g_object_unref (priv->selection_while_inactive);
631       priv->selection_while_inactive = NULL;
632     }
633 
634   priv->is_changing_selection = TRUE;
635 
636   update_label_and_image (button);
637   update_combo_box (button);
638 }
639 
640 static void
gtk_file_chooser_button_unselect_file(GtkFileChooser * chooser,GFile * file)641 gtk_file_chooser_button_unselect_file (GtkFileChooser *chooser,
642 				       GFile          *file)
643 {
644   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
645   GtkFileChooserButtonPrivate *priv = button->priv;
646 
647   if (g_file_equal (priv->selection_while_inactive, file))
648     unselect_current_file (button);
649 
650   if (priv->active)
651     gtk_file_chooser_unselect_file (GTK_FILE_CHOOSER (priv->dialog), file);
652 }
653 
654 static void
gtk_file_chooser_button_unselect_all(GtkFileChooser * chooser)655 gtk_file_chooser_button_unselect_all (GtkFileChooser *chooser)
656 {
657   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
658   GtkFileChooserButtonPrivate *priv = button->priv;
659 
660   unselect_current_file (button);
661 
662   if (priv->active)
663     gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
664 }
665 
666 static GFile *
get_selected_file(GtkFileChooserButton * button)667 get_selected_file (GtkFileChooserButton *button)
668 {
669   GtkFileChooserButtonPrivate *priv = button->priv;
670   GFile *retval;
671 
672   retval = NULL;
673 
674   if (priv->selection_while_inactive)
675     retval = priv->selection_while_inactive;
676   else if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)) == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
677     {
678       /* If there is no "real" selection in SELECT_FOLDER mode, then we'll just return
679        * the current folder, since that is what GtkFileChooserDefault would do.
680        */
681       if (priv->current_folder_while_inactive)
682 	retval = priv->current_folder_while_inactive;
683     }
684 
685   if (retval)
686     return g_object_ref (retval);
687   else
688     return NULL;
689 }
690 
691 static GSList *
gtk_file_chooser_button_get_files(GtkFileChooser * chooser)692 gtk_file_chooser_button_get_files (GtkFileChooser *chooser)
693 {
694   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
695   GFile *file;
696 
697   file = get_selected_file (button);
698   if (file)
699     return g_slist_prepend (NULL, file);
700   else
701     return NULL;
702 }
703 
704 static gboolean
gtk_file_chooser_button_add_shortcut_folder(GtkFileChooser * chooser,GFile * file,GError ** error)705 gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser  *chooser,
706 					     GFile           *file,
707 					     GError         **error)
708 {
709   GtkFileChooser *delegate;
710   gboolean retval;
711 
712   delegate = g_object_get_qdata (G_OBJECT (chooser),
713 				 GTK_FILE_CHOOSER_DELEGATE_QUARK);
714   retval = _gtk_file_chooser_add_shortcut_folder (delegate, file, error);
715 
716   if (retval)
717     {
718       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
719       GtkFileChooserButtonPrivate *priv = button->priv;
720       GtkTreeIter iter;
721       gint pos;
722 
723       pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
724       pos += priv->n_shortcuts;
725 
726       gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
727       gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
728 			  ICON_COLUMN, NULL,
729 			  DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
730 			  TYPE_COLUMN, ROW_TYPE_SHORTCUT,
731 			  DATA_COLUMN, g_object_ref (file),
732 			  IS_FOLDER_COLUMN, FALSE,
733 			  -1);
734       set_info_for_file_at_iter (button, file, &iter);
735       priv->n_shortcuts++;
736 
737       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
738     }
739 
740   return retval;
741 }
742 
743 static gboolean
gtk_file_chooser_button_remove_shortcut_folder(GtkFileChooser * chooser,GFile * file,GError ** error)744 gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser  *chooser,
745 						GFile           *file,
746 						GError         **error)
747 {
748   GtkFileChooser *delegate;
749   gboolean retval;
750 
751   delegate = g_object_get_qdata (G_OBJECT (chooser),
752 				 GTK_FILE_CHOOSER_DELEGATE_QUARK);
753 
754   retval = _gtk_file_chooser_remove_shortcut_folder (delegate, file, error);
755 
756   if (retval)
757     {
758       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
759       GtkFileChooserButtonPrivate *priv = button->priv;
760       GtkTreeIter iter;
761       gint pos;
762       gchar type;
763 
764       pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
765       gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
766 
767       do
768 	{
769 	  gpointer data;
770 
771 	  gtk_tree_model_get (priv->model, &iter,
772 			      TYPE_COLUMN, &type,
773 			      DATA_COLUMN, &data,
774 			      -1);
775 
776 	  if (type == ROW_TYPE_SHORTCUT &&
777 	      data && g_file_equal (data, file))
778 	    {
779 	      model_free_row_data (GTK_FILE_CHOOSER_BUTTON (chooser), &iter);
780 	      gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
781 	      priv->n_shortcuts--;
782 	      gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
783 	      update_combo_box (GTK_FILE_CHOOSER_BUTTON (chooser));
784 	      break;
785 	    }
786 	}
787       while (type == ROW_TYPE_SHORTCUT &&
788 	     gtk_tree_model_iter_next (priv->model, &iter));
789     }
790 
791   return retval;
792 }
793 
794 
795 /* ******************* *
796  *  GObject Functions  *
797  * ******************* */
798 
799 static GObject *
gtk_file_chooser_button_constructor(GType type,guint n_params,GObjectConstructParam * params)800 gtk_file_chooser_button_constructor (GType                  type,
801 				     guint                  n_params,
802 				     GObjectConstructParam *params)
803 {
804   GObject *object;
805   GtkFileChooserButton *button;
806   GtkFileChooserButtonPrivate *priv;
807   GSList *list;
808 
809   object = G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor (type,
810 									       n_params,
811 									       params);
812   button = GTK_FILE_CHOOSER_BUTTON (object);
813   priv = button->priv;
814 
815   if (!priv->dialog)
816     {
817       priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
818 						  GTK_FILE_CHOOSER_ACTION_OPEN,
819 						  GTK_STOCK_CANCEL,
820 						  GTK_RESPONSE_CANCEL,
821 						  GTK_STOCK_OPEN,
822 						  GTK_RESPONSE_ACCEPT,
823 						  NULL);
824 
825       gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
826 				       GTK_RESPONSE_ACCEPT);
827       gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
828 					       GTK_RESPONSE_ACCEPT,
829 					       GTK_RESPONSE_CANCEL,
830 					       -1);
831 
832       gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
833     }
834   else if (!gtk_window_get_title (GTK_WINDOW (priv->dialog)))
835     {
836       gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
837     }
838 
839   g_signal_connect (priv->dialog, "delete-event",
840 		    G_CALLBACK (dialog_delete_event_cb), object);
841   g_signal_connect (priv->dialog, "response",
842 		    G_CALLBACK (dialog_response_cb), object);
843 
844   /* This is used, instead of the standard delegate, to ensure that signals are only
845    * delegated when the OK button is pressed. */
846   g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
847 
848   g_signal_connect (priv->dialog, "update-preview",
849 		    G_CALLBACK (dialog_update_preview_cb), object);
850   g_signal_connect (priv->dialog, "notify",
851 		    G_CALLBACK (dialog_notify_cb), object);
852   g_object_add_weak_pointer (G_OBJECT (priv->dialog),
853 			     (gpointer) (&priv->dialog));
854 
855   priv->fs =
856     g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));
857 
858   model_add_special (button);
859 
860   list = _gtk_file_system_list_volumes (priv->fs);
861   model_add_volumes (button, list);
862   g_slist_free (list);
863 
864   list = _gtk_file_system_list_bookmarks (priv->fs);
865   model_add_bookmarks (button, list);
866   g_slist_foreach (list, (GFunc) g_object_unref, NULL);
867   g_slist_free (list);
868 
869   model_add_other (button);
870 
871   model_add_empty_selection (button);
872 
873   priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
874   gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
875 					  filter_model_visible_func,
876 					  object, NULL);
877 
878   gtk_combo_box_set_model (GTK_COMBO_BOX (priv->combo_box), priv->filter_model);
879   gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->combo_box),
880 					combo_box_row_separator_func,
881 					NULL, NULL);
882 
883   /* set up the action for a user-provided dialog, this also updates
884    * the label, image and combobox
885    */
886   g_object_set (object,
887 		"action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)),
888 		NULL);
889 
890   priv->fs_volumes_changed_id =
891     g_signal_connect (priv->fs, "volumes-changed",
892 		      G_CALLBACK (fs_volumes_changed_cb), object);
893   priv->fs_bookmarks_changed_id =
894     g_signal_connect (priv->fs, "bookmarks-changed",
895 		      G_CALLBACK (fs_bookmarks_changed_cb), object);
896 
897   update_label_and_image (button);
898   update_combo_box (button);
899 
900   return object;
901 }
902 
903 static void
gtk_file_chooser_button_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)904 gtk_file_chooser_button_set_property (GObject      *object,
905 				      guint         param_id,
906 				      const GValue *value,
907 				      GParamSpec   *pspec)
908 {
909   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
910   GtkFileChooserButtonPrivate *priv = button->priv;
911 
912   switch (param_id)
913     {
914     case PROP_DIALOG:
915       /* Construct-only */
916       priv->dialog = g_value_get_object (value);
917       break;
918     case PROP_FOCUS_ON_CLICK:
919       gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
920       break;
921     case PROP_WIDTH_CHARS:
922       gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
923 					       g_value_get_int (value));
924       break;
925     case GTK_FILE_CHOOSER_PROP_ACTION:
926       switch (g_value_get_enum (value))
927 	{
928 	case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
929 	case GTK_FILE_CHOOSER_ACTION_SAVE:
930 	  {
931 	    GEnumClass *eclass;
932 	    GEnumValue *eval;
933 
934 	    eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
935 	    eval = g_enum_get_value (eclass, g_value_get_enum (value));
936 	    g_warning ("%s: Choosers of type `%s' do not support `%s'.",
937 		       G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
938 
939 	    g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
940 	  }
941 	  break;
942 	}
943 
944       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
945       update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
946       update_combo_box (GTK_FILE_CHOOSER_BUTTON (object));
947 
948       switch (g_value_get_enum (value))
949 	{
950 	case GTK_FILE_CHOOSER_ACTION_OPEN:
951 	  gtk_widget_hide (priv->combo_box);
952 	  gtk_widget_show (priv->button);
953 	  break;
954 	case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
955 	  gtk_widget_hide (priv->button);
956 	  gtk_widget_show (priv->combo_box);
957 	  break;
958 	default:
959 	  g_assert_not_reached ();
960 	  break;
961 	}
962       break;
963 
964     case PROP_TITLE:
965     case GTK_FILE_CHOOSER_PROP_FILTER:
966     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
967     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
968     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
969     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
970     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
971     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
972     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
973       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
974       break;
975 
976     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
977       g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
978       fs_volumes_changed_cb (priv->fs, button);
979       fs_bookmarks_changed_cb (priv->fs, button);
980       break;
981 
982     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
983       /* Ignore property */
984       break;
985 
986     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
987       g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
988 		 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
989       break;
990     default:
991       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
992       break;
993     }
994 }
995 
996 static void
gtk_file_chooser_button_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)997 gtk_file_chooser_button_get_property (GObject    *object,
998 				      guint       param_id,
999 				      GValue     *value,
1000 				      GParamSpec *pspec)
1001 {
1002   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1003   GtkFileChooserButtonPrivate *priv = button->priv;
1004 
1005   switch (param_id)
1006     {
1007     case PROP_WIDTH_CHARS:
1008       g_value_set_int (value,
1009 		       gtk_label_get_width_chars (GTK_LABEL (priv->label)));
1010       break;
1011     case PROP_FOCUS_ON_CLICK:
1012       g_value_set_boolean (value,
1013                            gtk_file_chooser_button_get_focus_on_click (button));
1014       break;
1015 
1016     case PROP_TITLE:
1017     case GTK_FILE_CHOOSER_PROP_ACTION:
1018     case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
1019     case GTK_FILE_CHOOSER_PROP_FILTER:
1020     case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
1021     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
1022     case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
1023     case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
1024     case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
1025     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
1026     case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
1027     case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
1028     case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
1029       g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
1030       break;
1031 
1032     default:
1033       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1034       break;
1035     }
1036 }
1037 
1038 static void
gtk_file_chooser_button_finalize(GObject * object)1039 gtk_file_chooser_button_finalize (GObject *object)
1040 {
1041   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1042   GtkFileChooserButtonPrivate *priv = button->priv;
1043 
1044   if (priv->selection_while_inactive)
1045     g_object_unref (priv->selection_while_inactive);
1046 
1047   if (priv->current_folder_while_inactive)
1048     g_object_unref (priv->current_folder_while_inactive);
1049 
1050   G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object);
1051 }
1052 
1053 /* ********************* *
1054  *  GtkObject Functions  *
1055  * ********************* */
1056 
1057 static void
gtk_file_chooser_button_destroy(GtkObject * object)1058 gtk_file_chooser_button_destroy (GtkObject *object)
1059 {
1060   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
1061   GtkFileChooserButtonPrivate *priv = button->priv;
1062   GtkTreeIter iter;
1063   GSList *l;
1064 
1065   if (priv->dialog != NULL)
1066     {
1067       gtk_widget_destroy (priv->dialog);
1068       priv->dialog = NULL;
1069     }
1070 
1071   if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) do
1072     {
1073       model_free_row_data (button, &iter);
1074     }
1075   while (gtk_tree_model_iter_next (priv->model, &iter));
1076 
1077   if (priv->dnd_select_folder_cancellable)
1078     {
1079       g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1080       priv->dnd_select_folder_cancellable = NULL;
1081     }
1082 
1083   if (priv->update_button_cancellable)
1084     {
1085       g_cancellable_cancel (priv->update_button_cancellable);
1086       priv->update_button_cancellable = NULL;
1087     }
1088 
1089   if (priv->change_icon_theme_cancellables)
1090     {
1091       for (l = priv->change_icon_theme_cancellables; l; l = l->next)
1092         {
1093 	  GCancellable *cancellable = G_CANCELLABLE (l->data);
1094 	  g_cancellable_cancel (cancellable);
1095         }
1096       g_slist_free (priv->change_icon_theme_cancellables);
1097       priv->change_icon_theme_cancellables = NULL;
1098     }
1099 
1100   if (priv->model)
1101     {
1102       g_object_unref (priv->model);
1103       priv->model = NULL;
1104     }
1105 
1106   if (priv->filter_model)
1107     {
1108       g_object_unref (priv->filter_model);
1109       priv->filter_model = NULL;
1110     }
1111 
1112   if (priv->fs)
1113     {
1114       g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
1115       g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
1116       g_object_unref (priv->fs);
1117       priv->fs = NULL;
1118     }
1119 
1120   GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy (object);
1121 }
1122 
1123 
1124 /* ********************* *
1125  *  GtkWidget Functions  *
1126  * ********************* */
1127 
1128 struct DndSelectFolderData
1129 {
1130   GtkFileSystem *file_system;
1131   GtkFileChooserButton *button;
1132   GtkFileChooserAction action;
1133   GFile *file;
1134   gchar **uris;
1135   guint i;
1136   gboolean selected;
1137 };
1138 
1139 static void
dnd_select_folder_get_info_cb(GCancellable * cancellable,GFileInfo * info,const GError * error,gpointer user_data)1140 dnd_select_folder_get_info_cb (GCancellable *cancellable,
1141 			       GFileInfo    *info,
1142 			       const GError *error,
1143 			       gpointer      user_data)
1144 {
1145   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1146   struct DndSelectFolderData *data = user_data;
1147 
1148   if (cancellable != data->button->priv->dnd_select_folder_cancellable)
1149     {
1150       g_object_unref (data->button);
1151       g_object_unref (data->file);
1152       g_strfreev (data->uris);
1153       g_free (data);
1154 
1155       g_object_unref (cancellable);
1156       return;
1157     }
1158 
1159   data->button->priv->dnd_select_folder_cancellable = NULL;
1160 
1161   if (!cancelled && !error && info != NULL)
1162     {
1163       gboolean is_folder;
1164 
1165       is_folder = _gtk_file_info_consider_as_directory (info);
1166 
1167       data->selected =
1168 	(((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
1169 	  (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
1170 	 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button->priv->dialog),
1171 				       data->file, NULL));
1172     }
1173   else
1174     data->selected = FALSE;
1175 
1176   if (data->selected || data->uris[++data->i] == NULL)
1177     {
1178       g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);
1179 
1180       g_object_unref (data->button);
1181       g_object_unref (data->file);
1182       g_strfreev (data->uris);
1183       g_free (data);
1184 
1185       g_object_unref (cancellable);
1186       return;
1187     }
1188 
1189   if (data->file)
1190     g_object_unref (data->file);
1191 
1192   data->file = g_file_new_for_uri (data->uris[data->i]);
1193 
1194   data->button->priv->dnd_select_folder_cancellable =
1195     _gtk_file_system_get_info (data->file_system, data->file,
1196 			       "standard::type",
1197 			       dnd_select_folder_get_info_cb, user_data);
1198 
1199   g_object_unref (cancellable);
1200 }
1201 
1202 static void
gtk_file_chooser_button_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint type,guint drag_time)1203 gtk_file_chooser_button_drag_data_received (GtkWidget	     *widget,
1204 					    GdkDragContext   *context,
1205 					    gint	      x,
1206 					    gint	      y,
1207 					    GtkSelectionData *data,
1208 					    guint	      type,
1209 					    guint	      drag_time)
1210 {
1211   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1212   GtkFileChooserButtonPrivate *priv = button->priv;
1213   GFile *file;
1214   gchar *text;
1215 
1216   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
1217     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received (widget,
1218 										 context,
1219 										 x, y,
1220 										 data, type,
1221 										 drag_time);
1222 
1223   if (widget == NULL || context == NULL || data == NULL || gtk_selection_data_get_length (data) < 0)
1224     return;
1225 
1226   switch (type)
1227     {
1228     case TEXT_URI_LIST:
1229       {
1230 	gchar **uris;
1231 	struct DndSelectFolderData *info;
1232 
1233 	uris = gtk_selection_data_get_uris (data);
1234 
1235 	if (uris == NULL)
1236 	  break;
1237 
1238 	info = g_new0 (struct DndSelectFolderData, 1);
1239 	info->button = g_object_ref (button);
1240 	info->i = 0;
1241 	info->uris = uris;
1242 	info->selected = FALSE;
1243 	info->file_system = priv->fs;
1244 	g_object_get (priv->dialog, "action", &info->action, NULL);
1245 
1246 	info->file = g_file_new_for_uri (info->uris[info->i]);
1247 
1248 	if (priv->dnd_select_folder_cancellable)
1249 	  g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1250 
1251 	priv->dnd_select_folder_cancellable =
1252 	  _gtk_file_system_get_info (priv->fs, info->file,
1253 				     "standard::type",
1254 				     dnd_select_folder_get_info_cb, info);
1255       }
1256       break;
1257 
1258     case TEXT_PLAIN:
1259       text = (char*) gtk_selection_data_get_text (data);
1260       file = g_file_new_for_uri (text);
1261       gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file,
1262 				    NULL);
1263       g_object_unref (file);
1264       g_free (text);
1265       g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
1266       break;
1267 
1268     default:
1269       break;
1270     }
1271 
1272   gtk_drag_finish (context, TRUE, FALSE, drag_time);
1273 }
1274 
1275 static void
gtk_file_chooser_button_show_all(GtkWidget * widget)1276 gtk_file_chooser_button_show_all (GtkWidget *widget)
1277 {
1278   gtk_widget_show (widget);
1279 }
1280 
1281 static void
gtk_file_chooser_button_hide_all(GtkWidget * widget)1282 gtk_file_chooser_button_hide_all (GtkWidget *widget)
1283 {
1284   gtk_widget_hide (widget);
1285 }
1286 
1287 static void
gtk_file_chooser_button_show(GtkWidget * widget)1288 gtk_file_chooser_button_show (GtkWidget *widget)
1289 {
1290   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1291   GtkFileChooserButtonPrivate *priv = button->priv;
1292 
1293   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
1294     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show (widget);
1295 
1296   if (priv->active)
1297     open_dialog (GTK_FILE_CHOOSER_BUTTON (widget));
1298 }
1299 
1300 static void
gtk_file_chooser_button_hide(GtkWidget * widget)1301 gtk_file_chooser_button_hide (GtkWidget *widget)
1302 {
1303   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1304   GtkFileChooserButtonPrivate *priv = button->priv;
1305 
1306   gtk_widget_hide (priv->dialog);
1307 
1308   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
1309     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide (widget);
1310 }
1311 
1312 static void
gtk_file_chooser_button_map(GtkWidget * widget)1313 gtk_file_chooser_button_map (GtkWidget *widget)
1314 {
1315   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget);
1316 }
1317 
1318 static gboolean
gtk_file_chooser_button_mnemonic_activate(GtkWidget * widget,gboolean group_cycling)1319 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
1320 					   gboolean   group_cycling)
1321 {
1322   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
1323   GtkFileChooserButtonPrivate *priv = button->priv;
1324 
1325   switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
1326     {
1327     case GTK_FILE_CHOOSER_ACTION_OPEN:
1328       gtk_widget_grab_focus (priv->button);
1329       break;
1330     case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1331       return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
1332       break;
1333     default:
1334       g_assert_not_reached ();
1335       break;
1336     }
1337 
1338   return TRUE;
1339 }
1340 
1341 /* Changes the icons wherever it is needed */
1342 struct ChangeIconThemeData
1343 {
1344   GtkFileChooserButton *button;
1345   GtkTreeRowReference *row_ref;
1346 };
1347 
1348 static void
change_icon_theme_get_info_cb(GCancellable * cancellable,GFileInfo * info,const GError * error,gpointer user_data)1349 change_icon_theme_get_info_cb (GCancellable *cancellable,
1350 			       GFileInfo    *info,
1351 			       const GError *error,
1352 			       gpointer      user_data)
1353 {
1354   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1355   GdkPixbuf *pixbuf;
1356   struct ChangeIconThemeData *data = user_data;
1357 
1358   if (!g_slist_find (data->button->priv->change_icon_theme_cancellables, cancellable))
1359     goto out;
1360 
1361   data->button->priv->change_icon_theme_cancellables =
1362     g_slist_remove (data->button->priv->change_icon_theme_cancellables, cancellable);
1363 
1364   if (cancelled || error)
1365     goto out;
1366 
1367   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1368 
1369   if (pixbuf)
1370     {
1371       gint width = 0;
1372       GtkTreeIter iter;
1373       GtkTreePath *path;
1374 
1375       width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1376 
1377       path = gtk_tree_row_reference_get_path (data->row_ref);
1378       if (path)
1379         {
1380           gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1381           gtk_tree_path_free (path);
1382 
1383           gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1384 	  		      ICON_COLUMN, pixbuf,
1385 			      -1);
1386 
1387           g_object_set (data->button->priv->icon_cell,
1388 		        "width", width,
1389 		        NULL);
1390         }
1391       g_object_unref (pixbuf);
1392     }
1393 
1394 out:
1395   g_object_unref (data->button);
1396   gtk_tree_row_reference_free (data->row_ref);
1397   g_free (data);
1398 
1399   g_object_unref (cancellable);
1400 }
1401 
1402 static void
change_icon_theme(GtkFileChooserButton * button)1403 change_icon_theme (GtkFileChooserButton *button)
1404 {
1405   GtkFileChooserButtonPrivate *priv = button->priv;
1406   GtkSettings *settings;
1407   GtkIconTheme *theme;
1408   GtkTreeIter iter;
1409   GSList *l;
1410   gint width = 0, height = 0;
1411 
1412   for (l = button->priv->change_icon_theme_cancellables; l; l = l->next)
1413     {
1414       GCancellable *cancellable = G_CANCELLABLE (l->data);
1415       g_cancellable_cancel (cancellable);
1416     }
1417   g_slist_free (button->priv->change_icon_theme_cancellables);
1418   button->priv->change_icon_theme_cancellables = NULL;
1419 
1420   settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
1421 
1422   if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_MENU,
1423 					 &width, &height))
1424     priv->icon_size = MAX (width, height);
1425   else
1426     priv->icon_size = FALLBACK_ICON_SIZE;
1427 
1428   update_label_and_image (button);
1429 
1430   gtk_tree_model_get_iter_first (priv->model, &iter);
1431 
1432   theme = get_icon_theme (GTK_WIDGET (button));
1433 
1434   do
1435     {
1436       GdkPixbuf *pixbuf;
1437       gchar type;
1438       gpointer data;
1439 
1440       type = ROW_TYPE_INVALID;
1441       gtk_tree_model_get (priv->model, &iter,
1442 			  TYPE_COLUMN, &type,
1443 			  DATA_COLUMN, &data,
1444 			  -1);
1445 
1446       switch (type)
1447 	{
1448 	case ROW_TYPE_SPECIAL:
1449 	case ROW_TYPE_SHORTCUT:
1450 	case ROW_TYPE_BOOKMARK:
1451 	case ROW_TYPE_CURRENT_FOLDER:
1452 	  if (data)
1453 	    {
1454 	      if (g_file_is_native (G_FILE (data)))
1455 		{
1456 		  GtkTreePath *path;
1457 		  GCancellable *cancellable;
1458 		  struct ChangeIconThemeData *info;
1459 
1460 		  info = g_new0 (struct ChangeIconThemeData, 1);
1461 		  info->button = g_object_ref (button);
1462 		  path = gtk_tree_model_get_path (priv->model, &iter);
1463 		  info->row_ref = gtk_tree_row_reference_new (priv->model, path);
1464 		  gtk_tree_path_free (path);
1465 
1466 		  cancellable =
1467 		    _gtk_file_system_get_info (priv->fs, data,
1468 					       "standard::icon",
1469 					       change_icon_theme_get_info_cb,
1470 					       info);
1471 		  button->priv->change_icon_theme_cancellables =
1472 		    g_slist_append (button->priv->change_icon_theme_cancellables, cancellable);
1473 		  pixbuf = NULL;
1474 		}
1475 	      else
1476 		/* Don't call get_info for remote paths to avoid latency and
1477 		 * auth dialogs.
1478 		 * If we switch to a better bookmarks file format (XBEL), we
1479 		 * should use mime info to get a better icon.
1480 		 */
1481 		pixbuf = gtk_icon_theme_load_icon (theme, "folder-remote",
1482 						   priv->icon_size, 0, NULL);
1483 	    }
1484 	  else
1485 	    pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1486 					       priv->icon_size, 0, NULL);
1487 	  break;
1488 	case ROW_TYPE_VOLUME:
1489 	  if (data)
1490 	    pixbuf = _gtk_file_system_volume_render_icon (data,
1491 							  GTK_WIDGET (button),
1492 							  priv->icon_size,
1493 							  NULL);
1494 	  else
1495 	    pixbuf = gtk_icon_theme_load_icon (theme, FALLBACK_ICON_NAME,
1496 					       priv->icon_size, 0, NULL);
1497 	  break;
1498 	default:
1499 	  continue;
1500 	  break;
1501 	}
1502 
1503       if (pixbuf)
1504 	width = MAX (width, gdk_pixbuf_get_width (pixbuf));
1505 
1506       gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
1507 			  ICON_COLUMN, pixbuf,
1508 			  -1);
1509 
1510       if (pixbuf)
1511 	g_object_unref (pixbuf);
1512     }
1513   while (gtk_tree_model_iter_next (priv->model, &iter));
1514 
1515   g_object_set (button->priv->icon_cell,
1516 		"width", width,
1517 		NULL);
1518 }
1519 
1520 static void
gtk_file_chooser_button_style_set(GtkWidget * widget,GtkStyle * old_style)1521 gtk_file_chooser_button_style_set (GtkWidget *widget,
1522 				   GtkStyle  *old_style)
1523 {
1524   GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set (widget,
1525 								      old_style);
1526 
1527   if (gtk_widget_has_screen (widget))
1528     change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1529 }
1530 
1531 static void
gtk_file_chooser_button_screen_changed(GtkWidget * widget,GdkScreen * old_screen)1532 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
1533 					GdkScreen *old_screen)
1534 {
1535   if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
1536     GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed (widget,
1537 									     old_screen);
1538 
1539   change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
1540 }
1541 
1542 
1543 /* ******************* *
1544  *  Utility Functions  *
1545  * ******************* */
1546 
1547 /* General */
1548 static GtkIconTheme *
get_icon_theme(GtkWidget * widget)1549 get_icon_theme (GtkWidget *widget)
1550 {
1551   if (gtk_widget_has_screen (widget))
1552     return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
1553 
1554   return gtk_icon_theme_get_default ();
1555 }
1556 
1557 
1558 struct SetDisplayNameData
1559 {
1560   GtkFileChooserButton *button;
1561   char *label;
1562   GtkTreeRowReference *row_ref;
1563 };
1564 
1565 static void
set_info_get_info_cb(GCancellable * cancellable,GFileInfo * info,const GError * error,gpointer callback_data)1566 set_info_get_info_cb (GCancellable *cancellable,
1567 		      GFileInfo    *info,
1568 		      const GError *error,
1569 		      gpointer      callback_data)
1570 {
1571   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1572   GdkPixbuf *pixbuf;
1573   GtkTreePath *path;
1574   GtkTreeIter iter;
1575   GCancellable *model_cancellable;
1576   struct SetDisplayNameData *data = callback_data;
1577   gboolean is_folder;
1578 
1579   if (!data->button->priv->model)
1580     /* button got destroyed */
1581     goto out;
1582 
1583   path = gtk_tree_row_reference_get_path (data->row_ref);
1584   if (!path)
1585     /* Cancellable doesn't exist anymore in the model */
1586     goto out;
1587 
1588   gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1589   gtk_tree_path_free (path);
1590 
1591   /* Validate the cancellable */
1592   gtk_tree_model_get (data->button->priv->model, &iter,
1593 		      CANCELLABLE_COLUMN, &model_cancellable,
1594 		      -1);
1595   if (cancellable != model_cancellable)
1596     goto out;
1597 
1598   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1599 		      CANCELLABLE_COLUMN, NULL,
1600 		      -1);
1601 
1602   if (cancelled || error)
1603     /* There was an error, leave the fallback name in there */
1604     goto out;
1605 
1606   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1607 
1608   if (!data->label)
1609     data->label = g_strdup (g_file_info_get_display_name (info));
1610 
1611   is_folder = _gtk_file_info_consider_as_directory (info);
1612 
1613   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1614 		      ICON_COLUMN, pixbuf,
1615 		      DISPLAY_NAME_COLUMN, data->label,
1616 		      IS_FOLDER_COLUMN, is_folder,
1617 		      -1);
1618 
1619   if (pixbuf)
1620     g_object_unref (pixbuf);
1621 
1622 out:
1623   g_object_unref (data->button);
1624   g_free (data->label);
1625   gtk_tree_row_reference_free (data->row_ref);
1626   g_free (data);
1627 
1628   g_object_unref (cancellable);
1629 }
1630 
1631 static void
set_info_for_file_at_iter(GtkFileChooserButton * button,GFile * file,GtkTreeIter * iter)1632 set_info_for_file_at_iter (GtkFileChooserButton *button,
1633 			   GFile                *file,
1634 			   GtkTreeIter          *iter)
1635 {
1636   struct SetDisplayNameData *data;
1637   GtkTreePath *tree_path;
1638   GCancellable *cancellable;
1639 
1640   data = g_new0 (struct SetDisplayNameData, 1);
1641   data->button = g_object_ref (button);
1642   data->label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
1643 
1644   tree_path = gtk_tree_model_get_path (button->priv->model, iter);
1645   data->row_ref = gtk_tree_row_reference_new (button->priv->model, tree_path);
1646   gtk_tree_path_free (tree_path);
1647 
1648   cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1649 					   "standard::type,standard::icon,standard::display-name",
1650 					   set_info_get_info_cb, data);
1651 
1652   gtk_list_store_set (GTK_LIST_STORE (button->priv->model), iter,
1653 		      CANCELLABLE_COLUMN, cancellable,
1654 		      -1);
1655 }
1656 
1657 /* Shortcuts Model */
1658 static gint
model_get_type_position(GtkFileChooserButton * button,RowType row_type)1659 model_get_type_position (GtkFileChooserButton *button,
1660 			 RowType               row_type)
1661 {
1662   gint retval = 0;
1663 
1664   if (row_type == ROW_TYPE_SPECIAL)
1665     return retval;
1666 
1667   retval += button->priv->n_special;
1668 
1669   if (row_type == ROW_TYPE_VOLUME)
1670     return retval;
1671 
1672   retval += button->priv->n_volumes;
1673 
1674   if (row_type == ROW_TYPE_SHORTCUT)
1675     return retval;
1676 
1677   retval += button->priv->n_shortcuts;
1678 
1679   if (row_type == ROW_TYPE_BOOKMARK_SEPARATOR)
1680     return retval;
1681 
1682   retval += button->priv->has_bookmark_separator;
1683 
1684   if (row_type == ROW_TYPE_BOOKMARK)
1685     return retval;
1686 
1687   retval += button->priv->n_bookmarks;
1688 
1689   if (row_type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR)
1690     return retval;
1691 
1692   retval += button->priv->has_current_folder_separator;
1693 
1694   if (row_type == ROW_TYPE_CURRENT_FOLDER)
1695     return retval;
1696 
1697   retval += button->priv->has_current_folder;
1698 
1699   if (row_type == ROW_TYPE_OTHER_SEPARATOR)
1700     return retval;
1701 
1702   retval += button->priv->has_other_separator;
1703 
1704   if (row_type == ROW_TYPE_OTHER)
1705     return retval;
1706 
1707   retval++;
1708 
1709   if (row_type == ROW_TYPE_EMPTY_SELECTION)
1710     return retval;
1711 
1712   g_assert_not_reached ();
1713   return -1;
1714 }
1715 
1716 static void
model_free_row_data(GtkFileChooserButton * button,GtkTreeIter * iter)1717 model_free_row_data (GtkFileChooserButton *button,
1718 		     GtkTreeIter          *iter)
1719 {
1720   gchar type;
1721   gpointer data;
1722   GCancellable *cancellable;
1723 
1724   gtk_tree_model_get (button->priv->model, iter,
1725 		      TYPE_COLUMN, &type,
1726 		      DATA_COLUMN, &data,
1727 		      CANCELLABLE_COLUMN, &cancellable,
1728 		      -1);
1729 
1730   if (cancellable)
1731     g_cancellable_cancel (cancellable);
1732 
1733   switch (type)
1734     {
1735     case ROW_TYPE_SPECIAL:
1736     case ROW_TYPE_SHORTCUT:
1737     case ROW_TYPE_BOOKMARK:
1738     case ROW_TYPE_CURRENT_FOLDER:
1739       g_object_unref (data);
1740       break;
1741     case ROW_TYPE_VOLUME:
1742       _gtk_file_system_volume_unref (data);
1743       break;
1744     default:
1745       break;
1746     }
1747 }
1748 
1749 static void
model_add_special_get_info_cb(GCancellable * cancellable,GFileInfo * info,const GError * error,gpointer user_data)1750 model_add_special_get_info_cb (GCancellable *cancellable,
1751 			       GFileInfo    *info,
1752 			       const GError *error,
1753 			       gpointer      user_data)
1754 {
1755   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1756   GtkTreeIter iter;
1757   GtkTreePath *path;
1758   GdkPixbuf *pixbuf;
1759   GCancellable *model_cancellable;
1760   struct ChangeIconThemeData *data = user_data;
1761   gchar *name;
1762 
1763   if (!data->button->priv->model)
1764     /* button got destroyed */
1765     goto out;
1766 
1767   path = gtk_tree_row_reference_get_path (data->row_ref);
1768   if (!path)
1769     /* Cancellable doesn't exist anymore in the model */
1770     goto out;
1771 
1772   gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
1773   gtk_tree_path_free (path);
1774 
1775   gtk_tree_model_get (data->button->priv->model, &iter,
1776 		      CANCELLABLE_COLUMN, &model_cancellable,
1777 		      -1);
1778   if (cancellable != model_cancellable)
1779     goto out;
1780 
1781   gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1782 		      CANCELLABLE_COLUMN, NULL,
1783 		      -1);
1784 
1785   if (cancelled || error)
1786     goto out;
1787 
1788   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1789 
1790   if (pixbuf)
1791     {
1792       gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1793 			  ICON_COLUMN, pixbuf,
1794 			  -1);
1795       g_object_unref (pixbuf);
1796     }
1797 
1798   gtk_tree_model_get (data->button->priv->model, &iter,
1799                       DISPLAY_NAME_COLUMN, &name,
1800                       -1);
1801   if (!name)
1802     gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
1803   		        DISPLAY_NAME_COLUMN, g_file_info_get_display_name (info),
1804 		        -1);
1805   g_free (name);
1806 
1807 out:
1808   g_object_unref (data->button);
1809   gtk_tree_row_reference_free (data->row_ref);
1810   g_free (data);
1811 
1812   g_object_unref (cancellable);
1813 }
1814 
1815 static void
model_add_special(GtkFileChooserButton * button)1816 model_add_special (GtkFileChooserButton *button)
1817 {
1818   const gchar *homedir;
1819   const gchar *desktopdir;
1820   GtkListStore *store;
1821   GtkTreeIter iter;
1822   GFile *file;
1823   gint pos;
1824 
1825   store = GTK_LIST_STORE (button->priv->model);
1826   pos = model_get_type_position (button, ROW_TYPE_SPECIAL);
1827 
1828   homedir = g_get_home_dir ();
1829 
1830   if (homedir)
1831     {
1832       GtkTreePath *tree_path;
1833       GCancellable *cancellable;
1834       struct ChangeIconThemeData *info;
1835 
1836       file = g_file_new_for_path (homedir);
1837       gtk_list_store_insert (store, &iter, pos);
1838       pos++;
1839 
1840       info = g_new0 (struct ChangeIconThemeData, 1);
1841       info->button = g_object_ref (button);
1842       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1843       info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1844 						  tree_path);
1845       gtk_tree_path_free (tree_path);
1846 
1847       cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1848 					       "standard::icon,standard::display-name",
1849 					       model_add_special_get_info_cb, info);
1850 
1851       gtk_list_store_set (store, &iter,
1852 			  ICON_COLUMN, NULL,
1853 			  DISPLAY_NAME_COLUMN, NULL,
1854 			  TYPE_COLUMN, ROW_TYPE_SPECIAL,
1855 			  DATA_COLUMN, file,
1856 			  IS_FOLDER_COLUMN, TRUE,
1857 			  CANCELLABLE_COLUMN, cancellable,
1858 			  -1);
1859 
1860       button->priv->n_special++;
1861     }
1862 
1863   desktopdir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
1864 
1865   /* "To disable a directory, point it to the homedir."
1866    * See http://freedesktop.org/wiki/Software/xdg-user-dirs
1867    **/
1868   if (g_strcmp0 (desktopdir, g_get_home_dir ()) != 0)
1869     {
1870       GtkTreePath *tree_path;
1871       GCancellable *cancellable;
1872       struct ChangeIconThemeData *info;
1873 
1874       file = g_file_new_for_path (desktopdir);
1875       gtk_list_store_insert (store, &iter, pos);
1876       pos++;
1877 
1878       info = g_new0 (struct ChangeIconThemeData, 1);
1879       info->button = g_object_ref (button);
1880       tree_path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
1881       info->row_ref = gtk_tree_row_reference_new (GTK_TREE_MODEL (store),
1882 						  tree_path);
1883       gtk_tree_path_free (tree_path);
1884 
1885       cancellable = _gtk_file_system_get_info (button->priv->fs, file,
1886 					       "standard::icon,standard::display-name",
1887 					       model_add_special_get_info_cb, info);
1888 
1889       gtk_list_store_set (store, &iter,
1890 			  TYPE_COLUMN, ROW_TYPE_SPECIAL,
1891 			  ICON_COLUMN, NULL,
1892 			  DISPLAY_NAME_COLUMN, _(DESKTOP_DISPLAY_NAME),
1893 			  DATA_COLUMN, file,
1894 			  IS_FOLDER_COLUMN, TRUE,
1895 			  CANCELLABLE_COLUMN, cancellable,
1896 			  -1);
1897 
1898       button->priv->n_special++;
1899     }
1900 }
1901 
1902 static void
model_add_volumes(GtkFileChooserButton * button,GSList * volumes)1903 model_add_volumes (GtkFileChooserButton *button,
1904 		   GSList               *volumes)
1905 {
1906   GtkListStore *store;
1907   gint pos;
1908   gboolean local_only;
1909   GSList *l;
1910 
1911   if (!volumes)
1912     return;
1913 
1914   store = GTK_LIST_STORE (button->priv->model);
1915   pos = model_get_type_position (button, ROW_TYPE_VOLUME);
1916   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
1917 
1918   for (l = volumes; l; l = l->next)
1919     {
1920       GtkFileSystemVolume *volume;
1921       GtkTreeIter iter;
1922       GdkPixbuf *pixbuf;
1923       gchar *display_name;
1924 
1925       volume = l->data;
1926 
1927       if (local_only)
1928         {
1929           if (_gtk_file_system_volume_is_mounted (volume))
1930             {
1931               GFile *base_file;
1932 
1933               base_file = _gtk_file_system_volume_get_root (volume);
1934               if (base_file != NULL)
1935                 {
1936                   if (!_gtk_file_has_native_path (base_file))
1937                     {
1938                       g_object_unref (base_file);
1939                       continue;
1940                     }
1941                   else
1942                     g_object_unref (base_file);
1943                 }
1944             }
1945         }
1946 
1947       pixbuf = _gtk_file_system_volume_render_icon (volume,
1948                                                     GTK_WIDGET (button),
1949                                                     button->priv->icon_size,
1950                                                     NULL);
1951       display_name = _gtk_file_system_volume_get_display_name (volume);
1952 
1953       gtk_list_store_insert (store, &iter, pos);
1954       gtk_list_store_set (store, &iter,
1955                           ICON_COLUMN, pixbuf,
1956                           DISPLAY_NAME_COLUMN, display_name,
1957                           TYPE_COLUMN, ROW_TYPE_VOLUME,
1958                           DATA_COLUMN, _gtk_file_system_volume_ref (volume),
1959                           IS_FOLDER_COLUMN, TRUE,
1960                           -1);
1961 
1962       if (pixbuf)
1963         g_object_unref (pixbuf);
1964       g_free (display_name);
1965 
1966       button->priv->n_volumes++;
1967       pos++;
1968     }
1969 }
1970 
1971 extern gchar * _gtk_file_chooser_label_for_file (GFile *file);
1972 
1973 static void
model_add_bookmarks(GtkFileChooserButton * button,GSList * bookmarks)1974 model_add_bookmarks (GtkFileChooserButton *button,
1975 		     GSList               *bookmarks)
1976 {
1977   GtkListStore *store;
1978   GtkTreeIter iter;
1979   gint pos;
1980   gboolean local_only;
1981   GSList *l;
1982 
1983   if (!bookmarks)
1984     return;
1985 
1986   store = GTK_LIST_STORE (button->priv->model);
1987   pos = model_get_type_position (button, ROW_TYPE_BOOKMARK);
1988   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (button->priv->dialog));
1989 
1990   for (l = bookmarks; l; l = l->next)
1991     {
1992       GFile *file;
1993 
1994       file = l->data;
1995 
1996       if (_gtk_file_has_native_path (file))
1997 	{
1998 	  gtk_list_store_insert (store, &iter, pos);
1999 	  gtk_list_store_set (store, &iter,
2000 			      ICON_COLUMN, NULL,
2001 			      DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2002 			      TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2003 			      DATA_COLUMN, g_object_ref (file),
2004 			      IS_FOLDER_COLUMN, FALSE,
2005 			      -1);
2006 	  set_info_for_file_at_iter (button, file, &iter);
2007 	}
2008       else
2009 	{
2010 	  gchar *label;
2011 	  GtkIconTheme *icon_theme;
2012 	  GdkPixbuf *pixbuf;
2013 
2014 	  if (local_only)
2015 	    continue;
2016 
2017 	  /* Don't call get_info for remote paths to avoid latency and
2018 	   * auth dialogs.
2019 	   * If we switch to a better bookmarks file format (XBEL), we
2020 	   * should use mime info to get a better icon.
2021 	   */
2022 	  label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2023 	  if (!label)
2024 	    label = _gtk_file_chooser_label_for_file (file);
2025 
2026 	  icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2027 	  pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2028 					     button->priv->icon_size, 0, NULL);
2029 
2030 	  gtk_list_store_insert (store, &iter, pos);
2031 	  gtk_list_store_set (store, &iter,
2032 			      ICON_COLUMN, pixbuf,
2033 			      DISPLAY_NAME_COLUMN, label,
2034 			      TYPE_COLUMN, ROW_TYPE_BOOKMARK,
2035 			      DATA_COLUMN, g_object_ref (file),
2036 			      IS_FOLDER_COLUMN, TRUE,
2037 			      -1);
2038 
2039 	  g_free (label);
2040 	  g_object_unref (pixbuf);
2041 	}
2042 
2043       button->priv->n_bookmarks++;
2044       pos++;
2045     }
2046 
2047   if (button->priv->n_bookmarks > 0 &&
2048       !button->priv->has_bookmark_separator)
2049     {
2050       pos = model_get_type_position (button, ROW_TYPE_BOOKMARK_SEPARATOR);
2051 
2052       gtk_list_store_insert (store, &iter, pos);
2053       gtk_list_store_set (store, &iter,
2054 			  ICON_COLUMN, NULL,
2055 			  DISPLAY_NAME_COLUMN, NULL,
2056 			  TYPE_COLUMN, ROW_TYPE_BOOKMARK_SEPARATOR,
2057 			  DATA_COLUMN, NULL,
2058 			  IS_FOLDER_COLUMN, FALSE,
2059 			  -1);
2060       button->priv->has_bookmark_separator = TRUE;
2061     }
2062 }
2063 
2064 static void
model_update_current_folder(GtkFileChooserButton * button,GFile * file)2065 model_update_current_folder (GtkFileChooserButton *button,
2066 			     GFile                *file)
2067 {
2068   GtkListStore *store;
2069   GtkTreeIter iter;
2070   gint pos;
2071 
2072   if (!file)
2073     return;
2074 
2075   store = GTK_LIST_STORE (button->priv->model);
2076 
2077   if (!button->priv->has_current_folder_separator)
2078     {
2079       pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER_SEPARATOR);
2080       gtk_list_store_insert (store, &iter, pos);
2081       gtk_list_store_set (store, &iter,
2082 			  ICON_COLUMN, NULL,
2083 			  DISPLAY_NAME_COLUMN, NULL,
2084 			  TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
2085 			  DATA_COLUMN, NULL,
2086 			  IS_FOLDER_COLUMN, FALSE,
2087 			  -1);
2088       button->priv->has_current_folder_separator = TRUE;
2089     }
2090 
2091   pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2092   if (!button->priv->has_current_folder)
2093     {
2094       gtk_list_store_insert (store, &iter, pos);
2095       button->priv->has_current_folder = TRUE;
2096     }
2097   else
2098     {
2099       gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos);
2100       model_free_row_data (button, &iter);
2101     }
2102 
2103   if (g_file_is_native (file))
2104     {
2105       gtk_list_store_set (store, &iter,
2106 			  ICON_COLUMN, NULL,
2107 			  DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2108 			  TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2109 			  DATA_COLUMN, g_object_ref (file),
2110 			  IS_FOLDER_COLUMN, FALSE,
2111 			  -1);
2112       set_info_for_file_at_iter (button, file, &iter);
2113     }
2114   else
2115     {
2116       gchar *label;
2117       GtkIconTheme *icon_theme;
2118       GdkPixbuf *pixbuf;
2119 
2120       /* Don't call get_info for remote paths to avoid latency and
2121        * auth dialogs.
2122        * If we switch to a better bookmarks file format (XBEL), we
2123        * should use mime info to get a better icon.
2124        */
2125       label = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2126       if (!label)
2127 	label = _gtk_file_chooser_label_for_file (file);
2128 
2129       icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
2130 
2131       if (g_file_is_native (file))
2132 	  pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder",
2133 					     button->priv->icon_size, 0, NULL);
2134       else
2135 	  pixbuf = gtk_icon_theme_load_icon (icon_theme, "folder-remote",
2136 					     button->priv->icon_size, 0, NULL);
2137 
2138       gtk_list_store_set (store, &iter,
2139 			  ICON_COLUMN, pixbuf,
2140 			  DISPLAY_NAME_COLUMN, label,
2141 			  TYPE_COLUMN, ROW_TYPE_CURRENT_FOLDER,
2142 			  DATA_COLUMN, g_object_ref (file),
2143 			  IS_FOLDER_COLUMN, TRUE,
2144 			  -1);
2145 
2146       g_free (label);
2147       g_object_unref (pixbuf);
2148     }
2149 }
2150 
2151 static void
model_add_other(GtkFileChooserButton * button)2152 model_add_other (GtkFileChooserButton *button)
2153 {
2154   GtkListStore *store;
2155   GtkTreeIter iter;
2156   gint pos;
2157 
2158   store = GTK_LIST_STORE (button->priv->model);
2159   pos = model_get_type_position (button, ROW_TYPE_OTHER_SEPARATOR);
2160 
2161   gtk_list_store_insert (store, &iter, pos);
2162   gtk_list_store_set (store, &iter,
2163 		      ICON_COLUMN, NULL,
2164 		      DISPLAY_NAME_COLUMN, NULL,
2165 		      TYPE_COLUMN, ROW_TYPE_OTHER_SEPARATOR,
2166 		      DATA_COLUMN, NULL,
2167 		      IS_FOLDER_COLUMN, FALSE,
2168 		      -1);
2169   button->priv->has_other_separator = TRUE;
2170   pos++;
2171 
2172   gtk_list_store_insert (store, &iter, pos);
2173   gtk_list_store_set (store, &iter,
2174 		      ICON_COLUMN, NULL,
2175 		      DISPLAY_NAME_COLUMN, _("Other..."),
2176 		      TYPE_COLUMN, ROW_TYPE_OTHER,
2177 		      DATA_COLUMN, NULL,
2178 		      IS_FOLDER_COLUMN, FALSE,
2179 		      -1);
2180 }
2181 
2182 static void
model_add_empty_selection(GtkFileChooserButton * button)2183 model_add_empty_selection (GtkFileChooserButton *button)
2184 {
2185   GtkListStore *store;
2186   GtkTreeIter iter;
2187   gint pos;
2188 
2189   store = GTK_LIST_STORE (button->priv->model);
2190   pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2191 
2192   gtk_list_store_insert (store, &iter, pos);
2193   gtk_list_store_set (store, &iter,
2194 		      ICON_COLUMN, NULL,
2195 		      DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
2196 		      TYPE_COLUMN, ROW_TYPE_EMPTY_SELECTION,
2197 		      DATA_COLUMN, NULL,
2198 		      IS_FOLDER_COLUMN, FALSE,
2199 		      -1);
2200 }
2201 
2202 static void
model_remove_rows(GtkFileChooserButton * button,gint pos,gint n_rows)2203 model_remove_rows (GtkFileChooserButton *button,
2204 		   gint                  pos,
2205 		   gint                  n_rows)
2206 {
2207   GtkListStore *store;
2208 
2209   if (!n_rows)
2210     return;
2211 
2212   store = GTK_LIST_STORE (button->priv->model);
2213 
2214   do
2215     {
2216       GtkTreeIter iter;
2217 
2218       if (!gtk_tree_model_iter_nth_child (button->priv->model, &iter, NULL, pos))
2219 	g_assert_not_reached ();
2220 
2221       model_free_row_data (button, &iter);
2222       gtk_list_store_remove (store, &iter);
2223       n_rows--;
2224     }
2225   while (n_rows);
2226 }
2227 
2228 /* Filter Model */
2229 static gboolean
test_if_file_is_visible(GtkFileSystem * fs,GFile * file,gboolean local_only,gboolean is_folder)2230 test_if_file_is_visible (GtkFileSystem *fs,
2231 			 GFile         *file,
2232 			 gboolean       local_only,
2233 			 gboolean       is_folder)
2234 {
2235   if (!file)
2236     return FALSE;
2237 
2238   if (local_only && !_gtk_file_has_native_path (file))
2239     return FALSE;
2240 
2241   if (!is_folder)
2242     return FALSE;
2243 
2244   return TRUE;
2245 }
2246 
2247 static gboolean
filter_model_visible_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)2248 filter_model_visible_func (GtkTreeModel *model,
2249 			   GtkTreeIter  *iter,
2250 			   gpointer      user_data)
2251 {
2252   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2253   GtkFileChooserButtonPrivate *priv = button->priv;
2254   gchar type;
2255   gpointer data;
2256   gboolean local_only, retval, is_folder;
2257 
2258   type = ROW_TYPE_INVALID;
2259   data = NULL;
2260   local_only = gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog));
2261 
2262   gtk_tree_model_get (model, iter,
2263 		      TYPE_COLUMN, &type,
2264 		      DATA_COLUMN, &data,
2265 		      IS_FOLDER_COLUMN, &is_folder,
2266 		      -1);
2267 
2268   switch (type)
2269     {
2270     case ROW_TYPE_CURRENT_FOLDER:
2271       retval = TRUE;
2272       break;
2273     case ROW_TYPE_SPECIAL:
2274     case ROW_TYPE_SHORTCUT:
2275     case ROW_TYPE_BOOKMARK:
2276       retval = test_if_file_is_visible (priv->fs, data, local_only, is_folder);
2277       break;
2278     case ROW_TYPE_VOLUME:
2279       {
2280 	retval = TRUE;
2281 	if (local_only)
2282 	  {
2283 	    if (_gtk_file_system_volume_is_mounted (data))
2284 	      {
2285 		GFile *base_file;
2286 
2287 		base_file = _gtk_file_system_volume_get_root (data);
2288 
2289 		if (base_file)
2290 		  {
2291 		    if (!_gtk_file_has_native_path (base_file))
2292 		      retval = FALSE;
2293                     g_object_unref (base_file);
2294 		  }
2295 		else
2296 		  retval = FALSE;
2297 	      }
2298 	  }
2299       }
2300       break;
2301     case ROW_TYPE_EMPTY_SELECTION:
2302       {
2303 	gboolean popup_shown;
2304 
2305 	g_object_get (priv->combo_box,
2306 		      "popup-shown", &popup_shown,
2307 		      NULL);
2308 
2309 	if (popup_shown)
2310 	  retval = FALSE;
2311 	else
2312 	  {
2313 	    GFile *selected;
2314 
2315 	    /* When the combo box is not popped up... */
2316 
2317 	    selected = get_selected_file (button);
2318 	    if (selected)
2319 	      retval = FALSE; /* ... nonempty selection means the ROW_TYPE_EMPTY_SELECTION is *not* visible... */
2320 	    else
2321 	      retval = TRUE;  /* ... and empty selection means the ROW_TYPE_EMPTY_SELECTION *is* visible */
2322 
2323 	    if (selected)
2324 	      g_object_unref (selected);
2325 	  }
2326 
2327 	break;
2328       }
2329     default:
2330       retval = TRUE;
2331       break;
2332     }
2333 
2334   return retval;
2335 }
2336 
2337 /* Combo Box */
2338 static void
name_cell_data_func(GtkCellLayout * layout,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)2339 name_cell_data_func (GtkCellLayout   *layout,
2340 		     GtkCellRenderer *cell,
2341 		     GtkTreeModel    *model,
2342 		     GtkTreeIter     *iter,
2343 		     gpointer         user_data)
2344 {
2345   gchar type;
2346 
2347   type = 0;
2348   gtk_tree_model_get (model, iter,
2349 		      TYPE_COLUMN, &type,
2350 		      -1);
2351 
2352   if (type == ROW_TYPE_CURRENT_FOLDER)
2353     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
2354   else
2355     g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
2356 }
2357 
2358 static gboolean
combo_box_row_separator_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)2359 combo_box_row_separator_func (GtkTreeModel *model,
2360 			      GtkTreeIter  *iter,
2361 			      gpointer      user_data)
2362 {
2363   gchar type = ROW_TYPE_INVALID;
2364 
2365   gtk_tree_model_get (model, iter, TYPE_COLUMN, &type, -1);
2366 
2367   return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
2368 	  type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
2369 	  type == ROW_TYPE_OTHER_SEPARATOR);
2370 }
2371 
2372 static void
select_combo_box_row_no_notify(GtkFileChooserButton * button,int pos)2373 select_combo_box_row_no_notify (GtkFileChooserButton *button, int pos)
2374 {
2375   GtkFileChooserButtonPrivate *priv = button->priv;
2376   GtkTreeIter iter, filter_iter;
2377 
2378   gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2379   gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
2380 						    &filter_iter, &iter);
2381 
2382   g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2383   gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
2384   g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
2385 }
2386 
2387 static void
update_combo_box(GtkFileChooserButton * button)2388 update_combo_box (GtkFileChooserButton *button)
2389 {
2390   GtkFileChooserButtonPrivate *priv = button->priv;
2391   GFile *file;
2392   GtkTreeIter iter;
2393   gboolean row_found;
2394 
2395   file = get_selected_file (button);
2396 
2397   row_found = FALSE;
2398 
2399   gtk_tree_model_get_iter_first (priv->filter_model, &iter);
2400 
2401   do
2402     {
2403       gchar type;
2404       gpointer data;
2405 
2406       type = ROW_TYPE_INVALID;
2407       data = NULL;
2408 
2409       gtk_tree_model_get (priv->filter_model, &iter,
2410 			  TYPE_COLUMN, &type,
2411 			  DATA_COLUMN, &data,
2412 			  -1);
2413 
2414       switch (type)
2415 	{
2416 	case ROW_TYPE_SPECIAL:
2417 	case ROW_TYPE_SHORTCUT:
2418 	case ROW_TYPE_BOOKMARK:
2419 	case ROW_TYPE_CURRENT_FOLDER:
2420 	  row_found = (file && g_file_equal (data, file));
2421 	  break;
2422 	case ROW_TYPE_VOLUME:
2423 	  {
2424 	    GFile *base_file;
2425 
2426 	    base_file = _gtk_file_system_volume_get_root (data);
2427             if (base_file)
2428               {
2429 	        row_found = (file && g_file_equal (base_file, file));
2430 		g_object_unref (base_file);
2431               }
2432 	  }
2433 	  break;
2434 	default:
2435 	  row_found = FALSE;
2436 	  break;
2437 	}
2438 
2439       if (row_found)
2440 	{
2441 	  g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
2442 	  gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box),
2443 					 &iter);
2444 	  g_signal_handler_unblock (priv->combo_box,
2445 				    priv->combo_box_changed_id);
2446 	}
2447     }
2448   while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
2449 
2450   if (!row_found)
2451     {
2452       gint pos;
2453 
2454       /* If it hasn't been found already, update & select the current-folder row. */
2455       if (file)
2456 	{
2457 	  model_update_current_folder (button, file);
2458 	  pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
2459 	}
2460       else
2461 	{
2462 	  /* No selection; switch to that row */
2463 
2464 	  pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2465 	}
2466 
2467       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2468 
2469       select_combo_box_row_no_notify (button, pos);
2470     }
2471 
2472   if (file)
2473     g_object_unref (file);
2474 }
2475 
2476 /* Button */
2477 static void
update_label_get_info_cb(GCancellable * cancellable,GFileInfo * info,const GError * error,gpointer data)2478 update_label_get_info_cb (GCancellable *cancellable,
2479 			  GFileInfo    *info,
2480 			  const GError *error,
2481 			  gpointer      data)
2482 {
2483   gboolean cancelled = g_cancellable_is_cancelled (cancellable);
2484   GdkPixbuf *pixbuf;
2485   GtkFileChooserButton *button = data;
2486   GtkFileChooserButtonPrivate *priv = button->priv;
2487 
2488   if (cancellable != priv->update_button_cancellable)
2489     goto out;
2490 
2491   priv->update_button_cancellable = NULL;
2492 
2493   if (cancelled || error)
2494     goto out;
2495 
2496   gtk_label_set_text (GTK_LABEL (priv->label), g_file_info_get_display_name (info));
2497 
2498   pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (priv->image), priv->icon_size);
2499 
2500   if (!pixbuf)
2501     pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2502 				       FALLBACK_ICON_NAME,
2503 				       priv->icon_size, 0, NULL);
2504 
2505   gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2506   if (pixbuf)
2507     g_object_unref (pixbuf);
2508 
2509 out:
2510   emit_selection_changed_if_changing_selection (button);
2511 
2512   g_object_unref (button);
2513   g_object_unref (cancellable);
2514 }
2515 
2516 static void
update_label_and_image(GtkFileChooserButton * button)2517 update_label_and_image (GtkFileChooserButton *button)
2518 {
2519   GtkFileChooserButtonPrivate *priv = button->priv;
2520   gchar *label_text;
2521   GFile *file;
2522   gboolean done_changing_selection;
2523 
2524   file = get_selected_file (button);
2525 
2526   label_text = NULL;
2527   done_changing_selection = FALSE;
2528 
2529   if (priv->update_button_cancellable)
2530     {
2531       g_cancellable_cancel (priv->update_button_cancellable);
2532       priv->update_button_cancellable = NULL;
2533     }
2534 
2535   if (file)
2536     {
2537       GtkFileSystemVolume *volume = NULL;
2538 
2539       volume = _gtk_file_system_get_volume_for_file (priv->fs, file);
2540       if (volume)
2541         {
2542           GFile *base_file;
2543 
2544           base_file = _gtk_file_system_volume_get_root (volume);
2545           if (base_file && g_file_equal (base_file, file))
2546             {
2547               GdkPixbuf *pixbuf;
2548 
2549               label_text = _gtk_file_system_volume_get_display_name (volume);
2550               pixbuf = _gtk_file_system_volume_render_icon (volume,
2551                                                             GTK_WIDGET (button),
2552                                                             priv->icon_size,
2553                                                             NULL);
2554               gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2555               if (pixbuf)
2556                 g_object_unref (pixbuf);
2557             }
2558 
2559           if (base_file)
2560             g_object_unref (base_file);
2561 
2562           _gtk_file_system_volume_unref (volume);
2563 
2564           if (label_text)
2565 	    {
2566 	      done_changing_selection = TRUE;
2567 	      goto out;
2568 	    }
2569         }
2570 
2571       if (g_file_is_native (file))
2572         {
2573           priv->update_button_cancellable =
2574             _gtk_file_system_get_info (priv->fs, file,
2575                                        "standard::icon,standard::display-name",
2576                                        update_label_get_info_cb,
2577                                        g_object_ref (button));
2578         }
2579       else
2580         {
2581           GdkPixbuf *pixbuf;
2582 
2583           label_text = _gtk_file_system_get_bookmark_label (button->priv->fs, file);
2584           pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (priv->image)),
2585                                              "text-x-generic",
2586                                              priv->icon_size, 0, NULL);
2587           gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
2588           if (pixbuf)
2589             g_object_unref (pixbuf);
2590 
2591 	  done_changing_selection = TRUE;
2592         }
2593     }
2594   else
2595     {
2596       /* We know the selection is empty */
2597       done_changing_selection = TRUE;
2598     }
2599 
2600 out:
2601 
2602   if (file)
2603     g_object_unref (file);
2604 
2605   if (label_text)
2606     {
2607       gtk_label_set_text (GTK_LABEL (priv->label), label_text);
2608       g_free (label_text);
2609     }
2610   else
2611     {
2612       gtk_label_set_text (GTK_LABEL (priv->label), _(FALLBACK_DISPLAY_NAME));
2613       gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
2614     }
2615 
2616   if (done_changing_selection)
2617     emit_selection_changed_if_changing_selection (button);
2618 }
2619 
2620 
2621 /* ************************ *
2622  *  Child Object Callbacks  *
2623  * ************************ */
2624 
2625 /* File System */
2626 static void
fs_volumes_changed_cb(GtkFileSystem * fs,gpointer user_data)2627 fs_volumes_changed_cb (GtkFileSystem *fs,
2628 		       gpointer       user_data)
2629 {
2630   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2631   GtkFileChooserButtonPrivate *priv = button->priv;
2632   GSList *volumes;
2633 
2634   model_remove_rows (user_data,
2635 		     model_get_type_position (user_data, ROW_TYPE_VOLUME),
2636 		     priv->n_volumes);
2637 
2638   priv->n_volumes = 0;
2639 
2640   volumes = _gtk_file_system_list_volumes (fs);
2641   model_add_volumes (user_data, volumes);
2642   g_slist_free (volumes);
2643 
2644   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2645 
2646   update_label_and_image (user_data);
2647   update_combo_box (user_data);
2648 }
2649 
2650 static void
fs_bookmarks_changed_cb(GtkFileSystem * fs,gpointer user_data)2651 fs_bookmarks_changed_cb (GtkFileSystem *fs,
2652 			 gpointer       user_data)
2653 {
2654   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2655   GtkFileChooserButtonPrivate *priv = button->priv;
2656   GSList *bookmarks;
2657 
2658   bookmarks = _gtk_file_system_list_bookmarks (fs);
2659   model_remove_rows (user_data,
2660 		     model_get_type_position (user_data,
2661 					      ROW_TYPE_BOOKMARK_SEPARATOR),
2662 		     (priv->n_bookmarks + priv->has_bookmark_separator));
2663   priv->has_bookmark_separator = FALSE;
2664   priv->n_bookmarks = 0;
2665   model_add_bookmarks (user_data, bookmarks);
2666   g_slist_foreach (bookmarks, (GFunc) g_object_unref, NULL);
2667   g_slist_free (bookmarks);
2668 
2669   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2670 
2671   update_label_and_image (user_data);
2672   update_combo_box (user_data);
2673 }
2674 
2675 static void
save_inactive_state(GtkFileChooserButton * button)2676 save_inactive_state (GtkFileChooserButton *button)
2677 {
2678   GtkFileChooserButtonPrivate *priv = button->priv;
2679 
2680   if (priv->current_folder_while_inactive)
2681     g_object_unref (priv->current_folder_while_inactive);
2682 
2683   if (priv->selection_while_inactive)
2684     g_object_unref (priv->selection_while_inactive);
2685 
2686   priv->current_folder_while_inactive = gtk_file_chooser_get_current_folder_file (GTK_FILE_CHOOSER (priv->dialog));
2687   priv->selection_while_inactive = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
2688 }
2689 
2690 static void
restore_inactive_state(GtkFileChooserButton * button)2691 restore_inactive_state (GtkFileChooserButton *button)
2692 {
2693   GtkFileChooserButtonPrivate *priv = button->priv;
2694 
2695   if (priv->current_folder_while_inactive)
2696     gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog), priv->current_folder_while_inactive, NULL);
2697 
2698   if (priv->selection_while_inactive)
2699     gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), priv->selection_while_inactive, NULL);
2700   else
2701     gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
2702 }
2703 
2704 /* Dialog */
2705 static void
open_dialog(GtkFileChooserButton * button)2706 open_dialog (GtkFileChooserButton *button)
2707 {
2708   GtkFileChooserButtonPrivate *priv = button->priv;
2709 
2710   /* Setup the dialog parent to be chooser button's toplevel, and be modal
2711      as needed. */
2712   if (!gtk_widget_get_visible (priv->dialog))
2713     {
2714       GtkWidget *toplevel;
2715 
2716       toplevel = gtk_widget_get_toplevel (GTK_WIDGET (button));
2717 
2718       if (gtk_widget_is_toplevel (toplevel) && GTK_IS_WINDOW (toplevel))
2719         {
2720           if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
2721  	    gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
2722 					  GTK_WINDOW (toplevel));
2723 
2724 	  gtk_window_set_modal (GTK_WINDOW (priv->dialog),
2725 				gtk_window_get_modal (GTK_WINDOW (toplevel)));
2726 	}
2727     }
2728 
2729   if (!priv->active)
2730     {
2731       restore_inactive_state (button);
2732       priv->active = TRUE;
2733     }
2734 
2735   gtk_widget_set_sensitive (priv->combo_box, FALSE);
2736   gtk_window_present (GTK_WINDOW (priv->dialog));
2737 }
2738 
2739 /* Combo Box */
2740 static void
combo_box_changed_cb(GtkComboBox * combo_box,gpointer user_data)2741 combo_box_changed_cb (GtkComboBox *combo_box,
2742 		      gpointer     user_data)
2743 {
2744   GtkTreeIter iter;
2745 
2746   if (gtk_combo_box_get_active_iter (combo_box, &iter))
2747     {
2748       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2749       GtkFileChooserButtonPrivate *priv = button->priv;
2750       gchar type;
2751       gpointer data;
2752 
2753       type = ROW_TYPE_INVALID;
2754       data = NULL;
2755 
2756       gtk_tree_model_get (priv->filter_model, &iter,
2757 			  TYPE_COLUMN, &type,
2758 			  DATA_COLUMN, &data,
2759 			  -1);
2760 
2761       switch (type)
2762 	{
2763 	case ROW_TYPE_SPECIAL:
2764 	case ROW_TYPE_SHORTCUT:
2765 	case ROW_TYPE_BOOKMARK:
2766 	case ROW_TYPE_CURRENT_FOLDER:
2767 	  if (data)
2768 	    gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), data, NULL);
2769 	  break;
2770 	case ROW_TYPE_VOLUME:
2771 	  {
2772 	    GFile *base_file;
2773 
2774 	    base_file = _gtk_file_system_volume_get_root (data);
2775 	    if (base_file)
2776 	      {
2777 		gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), base_file, NULL);
2778 		g_object_unref (base_file);
2779 	      }
2780 	  }
2781 	  break;
2782 	case ROW_TYPE_OTHER:
2783 	  open_dialog (user_data);
2784 	  break;
2785 	default:
2786 	  break;
2787 	}
2788     }
2789 }
2790 
2791 /* Calback for the "notify::popup-shown" signal on the combo box.
2792  * When the combo is popped up, we don't want the ROW_TYPE_EMPTY_SELECTION to be visible
2793  * at all; otherwise we would be showing a "(None)" item in the combo box's popup.
2794  *
2795  * However, when the combo box is *not* popped up, we want the empty-selection row
2796  * to be visible depending on the selection.
2797  *
2798  * Since all that is done through the filter_model_visible_func(), this means
2799  * that we need to refilter the model when the combo box pops up - hence the
2800  * present signal handler.
2801  */
2802 static void
combo_box_notify_popup_shown_cb(GObject * object,GParamSpec * pspec,gpointer user_data)2803 combo_box_notify_popup_shown_cb (GObject    *object,
2804 				 GParamSpec *pspec,
2805 				 gpointer    user_data)
2806 {
2807   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2808   GtkFileChooserButtonPrivate *priv = button->priv;
2809   gboolean popup_shown;
2810 
2811   g_object_get (priv->combo_box,
2812 		"popup-shown", &popup_shown,
2813 		NULL);
2814 
2815   /* Indicate that the ROW_TYPE_EMPTY_SELECTION will change visibility... */
2816   gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2817 
2818   /* If the combo box popup got dismissed, go back to showing the ROW_TYPE_EMPTY_SELECTION if needed */
2819   if (!popup_shown)
2820     {
2821       GFile *selected = get_selected_file (button);
2822 
2823       if (!selected)
2824 	{
2825 	  int pos;
2826 
2827 	  pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
2828 	  select_combo_box_row_no_notify (button, pos);
2829 	}
2830       else
2831 	g_object_unref (selected);
2832     }
2833 }
2834 
2835 /* Button */
2836 static void
button_clicked_cb(GtkButton * real_button,gpointer user_data)2837 button_clicked_cb (GtkButton *real_button,
2838 		   gpointer   user_data)
2839 {
2840   open_dialog (user_data);
2841 }
2842 
2843 /* Dialog */
2844 
2845 static void
dialog_update_preview_cb(GtkFileChooser * dialog,gpointer user_data)2846 dialog_update_preview_cb (GtkFileChooser *dialog,
2847 			  gpointer        user_data)
2848 {
2849   g_signal_emit_by_name (user_data, "update-preview");
2850 }
2851 
2852 static void
dialog_notify_cb(GObject * dialog,GParamSpec * pspec,gpointer user_data)2853 dialog_notify_cb (GObject    *dialog,
2854 		  GParamSpec *pspec,
2855 		  gpointer    user_data)
2856 {
2857   gpointer iface;
2858 
2859   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
2860 				 GTK_TYPE_FILE_CHOOSER);
2861   if (g_object_interface_find_property (iface, pspec->name))
2862     g_object_notify (user_data, pspec->name);
2863 
2864   if (g_ascii_strcasecmp (pspec->name, "local-only") == 0)
2865     {
2866       GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2867       GtkFileChooserButtonPrivate *priv = button->priv;
2868 
2869       if (priv->has_current_folder)
2870 	{
2871 	  GtkTreeIter iter;
2872 	  gint pos;
2873 	  gpointer data;
2874 
2875 	  pos = model_get_type_position (user_data,
2876 					 ROW_TYPE_CURRENT_FOLDER);
2877 	  gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
2878 
2879 	  data = NULL;
2880 	  gtk_tree_model_get (priv->model, &iter, DATA_COLUMN, &data, -1);
2881 
2882 	  /* If the path isn't local but we're in local-only mode now, remove
2883 	   * the custom-folder row */
2884 	  if (data && !_gtk_file_has_native_path (G_FILE (data)) &&
2885 	      gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)))
2886 	    {
2887 	      pos--;
2888 	      model_remove_rows (user_data, pos, 2);
2889 	    }
2890 	}
2891 
2892       gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
2893       update_combo_box (user_data);
2894     }
2895 }
2896 
2897 static gboolean
dialog_delete_event_cb(GtkWidget * dialog,GdkEvent * event,gpointer user_data)2898 dialog_delete_event_cb (GtkWidget *dialog,
2899 			GdkEvent  *event,
2900 		        gpointer   user_data)
2901 {
2902   g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
2903 
2904   return TRUE;
2905 }
2906 
2907 static void
dialog_response_cb(GtkDialog * dialog,gint response,gpointer user_data)2908 dialog_response_cb (GtkDialog *dialog,
2909 		    gint       response,
2910 		    gpointer   user_data)
2911 {
2912   GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
2913   GtkFileChooserButtonPrivate *priv = button->priv;
2914 
2915   if (response == GTK_RESPONSE_ACCEPT ||
2916       response == GTK_RESPONSE_OK)
2917     {
2918       save_inactive_state (button);
2919 
2920       g_signal_emit_by_name (button, "current-folder-changed");
2921       g_signal_emit_by_name (button, "selection-changed");
2922     }
2923   else
2924     {
2925       restore_inactive_state (button);
2926     }
2927 
2928   if (priv->active)
2929     priv->active = FALSE;
2930 
2931   update_label_and_image (button);
2932   update_combo_box (button);
2933 
2934   gtk_widget_set_sensitive (priv->combo_box, TRUE);
2935   gtk_widget_hide (priv->dialog);
2936 
2937   if (response == GTK_RESPONSE_ACCEPT ||
2938       response == GTK_RESPONSE_OK)
2939     g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
2940 }
2941 
2942 
2943 /* ************************************************************************** *
2944  *  Public API                                                                *
2945  * ************************************************************************** */
2946 
2947 /**
2948  * gtk_file_chooser_button_new:
2949  * @title: the title of the browse dialog.
2950  * @action: the open mode for the widget.
2951  *
2952  * Creates a new file-selecting button widget.
2953  *
2954  * Returns: a new button widget.
2955  *
2956  * Since: 2.6
2957  **/
2958 GtkWidget *
gtk_file_chooser_button_new(const gchar * title,GtkFileChooserAction action)2959 gtk_file_chooser_button_new (const gchar          *title,
2960 			     GtkFileChooserAction  action)
2961 {
2962   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2963 			action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2964 
2965   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2966 		       "action", action,
2967 		       "title", (title ? title : _(DEFAULT_TITLE)),
2968 		       NULL);
2969 }
2970 
2971 /**
2972  * gtk_file_chooser_button_new_with_backend:
2973  * @title: the title of the browse dialog.
2974  * @action: the open mode for the widget.
2975  * @backend: the name of the #GtkFileSystem backend to use.
2976  *
2977  * Creates a new file-selecting button widget using @backend.
2978  *
2979  * Returns: a new button widget.
2980  *
2981  * Since: 2.6
2982  * Deprecated: 2.14: Use gtk_file_chooser_button_new() instead.
2983  **/
2984 GtkWidget *
gtk_file_chooser_button_new_with_backend(const gchar * title,GtkFileChooserAction action,const gchar * backend)2985 gtk_file_chooser_button_new_with_backend (const gchar          *title,
2986 					  GtkFileChooserAction  action,
2987 					  const gchar          *backend)
2988 {
2989   g_return_val_if_fail (action == GTK_FILE_CHOOSER_ACTION_OPEN ||
2990 			action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, NULL);
2991 
2992   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
2993 		       "action", action,
2994 		       "title", (title ? title : _(DEFAULT_TITLE)),
2995 		       NULL);
2996 }
2997 
2998 /**
2999  * gtk_file_chooser_button_new_with_dialog:
3000  * @dialog: the widget to use as dialog
3001  *
3002  * Creates a #GtkFileChooserButton widget which uses @dialog as its
3003  * file-picking window.
3004  *
3005  * Note that @dialog must be a #GtkDialog (or subclass) which
3006  * implements the #GtkFileChooser interface and must not have
3007  * %GTK_DIALOG_DESTROY_WITH_PARENT set.
3008  *
3009  * Also note that the dialog needs to have its confirmative button
3010  * added with response %GTK_RESPONSE_ACCEPT or %GTK_RESPONSE_OK in
3011  * order for the button to take over the file selected in the dialog.
3012  *
3013  * Returns: a new button widget.
3014  *
3015  * Since: 2.6
3016  **/
3017 GtkWidget *
gtk_file_chooser_button_new_with_dialog(GtkWidget * dialog)3018 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
3019 {
3020   g_return_val_if_fail (GTK_IS_FILE_CHOOSER (dialog) && GTK_IS_DIALOG (dialog), NULL);
3021 
3022   return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
3023 		       "dialog", dialog,
3024 		       NULL);
3025 }
3026 
3027 /**
3028  * gtk_file_chooser_button_set_title:
3029  * @button: the button widget to modify.
3030  * @title: the new browse dialog title.
3031  *
3032  * Modifies the @title of the browse dialog used by @button.
3033  *
3034  * Since: 2.6
3035  **/
3036 void
gtk_file_chooser_button_set_title(GtkFileChooserButton * button,const gchar * title)3037 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
3038 				   const gchar          *title)
3039 {
3040   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3041 
3042   gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
3043   g_object_notify (G_OBJECT (button), "title");
3044 }
3045 
3046 /**
3047  * gtk_file_chooser_button_get_title:
3048  * @button: the button widget to examine.
3049  *
3050  * Retrieves the title of the browse dialog used by @button. The returned value
3051  * should not be modified or freed.
3052  *
3053  * Returns: a pointer to the browse dialog's title.
3054  *
3055  * Since: 2.6
3056  **/
3057 const gchar *
gtk_file_chooser_button_get_title(GtkFileChooserButton * button)3058 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
3059 {
3060   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
3061 
3062   return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
3063 }
3064 
3065 /**
3066  * gtk_file_chooser_button_get_width_chars:
3067  * @button: the button widget to examine.
3068  *
3069  * Retrieves the width in characters of the @button widget's entry and/or label.
3070  *
3071  * Returns: an integer width (in characters) that the button will use to size itself.
3072  *
3073  * Since: 2.6
3074  **/
3075 gint
gtk_file_chooser_button_get_width_chars(GtkFileChooserButton * button)3076 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
3077 {
3078   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
3079 
3080   return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
3081 }
3082 
3083 /**
3084  * gtk_file_chooser_button_set_width_chars:
3085  * @button: the button widget to examine.
3086  * @n_chars: the new width, in characters.
3087  *
3088  * Sets the width (in characters) that @button will use to @n_chars.
3089  *
3090  * Since: 2.6
3091  **/
3092 void
gtk_file_chooser_button_set_width_chars(GtkFileChooserButton * button,gint n_chars)3093 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
3094 					 gint                  n_chars)
3095 {
3096   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3097 
3098   gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
3099   g_object_notify (G_OBJECT (button), "width-chars");
3100 }
3101 
3102 /**
3103  * gtk_file_chooser_button_set_focus_on_click:
3104  * @button: a #GtkFileChooserButton
3105  * @focus_on_click: whether the button grabs focus when clicked with the mouse
3106  *
3107  * Sets whether the button will grab focus when it is clicked with the mouse.
3108  * Making mouse clicks not grab focus is useful in places like toolbars where
3109  * you don't want the keyboard focus removed from the main area of the
3110  * application.
3111  *
3112  * Since: 2.10
3113  **/
3114 void
gtk_file_chooser_button_set_focus_on_click(GtkFileChooserButton * button,gboolean focus_on_click)3115 gtk_file_chooser_button_set_focus_on_click (GtkFileChooserButton *button,
3116 					    gboolean              focus_on_click)
3117 {
3118   GtkFileChooserButtonPrivate *priv;
3119 
3120   g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
3121 
3122   priv = button->priv;
3123 
3124   focus_on_click = focus_on_click != FALSE;
3125 
3126   if (priv->focus_on_click != focus_on_click)
3127     {
3128       priv->focus_on_click = focus_on_click;
3129       gtk_button_set_focus_on_click (GTK_BUTTON (priv->button), focus_on_click);
3130       gtk_combo_box_set_focus_on_click (GTK_COMBO_BOX (priv->combo_box), focus_on_click);
3131 
3132       g_object_notify (G_OBJECT (button), "focus-on-click");
3133     }
3134 }
3135 
3136 /**
3137  * gtk_file_chooser_button_get_focus_on_click:
3138  * @button: a #GtkFileChooserButton
3139  *
3140  * Returns whether the button grabs focus when it is clicked with the mouse.
3141  * See gtk_file_chooser_button_set_focus_on_click().
3142  *
3143  * Return value: %TRUE if the button grabs focus when it is clicked with
3144  *               the mouse.
3145  *
3146  * Since: 2.10
3147  **/
3148 gboolean
gtk_file_chooser_button_get_focus_on_click(GtkFileChooserButton * button)3149 gtk_file_chooser_button_get_focus_on_click (GtkFileChooserButton *button)
3150 {
3151   g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE);
3152 
3153   return button->priv->focus_on_click;
3154 }
3155 
3156 #define __GTK_FILE_CHOOSER_BUTTON_C__
3157 #include "gtkaliasdef.c"
3158