gtkfilechooserbutton.c 80.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/* -*- 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
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

23
#include <config.h>
24 25 26

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

#include <string.h>

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

#include "gtkfilechooserbutton.h"

55
#include "gtkprivate.h"
56 57
#include "gtkalias.h"

58 59 60 61
/* **************** *
 *  Private Macros  *
 * **************** */

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 66
#define DESKTOP_DISPLAY_NAME	N_("Desktop")
#define FALLBACK_DISPLAY_NAME	N_("(None)")
67
#define FALLBACK_ICON_NAME	"stock_unknown"
68
#define FALLBACK_ICON_SIZE	16
69

70 71 72 73 74 75 76 77 78 79 80

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

/* Property IDs */
enum
{
  PROP_0,

  PROP_DIALOG,
81
  PROP_FOCUS_ON_CLICK,
82
  PROP_TITLE,
83
  PROP_WIDTH_CHARS
84 85
};

86 87 88 89 90 91 92
/* Signals */
enum
{
  FILE_SET,
  LAST_SIGNAL
};

93 94 95 96 97 98 99
/* TreeModel Columns */
enum
{
  ICON_COLUMN,
  DISPLAY_NAME_COLUMN,
  TYPE_COLUMN,
  DATA_COLUMN,
100
  IS_FOLDER_COLUMN,
101
  CANCELLABLE_COLUMN,
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
  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;

122 123 124 125 126 127 128 129 130

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

struct _GtkFileChooserButtonPrivate
{
  GtkWidget *dialog;
  GtkWidget *button;
131 132
  GtkWidget *image;
  GtkWidget *label;
133 134 135 136 137 138
  GtkWidget *combo_box;
  GtkCellRenderer *icon_cell;
  GtkCellRenderer *name_cell;

  GtkTreeModel *model;
  GtkTreeModel *filter_model;
139

140
  gchar *backend;
141
  GtkFileSystem *fs;
142
  GFile *old_file;
143 144

  gulong combo_box_changed_id;
145 146 147
  gulong dialog_file_activated_id;
  gulong dialog_folder_changed_id;
  gulong dialog_selection_changed_id;
148 149 150
  gulong fs_volumes_changed_id;
  gulong fs_bookmarks_changed_id;

151 152 153
  GCancellable *dnd_select_folder_cancellable;
  GCancellable *update_button_cancellable;
  GSList *change_icon_theme_cancellables;
154

155
  gint icon_size;
156

157 158 159 160
  guint8 n_special;
  guint8 n_volumes;
  guint8 n_shortcuts;
  guint8 n_bookmarks;
161 162 163 164
  guint  has_bookmark_separator       : 1;
  guint  has_current_folder_separator : 1;
  guint  has_current_folder           : 1;
  guint  has_other_separator          : 1;
165

166
  /* Used for hiding/showing the dialog when the button is hidden */
167
  guint  active                       : 1;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
168 169

  /* Used to track whether we need to set a default current folder on ::map() */
170
  guint  folder_has_been_set          : 1;
171

172
  guint  focus_on_click               : 1;
173 174 175 176 177 178 179 180 181
};


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

enum
{
182 183
  TEXT_PLAIN,
  TEXT_URI_LIST
184 185
};

186

187 188 189 190
/* ********************* *
 *  Function Prototypes  *
 * ********************* */

191 192 193
/* 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,
194
								 GFile               *file,
195 196
								 GError             **error);
static gboolean gtk_file_chooser_button_remove_shortcut_folder  (GtkFileChooser      *chooser,
197
								 GFile               *file,
198 199
								 GError             **error);

200
/* GObject Functions */
201 202 203
static GObject *gtk_file_chooser_button_constructor        (GType             type,
							    guint             n_params,
							    GObjectConstructParam *params);
204
static void     gtk_file_chooser_button_set_property       (GObject          *object,
205
							    guint             param_id,
206 207 208
							    const GValue     *value,
							    GParamSpec       *pspec);
static void     gtk_file_chooser_button_get_property       (GObject          *object,
209
							    guint             param_id,
210 211
							    GValue           *value,
							    GParamSpec       *pspec);
212
static void     gtk_file_chooser_button_finalize           (GObject          *object);
213 214 215 216 217 218 219 220 221 222

/* GtkObject Functions */
static void     gtk_file_chooser_button_destroy            (GtkObject        *object);

/* GtkWidget Functions */
static void     gtk_file_chooser_button_drag_data_received (GtkWidget        *widget,
							    GdkDragContext   *context,
							    gint              x,
							    gint              y,
							    GtkSelectionData *data,
223
							    guint             type,
224 225 226
							    guint             drag_time);
static void     gtk_file_chooser_button_show_all           (GtkWidget        *widget);
static void     gtk_file_chooser_button_hide_all           (GtkWidget        *widget);
227 228
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
229
static void     gtk_file_chooser_button_map                (GtkWidget        *widget);
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
230 231
static gboolean gtk_file_chooser_button_mnemonic_activate  (GtkWidget        *widget,
							    gboolean          group_cycling);
232 233 234 235
static void     gtk_file_chooser_button_style_set          (GtkWidget        *widget,
							    GtkStyle         *old_style);
static void     gtk_file_chooser_button_screen_changed     (GtkWidget        *widget,
							    GdkScreen        *old_screen);
236 237

/* Utility Functions */
238
static GtkIconTheme *get_icon_theme               (GtkWidget            *widget);
239 240
static void          set_info_for_file_at_iter         (GtkFileChooserButton *fs,
							GFile                *file,
241
							GtkTreeIter          *iter);
242 243 244 245 246 247 248 249 250 251 252 253

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,
254
						   GFile                *file);
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
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);
304

305
static guint file_chooser_button_signals[LAST_SIGNAL] = { 0 };
306 307 308 309 310 311

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

G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \
312
    G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, gtk_file_chooser_button_file_chooser_iface_init) \
Matthias Clasen's avatar
Matthias Clasen committed
313
})
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330


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

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

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

331
  gobject_class->constructor = gtk_file_chooser_button_constructor;
332 333
  gobject_class->set_property = gtk_file_chooser_button_set_property;
  gobject_class->get_property = gtk_file_chooser_button_get_property;
334
  gobject_class->finalize = gtk_file_chooser_button_finalize;
335 336 337 338 339 340

  gtkobject_class->destroy = gtk_file_chooser_button_destroy;

  widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
  widget_class->show_all = gtk_file_chooser_button_show_all;
  widget_class->hide_all = gtk_file_chooser_button_hide_all;
341 342
  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
343
  widget_class->map = gtk_file_chooser_button_map;
344 345
  widget_class->style_set = gtk_file_chooser_button_style_set;
  widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
346
  widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
347

348
  /**
349
   * GtkFileChooserButton::file-set:
350 351 352
   * @widget: the object which received the signal.
   *
   * The ::file-set signal is emitted when the user selects a file.
353
   *
354
   * Note that this signal is only emitted when the <emphasis>user</emphasis>
355
   * changes the file.
356 357 358 359 360 361 362 363 364 365 366
   *
   * Since: 2.12
   */
  file_chooser_button_signals[FILE_SET] =
    g_signal_new (I_("file-set"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkFileChooserButtonClass, file_set),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);
367

Matthias Clasen's avatar
Matthias Clasen committed
368 369 370 371 372 373 374
  /**
   * GtkFileChooserButton:dialog:
   * 
   * Instance of the #GtkFileChooserDialog associated with the button.
   *
   * Since: 2.6
   */
375 376 377 378
  g_object_class_install_property (gobject_class, PROP_DIALOG,
				   g_param_spec_object ("dialog",
							P_("Dialog"),
							P_("The file chooser dialog to use."),
379
							GTK_TYPE_FILE_CHOOSER,
380
							(GTK_PARAM_WRITABLE |
381
							 G_PARAM_CONSTRUCT_ONLY)));
Matthias Clasen's avatar
Matthias Clasen committed
382

383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
  /**
   * 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
399 400 401 402 403 404 405
  /**
   * GtkFileChooserButton:title:
   * 
   * Title to put on the #GtkFileChooserDialog associated with the button.
   *
   * Since: 2.6
   */
406 407 408 409
  g_object_class_install_property (gobject_class, PROP_TITLE,
				   g_param_spec_string ("title",
							P_("Title"),
							P_("The title of the file chooser dialog."),
410
							_(DEFAULT_TITLE),
411
							GTK_PARAM_READWRITE));
Matthias Clasen's avatar
Matthias Clasen committed
412 413

  /**
Matthias Clasen's avatar
Matthias Clasen committed
414
   * GtkFileChooserButton:width-chars:
Matthias Clasen's avatar
Matthias Clasen committed
415 416 417 418 419
   * 
   * The width of the entry and label inside the button, in characters.
   *
   * Since: 2.6
   */
420 421 422 423 424
  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,
425
						     GTK_PARAM_READWRITE));
426 427 428 429 430 431 432 433 434 435

  _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;
436
  GtkWidget *box, *image, *sep;
437
  GtkTargetList *target_list;
438

439
  priv = button->priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
440

441
  priv->icon_size = FALLBACK_ICON_SIZE;
442
  priv->focus_on_click = TRUE;
443

444 445
  gtk_widget_push_composite_child ();

446
  /* Button */
447 448 449 450
  priv->button = gtk_button_new ();
  g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb),
		    button);
  gtk_container_add (GTK_CONTAINER (button), priv->button);
451 452 453 454 455
  gtk_widget_show (priv->button);

  box = gtk_hbox_new (FALSE, 4);
  gtk_container_add (GTK_CONTAINER (priv->button), box);
  gtk_widget_show (box);
456

457 458 459
  priv->image = gtk_image_new ();
  gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
  gtk_widget_show (priv->image);
460

461 462
  priv->label = gtk_label_new (_(FALLBACK_DISPLAY_NAME));
  gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_END);
463
  gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
464
  gtk_container_add (GTK_CONTAINER (box), priv->label);
465 466
  gtk_widget_show (priv->label);

467
  sep = gtk_vseparator_new ();
468
  gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
469 470
  gtk_widget_show (sep);

471
  image = gtk_image_new_from_stock (GTK_STOCK_OPEN,
472
				    GTK_ICON_SIZE_MENU);
473
  gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
474 475
  gtk_widget_show (image);

476 477 478 479 480 481 482
  /* 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 */
483 484
					G_TYPE_POINTER	 /* Volume || Path */,
					G_TYPE_BOOLEAN   /* Is Folder? */,
485
					G_TYPE_POINTER	 /* cancellable */));
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507

  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);
  gtk_container_add (GTK_CONTAINER (button), priv->combo_box);

  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);

508 509 510 511
  gtk_widget_pop_composite_child ();

  /* DnD */
  gtk_drag_dest_set (GTK_WIDGET (button),
512
                     (GTK_DEST_DEFAULT_ALL),
Matthias Clasen's avatar
Matthias Clasen committed
513
		     NULL, 0,
514
		     GDK_ACTION_COPY);
515 516 517 518 519
  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);
520 521 522
}


523 524 525 526 527 528 529 530 531 532 533 534 535
/* ******************************* *
 *  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
536 537 538
gtk_file_chooser_button_add_shortcut_folder (GtkFileChooser  *chooser,
					     GFile           *file,
					     GError         **error)
539 540 541 542 543 544
{
  GtkFileChooser *delegate;
  gboolean retval;

  delegate = g_object_get_qdata (G_OBJECT (chooser),
				 GTK_FILE_CHOOSER_DELEGATE_QUARK);
545
  retval = _gtk_file_chooser_add_shortcut_folder (delegate, file, error);
546 547 548

  if (retval)
    {
549 550
      GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
      GtkFileChooserButtonPrivate *priv = button->priv;
551 552 553
      GtkTreeIter iter;
      gint pos;

554
      pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
555 556 557 558
      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,
559 560
			  ICON_COLUMN, NULL,
			  DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
561
			  TYPE_COLUMN, ROW_TYPE_SHORTCUT,
562
			  DATA_COLUMN, g_object_ref (file),
563
			  IS_FOLDER_COLUMN, FALSE,
564
			  -1);
565
      set_info_for_file_at_iter (button, file, &iter);
566 567 568 569 570 571 572 573 574
      priv->n_shortcuts++;

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

  return retval;
}

static gboolean
575 576 577
gtk_file_chooser_button_remove_shortcut_folder (GtkFileChooser  *chooser,
						GFile           *file,
						GError         **error)
578 579 580 581 582 583 584
{
  GtkFileChooser *delegate;
  gboolean retval;

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

585
  retval = _gtk_file_chooser_remove_shortcut_folder (delegate, file, error);
586 587 588

  if (retval)
    {
589 590
      GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
      GtkFileChooserButtonPrivate *priv = button->priv;
591 592 593 594
      GtkTreeIter iter;
      gint pos;
      gchar type;

595
      pos = model_get_type_position (button, ROW_TYPE_SHORTCUT);
596
      gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
597 598 599 600 601 602 603 604 605 606 607

      do
	{
	  gpointer data;

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

	  if (type == ROW_TYPE_SHORTCUT &&
608
	      data && g_file_equal (data, file))
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
	    {
	      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;
}


626 627 628 629
/* ******************* *
 *  GObject Functions  *
 * ******************* */

630 631 632 633 634 635
static GObject *
gtk_file_chooser_button_constructor (GType                  type,
				     guint                  n_params,
				     GObjectConstructParam *params)
{
  GObject *object;
636
  GtkFileChooserButton *button;
637
  GtkFileChooserButtonPrivate *priv;
638
  GSList *list;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
639
  char *current_folder;
640 641 642 643

  object = (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor) (type,
										  n_params,
										  params);
644 645
  button = GTK_FILE_CHOOSER_BUTTON (object);
  priv = button->priv;
646 647 648 649 650 651

  if (!priv->dialog)
    {
      if (priv->backend)
	priv->dialog = gtk_file_chooser_dialog_new_with_backend (NULL, NULL,
								 GTK_FILE_CHOOSER_ACTION_OPEN,
652 653 654 655 656 657
								 priv->backend,
								 GTK_STOCK_CANCEL,
								 GTK_RESPONSE_CANCEL,
								 GTK_STOCK_OPEN,
								 GTK_RESPONSE_ACCEPT,
								 NULL);
658 659 660
      else
	priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
						    GTK_FILE_CHOOSER_ACTION_OPEN,
661 662 663 664
						    GTK_STOCK_CANCEL,
						    GTK_RESPONSE_CANCEL,
						    GTK_STOCK_OPEN,
						    GTK_RESPONSE_ACCEPT,
665 666 667 668 669 670 671 672 673
						    NULL);

      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);

674 675 676 677 678 679
      gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
    }
  else if (!GTK_WINDOW (priv->dialog)->title)
    {
      gtk_file_chooser_button_set_title (button, _(DEFAULT_TITLE));
    }
680

Federico Mena Quintero's avatar
Federico Mena Quintero committed
681 682 683 684 685 686 687
  current_folder = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (priv->dialog));
  if (current_folder != NULL)
    {
      priv->folder_has_been_set = TRUE;
      g_free (current_folder);
    }

688 689 690
  g_free (priv->backend);
  priv->backend = NULL;

691
  g_signal_connect (priv->dialog, "delete_event",
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
		    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),
713
			     (gpointer) (&priv->dialog));
714

715 716 717
  priv->fs =
    g_object_ref (_gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)));

718
  model_add_special (button);
719

720
  list = _gtk_file_system_list_volumes (priv->fs);
721
  model_add_volumes (button, list);
722 723
  g_slist_free (list);

724
  list = _gtk_file_system_list_bookmarks (priv->fs);
725
  model_add_bookmarks (button, list);
726 727
  g_slist_foreach (list, (GFunc) g_object_unref, NULL);
  g_slist_free (list);
728

729
  model_add_other (button);
730 731 732 733 734 735 736 737 738 739 740

  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);

741 742 743 744 745 746
  /* 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);
747 748 749 750 751 752 753 754

  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);

755 756
  return object;
}
757 758 759

static void
gtk_file_chooser_button_set_property (GObject      *object,
760
				      guint         param_id,
761 762 763
				      const GValue *value,
				      GParamSpec   *pspec)
{
764 765
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
766

767
  switch (param_id)
768 769
    {
    case PROP_DIALOG:
770 771
      /* Construct-only */
      priv->dialog = g_value_get_object (value);
772
      break;
773 774 775
    case PROP_FOCUS_ON_CLICK:
      gtk_file_chooser_button_set_focus_on_click (button, g_value_get_boolean (value));
      break;
776 777 778 779
    case PROP_WIDTH_CHARS:
      gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
					       g_value_get_int (value));
      break;
780
    case GTK_FILE_CHOOSER_PROP_ACTION:
781 782 783 784 785 786 787 788 789 790
      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));
791
	    g_warning ("%s: Choosers of type `%s' do not support `%s'.",
792 793
		       G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);

794
	    g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
795 796 797
	  }
	  break;
	}
798

799 800
      g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
      update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
      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;
	}
817 818 819 820 821 822 823 824 825
      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:
826
    case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
827 828 829
      g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
      break;

830 831 832 833 834 835
    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;

836 837
    case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
      /* Construct-only */
838
      priv->backend = g_value_dup_string (value);
839 840 841 842 843 844 845
      break;

    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:
846
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
847 848 849 850 851 852
      break;
    }
}

static void
gtk_file_chooser_button_get_property (GObject    *object,
853
				      guint       param_id,
854 855 856
				      GValue     *value,
				      GParamSpec *pspec)
{
857 858
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
859 860

  switch (param_id)
861
    {
862 863
    case PROP_WIDTH_CHARS:
      g_value_set_int (value,
864
		       gtk_label_get_width_chars (GTK_LABEL (priv->label)));
865
      break;
866 867 868 869
    case PROP_FOCUS_ON_CLICK:
      g_value_set_boolean (value,
                           gtk_file_chooser_button_get_focus_on_click (button));
      break;
870 871 872 873 874 875 876 877 878 879 880 881

    case PROP_TITLE:
    case GTK_FILE_CHOOSER_PROP_ACTION:
    case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
    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:
882
    case GTK_FILE_CHOOSER_PROP_DO_OVERWRITE_CONFIRMATION:
883 884 885 886
      g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
      break;

    default:
887
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
888 889 890 891
      break;
    }
}

892 893 894
static void
gtk_file_chooser_button_finalize (GObject *object)
{
895 896
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
897

898 899
  if (priv->old_file)
    g_object_unref (priv->old_file);
900 901 902 903

  if (G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize != NULL)
    (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->finalize) (object);
}
904 905 906 907 908 909

/* ********************* *
 *  GtkObject Functions  *
 * ********************* */

static void
910
gtk_file_chooser_button_destroy (GtkObject *object)
911
{
912 913
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (object);
  GtkFileChooserButtonPrivate *priv = button->priv;
914 915
  GtkTreeIter iter;
  GSList *l;
916 917

  if (priv->dialog != NULL)
918 919 920 921 922
    {
      gtk_widget_destroy (priv->dialog);
      priv->dialog = NULL;
    }

923
  if (priv->model && gtk_tree_model_get_iter_first (priv->model, &iter)) do
924 925 926 927 928
    {
      model_free_row_data (button, &iter);
    }
  while (gtk_tree_model_iter_next (priv->model, &iter));

929
  if (priv->dnd_select_folder_cancellable)
930
    {
931 932
      g_cancellable_cancel (priv->dnd_select_folder_cancellable);
      priv->dnd_select_folder_cancellable = NULL;
933 934
    }

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

941
  if (priv->change_icon_theme_cancellables)
942
    {
943
      for (l = priv->change_icon_theme_cancellables; l; l = l->next)
944
        {
945 946
	  GCancellable *cancellable = G_CANCELLABLE (l->data);
	  g_cancellable_cancel (cancellable);
947
        }
948 949
      g_slist_free (priv->change_icon_theme_cancellables);
      priv->change_icon_theme_cancellables = NULL;
950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
    }

  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;
    }
971

972 973 974 975 976 977 978 979 980
  if (GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy != NULL)
    (*GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy) (object);
}


/* ********************* *
 *  GtkWidget Functions  *
 * ********************* */

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

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

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

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

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

  if (!cancelled && !error && info != NULL)
    {
1016 1017 1018 1019 1020 1021 1022 1023 1024
      gboolean is_folder;

      is_folder = (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY);

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

  if (data->selected || data->uris[++data->i] == NULL)
    {
      g_object_unref (data->button);
1032
      g_object_unref (data->file);
1033 1034 1035
      g_strfreev (data->uris);
      g_free (data);

1036
      g_object_unref (cancellable);
1037 1038 1039
      return;
    }

1040 1041
  if (data->file)
    g_object_unref (data->file);
1042

1043
  data->file = g_file_new_for_uri (data->uris[data->i]);
1044

1045
  data->button->priv->dnd_select_folder_cancellable =
1046 1047 1048
    _gtk_file_system_get_info (data->file_system, data->file,
			       "standard::type",
			       dnd_select_folder_get_info_cb, user_data);
1049

1050
  g_object_unref (cancellable);
1051 1052
}

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

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

  if (widget == NULL || context == NULL || data == NULL || data->length < 0)
    return;

1077
  switch (type)
1078 1079 1080 1081
    {
    case TEXT_URI_LIST:
      {
	gchar **uris;
1082
	struct DndSelectFolderData *info;
1083

Matthias Clasen's avatar
Matthias Clasen committed
1084
	uris = gtk_selection_data_get_uris (data);
1085
	
1086 1087 1088
	if (uris == NULL)
	  break;

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

1097
	info->file = g_file_new_for_uri (info->uris[info->i]);
1098

1099 1100
	if (priv->dnd_select_folder_cancellable)
	  g_cancellable_cancel (priv->dnd_select_folder_cancellable);
1101

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

    case TEXT_PLAIN:
1110
      text = (char*) gtk_selection_data_get_text (data);
1111 1112
      file = g_file_new_for_uri (text);
      _gtk_file_chooser_select_file (GTK_FILE_CHOOSER (priv->dialog), file,
1113
				     NULL);
1114 1115
      g_object_unref (file);
      g_free (text);
1116 1117 1118
      break;

    default:
1119 1120 1121
      break;
    }

1122
  gtk_drag_finish (context, TRUE, FALSE, drag_time);
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
}

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

static void
gtk_file_chooser_button_hide_all (GtkWidget *widget)
{
  gtk_widget_hide (widget);
}

1137 1138 1139
static void
gtk_file_chooser_button_show (GtkWidget *widget)
{
1140 1141
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
1142 1143 1144 1145

  if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
    (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show) (widget);

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

static void
gtk_file_chooser_button_hide (GtkWidget *widget)
{
1153 1154
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
1155 1156 1157 1158 1159 1160

  gtk_widget_hide (priv->dialog);

  if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
    (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide) (widget);
}
1161

Federico Mena Quintero's avatar
Federico Mena Quintero committed
1162 1163 1164
static void
gtk_file_chooser_button_map (GtkWidget *widget)
{
1165 1166
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182

  if (!priv->folder_has_been_set)
    {
      char *current_working_dir;

      current_working_dir = g_get_current_dir ();
      gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (widget), current_working_dir);
      g_free (current_working_dir);

      priv->folder_has_been_set = TRUE;
    }

  if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map)
    (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->map) (widget);
}

Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
1183 1184 1185 1186
static gboolean
gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
					   gboolean   group_cycling)
{
1187 1188
  GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (widget);
  GtkFileChooserButtonPrivate *priv = button->priv;
Matthias Clasen's avatar
2.5.3  
Matthias Clasen committed
1189

1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
  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
1202 1203 1204 1205

  return TRUE;
}

1206
/* Changes the icons wherever it is needed */
1207 1208 1209 1210 1211 1212 1213
struct ChangeIconThemeData
{
  GtkFileChooserButton *button;
  GtkTreeRowReference *row_ref;
};

static void
1214 1215 1216 1217
change_icon_theme_get_info_cb (GCancellable *cancellable,
			       GFileInfo    *info,
			       const GError *error,
			       gpointer      user_data)
1218
{
1219
  gboolean cancelled = g_cancellable_is_cancelled (cancellable);
1220 1221 1222
  GdkPixbuf *pixbuf;
  struct ChangeIconThemeData *data = user_data;

1223
  if (!g_slist_find (data->button->priv->change_icon_theme_cancellables, cancellable))
1224 1225
    goto out;

1226 1227
  data->button->priv->change_icon_theme_cancellables =
    g_slist_remove (data->button->priv->change_icon_theme_cancellables, cancellable);
1228 1229 1230 1231

  if (cancelled || error)
    goto out;

1232
  pixbuf = _gtk_file_info_render_icon (info, GTK_WIDGET (data->button), data->button->priv->icon_size);
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242

  if (pixbuf)
    {
      gint width = 0;
      GtkTreeIter iter;
      GtkTreePath *path;

      width = MAX (width, gdk_pixbuf_get_width (pixbuf));

      path = gtk_tree_row_reference_get_path (data->row_ref);
1243 1244 1245 1246
      if (path) 
        {
          gtk_tree_model_get_iter (data->button->priv->model, &iter, path);
          gtk_tree_path_free (path);
1247

1248 1249 1250
          gtk_list_store_set (GTK_LIST_STORE (data->button->priv->model), &iter,
	  		      ICON_COLUMN, pixbuf,
			      -1);
1251

1252 1253 1254 1255 1256
          g_object_set (data->button->priv->icon_cell,
		        "width", width,
		        NULL);
        }
      g_object_unref (pixbuf);
1257 1258 1259 1260 1261 1262 1263
    }

out:
  g_object_unref (data->button);
  gtk_tree_row_reference_free (data->row_ref);
  g_free (data);

1264
  g_object_unref (cancellable);
1265 1266
}