gtkfilechooserbutton.c 80.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */

/* GTK+: gtkfilechooserbutton.c
 * 
 * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardon's avatar
Javier Jardon committed
18
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 20
 */

21
#include "config.h"
22 23 24

#include <sys/types.h>
#include <sys/stat.h>
25
#ifdef HAVE_UNISTD_H
26
#include <unistd.h>
27
#endif
28 29 30 31

#include <string.h>

#include "gtkintl.h"
32
#include "gtkbutton.h"
Matthias Clasen's avatar
Matthias Clasen committed
33
#include "gtkcelllayout.h"
Matthias Clasen's avatar
Matthias Clasen committed
34
#include "gtkcellrenderertext.h"
Matthias Clasen's avatar
Matthias Clasen committed
35
#include "gtkcellrendererpixbuf.h"
36
#include "gtkcombobox.h"
37 38
#include "gtkdnd.h"
#include "gtkicontheme.h"
39
#include "gtkiconfactory.h"
40 41
#include "gtkimage.h"
#include "gtklabel.h"
42
#include "gtkliststore.h"
43
#include "gtkstock.h"
44
#include "gtktreemodelfilter.h"
Javier Jardon's avatar
Javier Jardon committed
45
#include "gtkseparator.h"
46 47 48
#include "gtkfilechooserdialog.h"
#include "gtkfilechooserprivate.h"
#include "gtkfilechooserutils.h"
49
#include "gtkmarshalers.h"
50 51 52

#include "gtkfilechooserbutton.h"

53 54
#include "gtkorientable.h"

55
#include "gtktypebuiltins.h"
56
#include "gtkprivate.h"
57
#include "gtksettings.h"
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

/**
 * SECTION:gtkfilechooserbutton
 * @Short_description: A button to launch a file selection dialog
 * @Title: GtkFileChooserButton
 * @See_also:#GtkFileChooserDialog
 *
 * The #GtkFileChooserButton is a widget that lets the user select a
 * file.  It implements the #GtkFileChooser interface.  Visually, it is a
 * file name with a button to bring up a #GtkFileChooserDialog.
 * The user can then use that dialog to change the file associated with
 * that button.  This widget does not support setting the
 * #GtkFileChooser:select-multiple property to %TRUE.
 *
 * <example>
 * <title>Create a button to let the user select a file in /etc</title>
 * <programlisting>
 * {
 *   GtkWidget *button;
 *
 *   button = gtk_file_chooser_button_new (_("Select a file"),
 *                                         GTK_FILE_CHOOSER_ACTION_OPEN);
 *   gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (button),
 *                                        "/etc");
 * }
 * </programlisting>
 * </example>
 *
 * The #GtkFileChooserButton supports the #GtkFileChooserAction<!-- -->s
 * %GTK_FILE_CHOOSER_ACTION_OPEN and %GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER.
 *
 * <important>
 * The #GtkFileChooserButton will ellipsize the label,
 * and thus will thus request little horizontal space.  To give the button
 * more space, you should call gtk_widget_get_preferred_size(),
 * gtk_file_chooser_button_set_width_chars(), or pack the button in
 * such a way that other interface elements give space to the widget.
 * </important>
 */


100 101 102 103
/* **************** *
 *  Private Macros  *
 * **************** */

104
#define DEFAULT_TITLE		N_("Select a File")
105 106
#define DESKTOP_DISPLAY_NAME	N_("Desktop")
#define FALLBACK_DISPLAY_NAME	N_("(None)")
107
#define FALLBACK_ICON_NAME	"stock_unknown"
108
#define FALLBACK_ICON_SIZE	16
109

110 111 112 113 114 115 116 117 118 119 120

/* ********************** *
 *  Private Enumerations  *
 * ********************** */

/* Property IDs */
enum
{
  PROP_0,

  PROP_DIALOG,
121
  PROP_FOCUS_ON_CLICK,
122
  PROP_TITLE,
123
  PROP_WIDTH_CHARS
124 125
};

126 127 128 129 130 131 132
/* Signals */
enum
{
  FILE_SET,
  LAST_SIGNAL
};

133 134 135 136 137 138 139
/* TreeModel Columns */
enum
{
  ICON_COLUMN,
  DISPLAY_NAME_COLUMN,
  TYPE_COLUMN,
  DATA_COLUMN,
140
  IS_FOLDER_COLUMN,
141
  CANCELLABLE_COLUMN,
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
  NUM_COLUMNS
};

/* TreeModel Row Types */
typedef enum
{
  ROW_TYPE_SPECIAL,
  ROW_TYPE_VOLUME,
  ROW_TYPE_SHORTCUT,
  ROW_TYPE_BOOKMARK_SEPARATOR,
  ROW_TYPE_BOOKMARK,
  ROW_TYPE_CURRENT_FOLDER_SEPARATOR,
  ROW_TYPE_CURRENT_FOLDER,
  ROW_TYPE_OTHER_SEPARATOR,
  ROW_TYPE_OTHER,

  ROW_TYPE_INVALID = -1
}
RowType;

162 163 164 165 166 167 168 169 170

/* ******************** *
 *  Private Structures  *
 * ******************** */

struct _GtkFileChooserButtonPrivate
{
  GtkWidget *dialog;
  GtkWidget *button;
171 172
  GtkWidget *image;
  GtkWidget *label;
173 174 175 176 177 178
  GtkWidget *combo_box;
  GtkCellRenderer *icon_cell;
  GtkCellRenderer *name_cell;

  GtkTreeModel *model;
  GtkTreeModel *filter_model;
179

180
  GtkFileSystem *fs;
181
  GFile *old_file;
182 183

  gulong combo_box_changed_id;
184 185 186
  gulong dialog_file_activated_id;
  gulong dialog_folder_changed_id;
  gulong dialog_selection_changed_id;
187 188 189
  gulong fs_volumes_changed_id;
  gulong fs_bookmarks_changed_id;

190 191 192
  GCancellable *dnd_select_folder_cancellable;
  GCancellable *update_button_cancellable;
  GSList *change_icon_theme_cancellables;
193

194
  gint icon_size;
195

196 197 198 199
  guint8 n_special;
  guint8 n_volumes;
  guint8 n_shortcuts;
  guint8 n_bookmarks;
200 201 202 203
  guint  has_bookmark_separator       : 1;
  guint  has_current_folder_separator : 1;
  guint  has_current_folder           : 1;
  guint  has_other_separator          : 1;
204

205
  /* Used for hiding/showing the dialog when the button is hidden */
206
  guint  active                       : 1;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
207

208
  guint  focus_on_click               : 1;
209 210 211 212 213 214 215 216 217
};


/* ************* *
 *  DnD Support  *
 * ************* */

enum
{
218 219
  TEXT_PLAIN,
  TEXT_URI_LIST
220 221
};

222

223 224 225 226
/* ********************* *
 *  Function Prototypes  *
 * ********************* */

227 228 229
/* GtkFileChooserIface Functions */
static void     gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface);
static gboolean gtk_file_chooser_button_add_shortcut_folder     (GtkFileChooser      *chooser,
230
								 GFile               *file,
231 232
								 GError             **error);
static gboolean gtk_file_chooser_button_remove_shortcut_folder  (GtkFileChooser      *chooser,
233
								 GFile               *file,
234 235
								 GError             **error);

236
/* GObject Functions */
237 238 239
static GObject *gtk_file_chooser_button_constructor        (GType             type,
							    guint             n_params,
							    GObjectConstructParam *params);
240
static void     gtk_file_chooser_button_set_property       (GObject          *object,
241
							    guint             param_id,
242 243 244
							    const GValue     *value,
							    GParamSpec       *pspec);
static void     gtk_file_chooser_button_get_property       (GObject          *object,
245
							    guint             param_id,
246 247
							    GValue           *value,
							    GParamSpec       *pspec);
248
static void     gtk_file_chooser_button_finalize           (GObject          *object);
249 250

/* GtkWidget Functions */
251
static void     gtk_file_chooser_button_destroy            (GtkWidget        *widget);
252 253 254 255 256
static void     gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
							    GdkDragContext   *context,
							    gint              x,
							    gint              y,
							    GtkSelectionData *data,
257
							    guint             type,
258 259
							    guint             drag_time);
static void     gtk_file_chooser_button_show_all           (GtkWidget        *widget);
260 261
static void     gtk_file_chooser_button_show               (GtkWidget        *widget);
static void     gtk_file_chooser_button_hide               (GtkWidget        *widget);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
262
static void     gtk_file_chooser_button_map                (GtkWidget        *widget);
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
263 264
static gboolean gtk_file_chooser_button_mnemonic_activate  (GtkWidget        *widget,
							    gboolean          group_cycling);
265
static void     gtk_file_chooser_button_style_updated      (GtkWidget        *widget);
266 267
static void     gtk_file_chooser_button_screen_changed     (GtkWidget        *widget,
							    GdkScreen        *old_screen);
268 269

/* Utility Functions */
270
static GtkIconTheme *get_icon_theme               (GtkWidget            *widget);
271 272
static void          set_info_for_file_at_iter         (GtkFileChooserButton *fs,
							GFile                *file,
273
							GtkTreeIter          *iter);
274 275 276 277 278 279 280 281 282 283 284 285

static gint          model_get_type_position      (GtkFileChooserButton *button,
						   RowType               row_type);
static void          model_free_row_data          (GtkFileChooserButton *button,
						   GtkTreeIter          *iter);
static inline void   model_add_special            (GtkFileChooserButton *button);
static inline void   model_add_other              (GtkFileChooserButton *button);
static void          model_add_volumes            (GtkFileChooserButton *button,
						   GSList               *volumes);
static void          model_add_bookmarks          (GtkFileChooserButton *button,
						   GSList               *bookmarks);
static void          model_update_current_folder  (GtkFileChooserButton *button,
286
						   GFile                *file);
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
static void          model_remove_rows            (GtkFileChooserButton *button,
						   gint                  pos,
						   gint                  n_rows);

static gboolean      filter_model_visible_func    (GtkTreeModel         *model,
						   GtkTreeIter          *iter,
						   gpointer              user_data);

static gboolean      combo_box_row_separator_func (GtkTreeModel         *model,
						   GtkTreeIter          *iter,
						   gpointer              user_data);
static void          name_cell_data_func          (GtkCellLayout        *layout,
						   GtkCellRenderer      *cell,
						   GtkTreeModel         *model,
						   GtkTreeIter          *iter,
						   gpointer              user_data);
static void          open_dialog                  (GtkFileChooserButton *button);
static void          update_combo_box             (GtkFileChooserButton *button);
static void          update_label_and_image       (GtkFileChooserButton *button);

/* Child Object Callbacks */
static void     fs_volumes_changed_cb            (GtkFileSystem  *fs,
						  gpointer        user_data);
static void     fs_bookmarks_changed_cb          (GtkFileSystem  *fs,
						  gpointer        user_data);

static void     combo_box_changed_cb             (GtkComboBox    *combo_box,
						  gpointer        user_data);

static void     button_clicked_cb                (GtkButton      *real_button,
						  gpointer        user_data);

static void     dialog_update_preview_cb         (GtkFileChooser *dialog,
						  gpointer        user_data);
static void     dialog_selection_changed_cb      (GtkFileChooser *dialog,
						  gpointer        user_data);
static void     dialog_file_activated_cb         (GtkFileChooser *dialog,
						  gpointer        user_data);
static void     dialog_current_folder_changed_cb (GtkFileChooser *dialog,
						  gpointer        user_data);
static void     dialog_notify_cb                 (GObject        *dialog,
						  GParamSpec     *pspec,
						  gpointer        user_data);
static gboolean dialog_delete_event_cb           (GtkWidget      *dialog,
						  GdkEvent       *event,
						  gpointer        user_data);
static void     dialog_response_cb               (GtkDialog      *dialog,
						  gint            response,
						  gpointer        user_data);
336

337
static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 };
338 339 340 341 342

/* ******************* *
 *  GType Declaration  *
 * ******************* */

343
G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_BOX, { \
344
    G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
Matthias Clasen's avatar
Matthias Clasen committed
345
})
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360


/* ***************** *
 *  GType Functions  *
 * ***************** */

static void
gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
{
  GObjectClass *gobject_class;
  GtkWidgetClass *widget_class;

  gobject_class = G_OBJECT_CLASS (class);
  widget_class = GTK_WIDGET_CLASS (class);

361
  gobject_class->constructor = gtk_file_chooser_button_constructor;
362 363
  gobject_class->set_property = gtk_file_chooser_button_set_property;
  gobject_class->get_property = gtk_file_chooser_button_get_property;
364
  gobject_class->finalize = gtk_file_chooser_button_finalize;
365

366
  widget_class->destroy = gtk_file_chooser_button_destroy;
367 368
  widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
  widget_class->show_all = gtk_file_chooser_button_show_all;
369 370
  widget_class->show = gtk_file_chooser_button_show;
  widget_class->hide = gtk_file_chooser_button_hide;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
371
  widget_class->map = gtk_file_chooser_button_map;
372
  widget_class->style_updated = gtk_file_chooser_button_style_updated;
373
  widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
374
  widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
375

376
  /**
377
   * GtkFileChooserButton::file-set:
378 379 380
   * @widget: the object which received the signal.
   *
   * The ::file-set signal is emitted when the user selects a file.
381
   *
382
   * Note that this signal is only emitted when the <emphasis>user</emphasis>
383
   * changes the file.
384 385 386 387 388 389
   *
   * Since: 2.12
   */
  file_chooser_button_signals[FILE_SET] =
    g_signal_new (I_("file-set"),
		  G_TYPE_FROM_CLASS (gobject_class),
390
		  G_SIGNAL_RUN_FIRST,
391 392 393 394
		  G_STRUCT_OFFSET (GtkFileChooserButtonClass, file_set),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
395

Matthias Clasen's avatar
Matthias Clasen committed
396 397 398 399 400 401 402
  /**
   * GtkFileChooserButton:dialog:
   * 
   * Instance of the #GtkFileChooserDialog associated with the button.
   *
   * Since: 2.6
   */
403 404 405 406
  g_object_class_install_property (gobject_class, PROP_DIALOG,
				   g_param_spec_object ("dialog",
							P_("Dialog"),
							P_("The file chooser dialog to use."),
407
							GTK_TYPE_FILE_CHOOSER,
408
							(GTK_PARAM_WRITABLE |
409
							 G_PARAM_CONSTRUCT_ONLY)));
Matthias Clasen's avatar
Matthias Clasen committed
410

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
  /**
   * GtkFileChooserButton:focus-on-click:
   * 
   * Whether the #GtkFileChooserButton button grabs focus when it is clicked
   * with the mouse.
   *
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
                                   PROP_FOCUS_ON_CLICK,
                                   g_param_spec_boolean ("focus-on-click",
							 P_("Focus on click"),
							 P_("Whether the button grabs focus when it is clicked with the mouse"),
							 TRUE,
							 GTK_PARAM_READWRITE));
  
Matthias Clasen's avatar
Matthias Clasen committed
427 428 429 430 431 432 433
  /**
   * GtkFileChooserButton:title:
   * 
   * Title to put on the #GtkFileChooserDialog associated with the button.
   *
   * Since: 2.6
   */
434 435 436 437
  g_object_class_install_property (gobject_class, PROP_TITLE,
				   g_param_spec_string ("title",
							P_("Title"),
							P_("The title of the file chooser dialog."),
438
							_(DEFAULT_TITLE),
439
							GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
440 441

  /**
Matthias Clasen's avatar
Matthias Clasen committed
442
   * GtkFileChooserButton:width-chars:
Matthias Clasen's avatar
Matthias Clasen committed
443 444 445 446 447
   * 
   * The width of the entry and label inside the button, in characters.
   *
   * Since: 2.6
   */
448 449 450 451 452
  g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
				   g_param_spec_int ("width-chars",
						     P_("Width In Characters"),
						     P_("The desired width of the button widget, in characters."),
						     -1, G_MAXINT, -1,
453
						     GTK_PARAM_READWRITE));
454 455 456 457 458 459 460 461 462 463

  _gtk_file_chooser_install_properties (gobject_class);

  g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
}

static void
gtk_file_chooser_button_init (GtkFileChooserButton *button)
{
  GtkFileChooserButtonPrivate *priv;
464
  GtkWidget *box, *image, *sep;
465
  GtkTargetList *target_list;
466

467 468 469
  priv = button->priv = G_TYPE_INSTANCE_GET_PRIVATE (button,
                                                     GTK_TYPE_FILE_CHOOSER_BUTTON,
                                                     GtkFileChooserButtonPrivate);
470

471
  priv->icon_size = FALLBACK_ICON_SIZE;
472
  priv->focus_on_click = TRUE;
473

474 475
  gtk_widget_push_composite_child ();

476
  /* Button */
477
  priv->button = gtk_button_new ();
478 479 480 481
  g_signal_connect (priv->button, "clicked",
                    G_CALLBACK (button_clicked_cb), button);
  gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0);
  gtk_widget_set_halign (priv->button, GTK_ALIGN_FILL);
482 483
  gtk_widget_show (priv->button);

484
  box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
485 486
  gtk_container_add (GTK_CONTAINER (priv->button), box);
  gtk_widget_show (box);
487

488 489 490
  priv->image = gtk_image_new ();
  gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
  gtk_widget_show (priv->image);
491

492 493
  priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
  gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
494 495
  gtk_widget_set_halign (priv->label, GTK_ALIGN_START);
  gtk_widget_set_valign (priv->label, GTK_ALIGN_CENTER);
496 497
  gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 0);
  //gtk_container_add (GTK_CONTAINER (box), priv->label);
498 499
  gtk_widget_show (priv->label);

500
  sep = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
501
  gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
502 503
  gtk_widget_show (sep);

504
  image = gtk_image_new_from_stock (GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU);
505
  gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
506 507
  gtk_widget_show (image);

508 509 510 511 512 513 514
  /* Combo Box */
  /* Keep in sync with columns enum, line 88 */
  priv->model =
    GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
					GDK_TYPE_PIXBUF, /* Icon */
					G_TYPE_STRING,	 /* Display Name */
					G_TYPE_CHAR,	 /* Row Type */
515 516
					G_TYPE_POINTER	 /* Volume || Path */,
					G_TYPE_BOOLEAN   /* Is Folder? */,
517
					G_TYPE_POINTER	 /* cancellable */));
518 519 520 521 522

  priv->combo_box = gtk_combo_box_new ();
  priv->combo_box_changed_id =
    g_signal_connect (priv->combo_box, "changed",
		      G_CALLBACK (combo_box_changed_cb), button);
523 524
  gtk_box_pack_start (GTK_BOX (button), priv->combo_box, TRUE, TRUE, 0);
  gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL);
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540

  priv->icon_cell = gtk_cell_renderer_pixbuf_new ();
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
			      priv->icon_cell, FALSE);
  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
				 priv->icon_cell, "pixbuf", ICON_COLUMN);

  priv->name_cell = gtk_cell_renderer_text_new ();
  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->combo_box),
			      priv->name_cell, TRUE);
  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (priv->combo_box),
				 priv->name_cell, "text", DISPLAY_NAME_COLUMN);
  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->combo_box),
				      priv->name_cell, name_cell_data_func,
				      NULL, NULL);

541 542 543 544
  gtk_widget_pop_composite_child ();

  /* DnD */
  gtk_drag_dest_set (GTK_WIDGET (button),
545
                     (GTK_DEST_DEFAULT_ALL),
Matthias Clasen's avatar
Matthias Clasen committed
546
		     NULL, 0,
547
		     GDK_ACTION_COPY);
548 549 550 551 552
  target_list = gtk_target_list_new (NULL, 0);
  gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
  gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
  gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
  gtk_target_list_unref (target_list);
553 554 555
}


556 557 558 559 560 561 562 563 564 565 566 567 568
/* ******************************* *
 *  GtkFileChooserIface Functions  *
 * ******************************* */
static void
gtk_file_chooser_button_file_chooser_iface_init (GtkFileChooserIface *iface)
{
  _gtk_file_chooser_delegate_iface_init (iface);

  iface->add_shortcut_folder = gtk_file_chooser_button_add_shortcut_folder;
  iface->remove_shortcut_folder = gtk_file_chooser_button_remove_shortcut_folder;
}

static gboolean
569 570 571
gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser  *chooser,
					     GFile           *file,
					     GError         **error)
572 573 574 575 576 577
{
  GtkFileChooser *delegate;
  gboolean retval;

  delegate = g_object_get_qdata (G_OBJECT (chooser),
				 GTK_FILE_CHOOSER_DELEGATE_QUARK);
578
  retval = _gtk_file_chooser_add_shortcut_folder (delegate, file, error);
579 580 581

  if (retval)
    {
582 583
      GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
      GtkFileChooserButtonPrivate *priv = button->priv;
584 585 586
      GtkTreeIter iter;
      gint pos;

587
      pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
588 589 590 591
      pos += priv->n_shortcuts;

      gtk_list_store_insert (GTK_LIST_STORE (priv->model), &iter, pos);
      gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
592 593
			  ICON_COLUMN, NULL,
			  DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
594
			  TYPE_COLUMN, ROW_TYPE_SHORTCUT,
595
			  DATA_COLUMN, g_object_ref (file),
596
			  IS_FOLDER_COLUMN, FALSE,
597
			  -1);
598
      set_info_for_file_at_iter (button, file, &iter);
599 600 601 602 603 604 605 606 607
      priv->n_shortcuts++;

      gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
    }

  return retval;
}

static gboolean
608 609 610
gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser  *chooser,
						GFile           *file,
						GError         **error)
611 612 613 614 615 616 617
{
  GtkFileChooser *delegate;
  gboolean retval;

  delegate = g_object_get_qdata (G_OBJECT (chooser),
				 GTK_FILE_CHOOSER_DELEGATE_QUARK);

618
  retval = _gtk_file_chooser_remove_shortcut_folder (delegate, file, error);
619 620 621

  if (retval)
    {
622 623
      GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
      GtkFileChooserButtonPrivate *priv = button->priv;
624 625 626 627
      GtkTreeIter iter;
      gint pos;
      gchar type;

628
      pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
629
      gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
630 631 632 633 634 635 636 637 638 639 640

      do
	{
	  gpointer data;

	  gtk_tree_model_get (priv->model, &iter,
			      TYPE_COLUMN, &type,
			      DATA_COLUMN, &data,
			      -1);

	  if (type == ROW_TYPE_SHORTCUT &&
641
	      data && g_file_equal (data, file))
642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
	    {
	      model_free_row_data (GTK_FILE_CHOOSER_BUTTON (chooser), &iter);
	      gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
	      priv->n_shortcuts--;
	      gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
	      update_combo_box (GTK_FILE_CHOOSER_BUTTON (chooser));
	      break;
	    }
	}
      while (type == ROW_TYPE_SHORTCUT &&
	     gtk_tree_model_iter_next (priv->model, &iter));
    }

  return retval;
}


659 660 661 662
/* ******************* *
 *  GObject Functions  *
 * ******************* */

663 664 665 666 667 668
static GObject *
gtk_file_chooser_button_constructor (GType                  type,
				     guint                  n_params,
				     GObjectConstructParam *params)
{
  GObject *object;
669
  GtkFileChooserButton *button;
670
  GtkFileChooserButtonPrivate *priv;
671
  GSList *list;
672

673 674 675
  object = G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor (type,
									       n_params,
									       params);
676 677
  button = GTK_FILE_CHOOSER_BUTTON (object);
  priv = button->priv;
678 679 680

  if (!priv->dialog)
    {
681 682 683 684 685 686 687
      priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
						  GTK_FILE_CHOOSER_ACTION_OPEN,
						  GTK_STOCK_CANCEL,
						  GTK_RESPONSE_CANCEL,
						  GTK_STOCK_OPEN,
						  GTK_RESPONSE_ACCEPT,
						  NULL);
688 689 690 691 692 693 694 695

      gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
				       GTK_RESPONSE_ACCEPT);
      gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
					       GTK_RESPONSE_ACCEPT,
					       GTK_RESPONSE_CANCEL,
					       -1);

696 697
      gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
    }
698
  else if (!gtk_window_get_title (GTK_WINDOW (priv->dialog)))
699 700 701
    {
      gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
    }
702

703
  g_signal_connect (priv->dialog, "delete-event",
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
		    G_CALLBACK (dialog_delete_event_cb), object);
  g_signal_connect (priv->dialog, "response",
		    G_CALLBACK (dialog_response_cb), object);

  /* This is used, instead of the standard delegate, to ensure that signals are only
   * delegated when the OK button is pressed. */
  g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
  priv->dialog_folder_changed_id =
    g_signal_connect (priv->dialog, "current-folder-changed",
		      G_CALLBACK (dialog_current_folder_changed_cb), object);
  priv->dialog_file_activated_id =
    g_signal_connect (priv->dialog, "file-activated",
		      G_CALLBACK (dialog_file_activated_cb), object);
  priv->dialog_selection_changed_id =
    g_signal_connect (priv->dialog, "selection-changed",
		      G_CALLBACK (dialog_selection_changed_cb), object);
  g_signal_connect (priv->dialog, "update-preview",
		    G_CALLBACK (dialog_update_preview_cb), object);
  g_signal_connect (priv->dialog, "notify",
		    G_CALLBACK (dialog_notify_cb), object);
  g_object_add_weak_pointer (G_OBJECT (priv->dialog),
725
			     (gpointer) (&priv->dialog));
726

727 728 729
  priv->fs =
    g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));

730
  model_add_special (button);
731

732
  list = _gtk_file_system_list_volumes (priv->fs);
733
  model_add_volumes (button, list);
734 735
  g_slist_free (list);

736
  list = _gtk_file_system_list_bookmarks (priv->fs);
737
  model_add_bookmarks (button, list);
738 739
  g_slist_foreach (list, (GFunc) g_object_unref, NULL);
  g_slist_free (list);
740

741
  model_add_other (button);
742 743 744 745 746 747 748 749 750 751 752

  priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
  gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
					  filter_model_visible_func,
					  object, NULL);

  gtk_combo_box_set_model (GTK_COMBO_BOX (priv->combo_box), priv->filter_model);
  gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (priv->combo_box),
					combo_box_row_separator_func,
					NULL, NULL);

753 754 755 756 757 758
  /* set up the action for a user-provided dialog, this also updates
   * the label, image and combobox
   */
  g_object_set (object, 
		"action", gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)),
		NULL);
759 760 761 762 763 764 765 766

  priv->fs_volumes_changed_id =
    g_signal_connect (priv->fs, "volumes-changed",
		      G_CALLBACK (fs_volumes_changed_cb), object);
  priv->fs_bookmarks_changed_id =
    g_signal_connect (priv->fs, "bookmarks-changed",
		      G_CALLBACK (fs_bookmarks_changed_cb), object);

767 768
  return object;
}
769 770 771

static void
gtk_file_chooser_button_set_property (GObject      *object,
772
				      guint         param_id,
773 774 775
				      const GValue *value,
				      GParamSpec   *pspec)
{
776 777
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
778

779
  switch (param_id)
780 781
    {
    case PROP_DIALOG:
782 783
      /* Construct-only */
      priv->dialog = g_value_get_object (value);
784
      break;
785 786 787
    case PROP_FOCUS_ON_CLICK:
      gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
788 789 790 791
    case PROP_WIDTH_CHARS:
      gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
					       g_value_get_int (value));
      break;
792
    case GTK_FILE_CHOOSER_PROP_ACTION:
793 794 795 796 797 798 799 800 801 802
      switch (g_value_get_enum (value))
	{
	case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
	case GTK_FILE_CHOOSER_ACTION_SAVE:
	  {
	    GEnumClass *eclass;
	    GEnumValue *eval;

	    eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
	    eval = g_enum_get_value (eclass, g_value_get_enum (value));
803
	    g_warning ("%s: Choosers of type `%s' do not support `%s'.",
804 805
		       G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);

806
	    g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
807 808 809
	  }
	  break;
	}
810

811 812
      g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
      update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
      update_combo_box (GTK_FILE_CHOOSER_BUTTON (object));

      switch (g_value_get_enum (value))
	{
	case GTK_FILE_CHOOSER_ACTION_OPEN:
	  gtk_widget_hide (priv->combo_box);
	  gtk_widget_show (priv->button);
	  break;
	case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
	  gtk_widget_hide (priv->button);
	  gtk_widget_show (priv->combo_box);
	  break;
	default:
	  g_assert_not_reached ();
	  break;
	}
829 830 831 832 833 834 835 836 837
      break;

    case PROP_TITLE:
    case GTK_FILE_CHOOSER_PROP_FILTER:
    case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
    case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
    case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
    case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
    case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
838
    case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
839
    case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
840 841 842
      g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
      break;

843 844 845 846 847 848
    case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
      g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
      fs_volumes_changed_cb (priv->fs, button);
      fs_bookmarks_changed_cb (priv->fs, button);
      break;

849 850 851 852 853
    case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
      g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
		 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
      break;
    default:
854
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
855 856 857 858 859 860
      break;
    }
}

static void
gtk_file_chooser_button_get_property (GObject    *object,
861
				      guint       param_id,
862 863 864
				      GValue     *value,
				      GParamSpec *pspec)
{
865 866
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
867 868

  switch (param_id)
869
    {
870 871
    case PROP_WIDTH_CHARS:
      g_value_set_int (value,
872
		       gtk_label_get_width_chars (GTK_LABEL (priv->label)));
873
      break;
874 875 876 877
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value,
                           gtk_file_chooser_button_get_focus_on_click (button));
      break;
878 879 880 881 882 883 884 885 886 887 888

    case PROP_TITLE:
    case GTK_FILE_CHOOSER_PROP_ACTION:
    case GTK_FILE_CHOOSER_PROP_FILTER:
    case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
    case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
    case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
    case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
    case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
    case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
    case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
889
    case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
890
    case GTK_FILE_CHOOSER_PROP_CREATE_FOLDERS:
891 892 893 894
      g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
      break;

    default:
895
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
896 897 898 899
      break;
    }
}

900 901 902
static void
gtk_file_chooser_button_finalize (GObject *object)
{
903 904
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
905

906 907
  if (priv->old_file)
    g_object_unref (priv->old_file);
908

909
  G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize (object);
910
}
911 912

/* ********************* *
913
 *  GtkWidget Functions  *
914 915 916
 * ********************* */

static void
917
gtk_file_chooser_button_destroy (GtkWidget *widget)
918
{
919
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
920
  GtkFileChooserButtonPrivate *priv = button->priv;
921 922
  GtkTreeIter iter;
  GSList *l;
923 924

  if (priv->dialog != NULL)
925 926 927 928 929
    {
      gtk_widget_destroy (priv->dialog);
      priv->dialog = NULL;
    }

930
  if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) do
931 932 933 934 935
    {
      model_free_row_data (button, &iter);
    }
  while (gtk_tree_model_iter_next (priv->model, &iter));

936
  if (priv->dnd_select_folder_cancellable)
937
    {
938 939
      g_cancellable_cancel (priv->dnd_select_folder_cancellable);
      priv->dnd_select_folder_cancellable = NULL;
940 941
    }

942
  if (priv->update_button_cancellable)
943
    {
944 945
      g_cancellable_cancel (priv->update_button_cancellable);
      priv->update_button_cancellable = NULL;
946 947
    }

948
  if (priv->change_icon_theme_cancellables)
949
    {
950
      for (l = priv->change_icon_theme_cancellables; l; l = l->next)
951
        {
952 953
	  GCancellable *cancellable = G_CANCELLABLE (l->data);
	  g_cancellable_cancel (cancellable);
954
        }
955 956
      g_slist_free (priv->change_icon_theme_cancellables);
      priv->change_icon_theme_cancellables = NULL;
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977
    }

  if (priv->model)
    {
      g_object_unref (priv->model);
      priv->model = NULL;
    }

  if (priv->filter_model)
    {
      g_object_unref (priv->filter_model);
      priv->filter_model = NULL;
    }

  if (priv->fs)
    {
      g_signal_handler_disconnect (priv->fs, priv->fs_volumes_changed_id);
      g_signal_handler_disconnect (priv->fs, priv->fs_bookmarks_changed_id);
      g_object_unref (priv->fs);
      priv->fs = NULL;
    }
978

979
  GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->destroy (widget);
980 981
}

982 983
struct DndSelectFolderData
{
984
  GtkFileSystem *file_system;
985 986
  GtkFileChooserButton *button;
  GtkFileChooserAction action;
987
  GFile *file;
988 989 990 991 992 993
  gchar **uris;
  guint i;
  gboolean selected;
};

static void
994 995 996 997
dnd_select_folder_get_info_cb (GCancellable *cancellable,
			       GFileInfo    *info,
			       const GError *error,
			       gpointer      user_data)
998
{
999
  gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1000 1001
  struct DndSelectFolderData *data = user_data;

1002
  if (cancellable != data->button->priv->dnd_select_folder_cancellable)
1003 1004
    {
      g_object_unref (data->button);
1005
      g_object_unref (data->file);
1006 1007 1008
      g_strfreev (data->uris);
      g_free (data);

1009
      g_object_unref (cancellable);
1010 1011 1012
      return;
    }

1013
  data->button->priv->dnd_select_folder_cancellable = NULL;
1014 1015 1016

  if (!cancelled && !error && info != NULL)
    {
1017 1018
      gboolean is_folder;

1019
      is_folder = _gtk_file_info_consider_as_directory (info);
1020 1021 1022 1023

      data->selected =
	(((data->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER && is_folder) ||
	  (data->action == GTK_FILE_CHOOSER_ACTION_OPEN && !is_folder)) &&
1024 1025
	 gtk_file_chooser_select_file (GTK_FILE_CHOOSER (data->button->priv->dialog),
				       data->file, NULL));
1026 1027 1028 1029 1030 1031
    }
  else
    data->selected = FALSE;

  if (data->selected || data->uris[++data->i] == NULL)
    {
1032 1033
      g_signal_emit (data->button, file_chooser_button_signals[FILE_SET], 0);

1034
      g_object_unref (data->button);
1035
      g_object_unref (data->file);
1036 1037 1038
      g_strfreev (data->uris);
      g_free (data);

1039
      g_object_unref (cancellable);
1040 1041 1042
      return;
    }

1043 1044
  if (data->file)
    g_object_unref (data->file);
1045

1046
  data->file = g_file_new_for_uri (data->uris[data->i]);
1047

1048
  data->button->priv->dnd_select_folder_cancellable =
1049 1050 1051
    _gtk_file_system_get_info (data->file_system, data->file,
			       "standard::type",
			       dnd_select_folder_get_info_cb, user_data);
1052

1053
  g_object_unref (cancellable);
1054 1055
}

1056 1057 1058 1059 1060 1061
static void
gtk_file_chooser_button_drag_data_received (GtkWidget	     *widget,
					    GdkDragContext   *context,
					    gint	      x,
					    gint	      y,
					    GtkSelectionData *data,
1062
					    guint	      type,
1063 1064
					    guint	      drag_time)
{
1065 1066
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
1067
  GFile *file;
1068
  gchar *text;
1069 1070

  if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
1071 1072 1073 1074 1075
    GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received (widget,
										 context,
										 x, y,
										 data, type,
										 drag_time);
1076

1077
  if (widget == NULL || context == NULL || data == NULL || gtk_selection_data_get_length (data) < 0)
1078 1079
    return;

1080
  switch (type)
1081 1082 1083 1084
    {
    case TEXT_URI_LIST:
      {
	gchar **uris;
1085
	struct DndSelectFolderData *info;
1086

Matthias Clasen's avatar
Matthias Clasen committed
1087
	uris = gtk_selection_data_get_uris (data);
1088
	
1089 1090 1091
	if (uris == NULL)
	  break;

1092 1093 1094 1095 1096
	info = g_new0 (struct DndSelectFolderData, 1);
	info->button = g_object_ref (button);
	info->i = 0;
	info->uris = uris;
	info->selected = FALSE;
1097
	info->file_system = priv->fs;
1098
	g_object_get (priv->dialog, "action", &info->action, NULL);
1099

1100
	info->file = g_file_new_for_uri (info->uris[info->i]);
1101

1102 1103
	if (priv->dnd_select_folder_cancellable)
	  g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1104

1105
	priv->dnd_select_folder_cancellable =
1106 1107 1108
	  _gtk_file_system_get_info (priv->fs, info->file,
				     "standard::type",
				     dnd_select_folder_get_info_cb, info);
1109 1110 1111 1112
      }
      break;

    case TEXT_PLAIN:
1113
      text = (char*) gtk_selection_data_get_text (data);
1114
      file = g_file_new_for_uri (text);
1115 1116
      gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file,
				    NULL);
1117 1118
      g_object_unref (file);
      g_free (text);
1119
      g_signal_emit (button, file_chooser_button_signals[FILE_SET], 0);
1120 1121 1122
      break;

    default:
1123 1124 1125
      break;
    }

1126
  gtk_drag_finish (context, TRUE, FALSE, drag_time);
1127 1128 1129 1130 1131 1132 1133 1134
}

static void
gtk_file_chooser_button_show_all (GtkWidget *widget)
{
  gtk_widget_show (widget);
}

1135 1136 1137
static void
gtk_file_chooser_button_show (GtkWidget *widget)
{
1138 1139
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
1140 1141

  if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
1142
    GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show (widget);
1143

1144
  if (priv->active)
1145
    open_dialog (GTK_FILE_CHOOSER_BUTTON (widget));
1146 1147 1148 1149 1150
}

static void
gtk_file_chooser_button_hide (GtkWidget *widget)
{
1151 1152
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
1153 1154 1155 1156

  gtk_widget_hide (priv->dialog);

  if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
1157
    GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide (widget);
1158
}
1159

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1160 1161 1162
static void
gtk_file_chooser_button_map (GtkWidget *widget)
{
1163
  GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map (widget);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1164 1165
}

Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
1166 1167 1168 1169
static gboolean
gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
					   gboolean   group_cycling)
{
1170 1171
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
1172

1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
  switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)))
    {
    case GTK_FILE_CHOOSER_ACTION_OPEN:
      gtk_widget_grab_focus (priv->button);
      break;
    case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
      return gtk_widget_mnemonic_activate (priv->combo_box, group_cycling);
      break;
    default:
      g_assert_not_reached ();
      break;
    }
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
1185 1186 1187 1188

  return TRUE;
}

1189
/* Changes the icons wherever it is needed */
1190 1191 1192 1193 1194 1195 1196
struct ChangeIconThemeData
{
  GtkFileChooserButton *button;
  GtkTreeRowReference *row_ref;
};

static void
1197 1198 1199 1200
change_icon_theme_get_info_cb (GCancellable *cancellable,
			       GFileInfo    *info,
			       const GError *error,
			       gpointer      user_data)
1201
{