gtkfilechooserdefault.c 75.4 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1
/* GTK - The GIMP Toolkit
2
 * gtkfilechooserdefault.c: Default implementation of GtkFileChooser
Owen Taylor's avatar
Owen Taylor committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 * Copyright (C) 2003, Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 */

21
#include "gtkalignment.h"
22
#include "gtkbutton.h"
23
#include "gtkcellrendererpixbuf.h"
24
#include "gtkcellrendererseptext.h"
25
#include "gtkcellrenderertext.h"
26
#include "gtkcombobox.h"
27 28
#include "gtkentry.h"
#include "gtkfilechooserdefault.h"
29
#include "gtkfilechooserentry.h"
Owen Taylor's avatar
Owen Taylor committed
30 31 32
#include "gtkfilechooserutils.h"
#include "gtkfilechooser.h"
#include "gtkfilesystemmodel.h"
33 34 35 36 37
#include "gtkframe.h"
#include "gtkhbox.h"
#include "gtkhpaned.h"
#include "gtkicontheme.h"
#include "gtkimage.h"
38
#include "gtkintl.h"
39 40
#include "gtklabel.h"
#include "gtkmenuitem.h"
41
#include "gtkmessagedialog.h"
42 43 44 45
#include "gtkprivate.h"
#include "gtkscrolledwindow.h"
#include "gtkstock.h"
#include "gtktable.h"
46 47
#include "gtktoolbar.h"
#include "gtktoolbutton.h"
48 49 50 51 52 53
#include "gtktreeview.h"
#include "gtktreemodelsort.h"
#include "gtktreeselection.h"
#include "gtktreestore.h"
#include "gtktypebuiltins.h"
#include "gtkvbox.h"
Owen Taylor's avatar
Owen Taylor committed
54

55
#include <string.h>
Federico Mena Quintero's avatar
Federico Mena Quintero committed
56
#include <time.h>
57

58
typedef struct _GtkFileChooserDefaultClass GtkFileChooserDefaultClass;
Owen Taylor's avatar
Owen Taylor committed
59

60 61 62
#define GTK_FILE_CHOOSER_DEFAULT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
#define GTK_IS_FILE_CHOOSER_DEFAULT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_DEFAULT))
#define GTK_FILE_CHOOSER_DEFAULT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_CHOOSER_DEFAULT, GtkFileChooserDefaultClass))
Owen Taylor's avatar
Owen Taylor committed
63

64
struct _GtkFileChooserDefaultClass
Owen Taylor's avatar
Owen Taylor committed
65 66 67 68
{
  GtkVBoxClass parent_class;
};

69
struct _GtkFileChooserDefault
Owen Taylor's avatar
Owen Taylor committed
70 71 72 73 74
{
  GtkVBox parent_instance;

  GtkFileSystem *file_system;
  GtkFileSystemModel *tree_model;
75
  GtkTreeStore *shortcuts_model;
Owen Taylor's avatar
Owen Taylor committed
76
  GtkFileSystemModel *list_model;
Owen Taylor's avatar
Owen Taylor committed
77
  GtkTreeModelSort *sort_model;
Owen Taylor's avatar
Owen Taylor committed
78 79 80

  GtkFileChooserAction action;

Owen Taylor's avatar
Owen Taylor committed
81 82
  GtkFileFilter *current_filter;
  GSList *filters;
83

84 85 86 87 88
  gboolean has_home;
  gboolean has_desktop;
  int num_roots;
  int num_shortcuts;
  int num_bookmarks;
89 90 91 92

  guint bookmarks_changed_id;
  GtkTreeIter bookmarks_iter;

93
  GtkFilePath *current_folder;
94
  GtkFilePath *preview_path;
95

96 97
  GtkToolItem *up_button;

98
  GtkWidget *preview_frame;
99

100
  GtkWidget *toolbar;
Owen Taylor's avatar
Owen Taylor committed
101
  GtkWidget *filter_alignment;
102
  GtkWidget *filter_combo;
Owen Taylor's avatar
Owen Taylor committed
103 104
  GtkWidget *tree_scrollwin;
  GtkWidget *tree;
105 106
  GtkWidget *shortcuts_scrollwin;
  GtkWidget *shortcuts_tree;
107
  GtkWidget *add_bookmark_button;
108
  GtkWidget *remove_bookmark_button;
Owen Taylor's avatar
Owen Taylor committed
109 110
  GtkWidget *list_scrollwin;
  GtkWidget *list;
111
  GtkWidget *entry;
Owen Taylor's avatar
Owen Taylor committed
112
  GtkWidget *preview_widget;
113
  GtkWidget *extra_widget;
114 115 116 117 118 119 120

  guint folder_mode : 1;
  guint local_only : 1;
  guint preview_widget_active : 1;
  guint select_multiple : 1;
  guint show_hidden : 1;
  guint changing_folder : 1;
121
  guint list_sort_ascending : 1;
122
  guint bookmarks_set : 1;
123 124
};

125
/* Column numbers for the shortcuts tree.  Keep these in sync with create_shortcuts_model() */
126 127 128 129 130
enum {
  SHORTCUTS_COL_PIXBUF,
  SHORTCUTS_COL_NAME,
  SHORTCUTS_COL_PATH,
  SHORTCUTS_COL_NUM_COLUMNS
Owen Taylor's avatar
Owen Taylor committed
131 132
};

133 134 135 136 137 138 139 140
/* Column numbers for the file list */
enum {
  FILE_LIST_COL_NAME,
  FILE_LIST_COL_SIZE,
  FILE_LIST_COL_MTIME,
  FILE_LIST_COL_NUM_COLUMNS
};

141
/* Standard icon size */
142 143
/* FIXME: maybe this should correspond to the font size in the tree views... */
#define ICON_SIZE 20
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
static void gtk_file_chooser_default_class_init   (GtkFileChooserDefaultClass *class);
static void gtk_file_chooser_default_iface_init   (GtkFileChooserIface        *iface);
static void gtk_file_chooser_default_init         (GtkFileChooserDefault      *impl);

static GObject* gtk_file_chooser_default_constructor  (GType                  type,
						       guint                  n_construct_properties,
						       GObjectConstructParam *construct_params);
static void     gtk_file_chooser_default_finalize     (GObject               *object);
static void     gtk_file_chooser_default_set_property (GObject               *object,
						       guint                  prop_id,
						       const GValue          *value,
						       GParamSpec            *pspec);
static void     gtk_file_chooser_default_get_property (GObject               *object,
						       guint                  prop_id,
						       GValue                *value,
						       GParamSpec            *pspec);
static void     gtk_file_chooser_default_show_all     (GtkWidget             *widget);

static void           gtk_file_chooser_default_set_current_folder 	   (GtkFileChooser    *chooser,
164
									    const GtkFilePath *path);
165 166
static GtkFilePath *  gtk_file_chooser_default_get_current_folder 	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_set_current_name   	   (GtkFileChooser    *chooser,
167
									    const gchar       *name);
168
static void           gtk_file_chooser_default_select_path        	   (GtkFileChooser    *chooser,
169
									    const GtkFilePath *path);
170
static void           gtk_file_chooser_default_unselect_path      	   (GtkFileChooser    *chooser,
171
									    const GtkFilePath *path);
172 173 174 175 176 177
static void           gtk_file_chooser_default_select_all         	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_unselect_all       	   (GtkFileChooser    *chooser);
static GSList *       gtk_file_chooser_default_get_paths          	   (GtkFileChooser    *chooser);
static GtkFilePath *  gtk_file_chooser_default_get_preview_path   	   (GtkFileChooser    *chooser);
static GtkFileSystem *gtk_file_chooser_default_get_file_system    	   (GtkFileChooser    *chooser);
static void           gtk_file_chooser_default_add_filter         	   (GtkFileChooser    *chooser,
178
									    GtkFileFilter     *filter);
179
static void           gtk_file_chooser_default_remove_filter      	   (GtkFileChooser    *chooser,
180
									    GtkFileFilter     *filter);
181 182 183 184 185 186 187 188 189 190 191 192 193
static GSList *       gtk_file_chooser_default_list_filters       	   (GtkFileChooser    *chooser);
static gboolean       gtk_file_chooser_default_add_shortcut_folder    (GtkFileChooser    *chooser,
								       const GtkFilePath *path,
								       GError           **error);
static gboolean       gtk_file_chooser_default_remove_shortcut_folder (GtkFileChooser    *chooser,
								       const GtkFilePath *path,
								       GError           **error);
static GSList *       gtk_file_chooser_default_list_shortcut_folders  (GtkFileChooser    *chooser);

static void set_current_filter   (GtkFileChooserDefault *impl,
				  GtkFileFilter         *filter);
static void check_preview_change (GtkFileChooserDefault *impl);

194
static void filter_combo_changed       (GtkComboBox           *combo_box,
195 196 197
					GtkFileChooserDefault *impl);
static void tree_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
198 199 200 201

static void     shortcuts_row_activated (GtkTreeView           *tree_view,
					 GtkTreePath           *path,
					 GtkTreeViewColumn     *column,
202
					 GtkFileChooserDefault *impl);
203 204 205 206 207 208
static gboolean shortcuts_select_func   (GtkTreeSelection      *selection,
					 GtkTreeModel          *model,
					 GtkTreePath           *path,
					 gboolean               path_currentlny_selected,
					 gpointer               data);

209 210 211 212 213 214 215 216
static void list_selection_changed     (GtkTreeSelection      *tree_selection,
					GtkFileChooserDefault *impl);
static void list_row_activated         (GtkTreeView           *tree_view,
					GtkTreePath           *path,
					GtkTreeViewColumn     *column,
					GtkFileChooserDefault *impl);
static void entry_activate             (GtkEntry              *entry,
					GtkFileChooserDefault *impl);
Owen Taylor's avatar
Owen Taylor committed
217

218 219 220 221 222
static void tree_name_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
Owen Taylor's avatar
Owen Taylor committed
223 224 225 226 227
static void list_icon_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
228 229 230 231 232
static void list_name_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
233
#if 0
234 235 236 237 238
static void list_size_data_func (GtkTreeViewColumn *tree_column,
				 GtkCellRenderer   *cell,
				 GtkTreeModel      *tree_model,
				 GtkTreeIter       *iter,
				 gpointer           data);
Federico Mena Quintero's avatar
Federico Mena Quintero committed
239 240 241 242 243 244
#endif
static void list_mtime_data_func (GtkTreeViewColumn *tree_column,
				  GtkCellRenderer   *cell,
				  GtkTreeModel      *tree_model,
				  GtkTreeIter       *iter,
				  gpointer           data);
245

Owen Taylor's avatar
Owen Taylor committed
246
static GObjectClass *parent_class;
247

Owen Taylor's avatar
Owen Taylor committed
248
GType
249
_gtk_file_chooser_default_get_type (void)
Owen Taylor's avatar
Owen Taylor committed
250
{
251
  static GType file_chooser_default_type = 0;
Owen Taylor's avatar
Owen Taylor committed
252

253
  if (!file_chooser_default_type)
Owen Taylor's avatar
Owen Taylor committed
254
    {
255
      static const GTypeInfo file_chooser_default_info =
Owen Taylor's avatar
Owen Taylor committed
256
      {
257
	sizeof (GtkFileChooserDefaultClass),
Owen Taylor's avatar
Owen Taylor committed
258 259
	NULL,		/* base_init */
	NULL,		/* base_finalize */
260
	(GClassInitFunc) gtk_file_chooser_default_class_init,
Owen Taylor's avatar
Owen Taylor committed
261 262
	NULL,		/* class_finalize */
	NULL,		/* class_data */
263
	sizeof (GtkFileChooserDefault),
Owen Taylor's avatar
Owen Taylor committed
264
	0,		/* n_preallocs */
265
	(GInstanceInitFunc) gtk_file_chooser_default_init,
Owen Taylor's avatar
Owen Taylor committed
266
      };
267

Owen Taylor's avatar
Owen Taylor committed
268 269
      static const GInterfaceInfo file_chooser_info =
      {
270
	(GInterfaceInitFunc) gtk_file_chooser_default_iface_init, /* interface_init */
Owen Taylor's avatar
Owen Taylor committed
271 272 273 274
	NULL,			                                       /* interface_finalize */
	NULL			                                       /* interface_data */
      };

275 276 277
      file_chooser_default_type = g_type_register_static (GTK_TYPE_VBOX, "GtkFileChooserDefault",
							 &file_chooser_default_info, 0);
      g_type_add_interface_static (file_chooser_default_type,
Owen Taylor's avatar
Owen Taylor committed
278 279 280 281
				   GTK_TYPE_FILE_CHOOSER,
				   &file_chooser_info);
    }

282
  return file_chooser_default_type;
Owen Taylor's avatar
Owen Taylor committed
283 284 285
}

static void
286
gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
Owen Taylor's avatar
Owen Taylor committed
287 288
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
289
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
Owen Taylor's avatar
Owen Taylor committed
290

Owen Taylor's avatar
Owen Taylor committed
291 292
  parent_class = g_type_class_peek_parent (class);

293 294 295 296
  gobject_class->finalize = gtk_file_chooser_default_finalize;
  gobject_class->constructor = gtk_file_chooser_default_constructor;
  gobject_class->set_property = gtk_file_chooser_default_set_property;
  gobject_class->get_property = gtk_file_chooser_default_get_property;
Owen Taylor's avatar
Owen Taylor committed
297

298
  widget_class->show_all = gtk_file_chooser_default_show_all;
Owen Taylor's avatar
Owen Taylor committed
299

Owen Taylor's avatar
Owen Taylor committed
300 301 302 303
  _gtk_file_chooser_install_properties (gobject_class);
}

static void
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
gtk_file_chooser_default_iface_init (GtkFileChooserIface *iface)
{
  iface->select_path = gtk_file_chooser_default_select_path;
  iface->unselect_path = gtk_file_chooser_default_unselect_path;
  iface->select_all = gtk_file_chooser_default_select_all;
  iface->unselect_all = gtk_file_chooser_default_unselect_all;
  iface->get_paths = gtk_file_chooser_default_get_paths;
  iface->get_preview_path = gtk_file_chooser_default_get_preview_path;
  iface->get_file_system = gtk_file_chooser_default_get_file_system;
  iface->set_current_folder = gtk_file_chooser_default_set_current_folder;
  iface->get_current_folder = gtk_file_chooser_default_get_current_folder;
  iface->set_current_name = gtk_file_chooser_default_set_current_name;
  iface->add_filter = gtk_file_chooser_default_add_filter;
  iface->remove_filter = gtk_file_chooser_default_remove_filter;
  iface->list_filters = gtk_file_chooser_default_list_filters;
  iface->add_shortcut_folder = gtk_file_chooser_default_add_shortcut_folder;
  iface->remove_shortcut_folder = gtk_file_chooser_default_remove_shortcut_folder;
  iface->list_shortcut_folders = gtk_file_chooser_default_list_shortcut_folders;
Owen Taylor's avatar
Owen Taylor committed
322 323 324
}

static void
325
gtk_file_chooser_default_init (GtkFileChooserDefault *impl)
Owen Taylor's avatar
Owen Taylor committed
326
{
327 328 329 330 331 332
  impl->folder_mode = FALSE;
  impl->local_only = TRUE;
  impl->preview_widget_active = TRUE;
  impl->select_multiple = FALSE;
  impl->show_hidden = FALSE;

333
  gtk_box_set_spacing (GTK_BOX (impl), 12);
334 335 336
}

static void
337
gtk_file_chooser_default_finalize (GObject *object)
338
{
339
  GtkFileChooserDefault *impl = GTK_FILE_CHOOSER_DEFAULT (object);
340

341 342
  g_signal_handler_disconnect (impl->file_system, impl->bookmarks_changed_id);
  impl->bookmarks_changed_id = 0;
343 344 345 346 347
  g_object_unref (impl->file_system);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

348
/* Shows an error dialog */
349
static void
350 351
error_message (GtkFileChooserDefault *impl,
	       const char            *msg)
352 353 354 355 356 357 358 359 360 361
{
  GtkWidget *toplevel;
  GtkWidget *dialog;

  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (impl));

  dialog = gtk_message_dialog_new (toplevel ? GTK_WINDOW (toplevel) : NULL,
				   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
				   GTK_MESSAGE_ERROR,
				   GTK_BUTTONS_CLOSE,
362 363
				   "%s",
				   msg);
364 365 366 367
  gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}

368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
/* Shows a simple error dialog relative to a path.  Frees the GError as well. */
static void
error_dialog (GtkFileChooserDefault *impl,
	      const char            *msg,
	      const GtkFilePath     *path,
	      GError                *error)
{
  char *text;

  text = g_strdup_printf (msg,
			  gtk_file_path_get_string (path),
			  error->message);
  error_message (impl, text);
  g_free (text);
  g_error_free (error);
}

385
static void
386
update_preview_widget_visibility (GtkFileChooserDefault *impl)
387 388 389 390 391 392 393
{
  if (impl->preview_widget_active && impl->preview_widget)
    gtk_widget_show (impl->preview_frame);
  else
    gtk_widget_hide (impl->preview_frame);
}

394
static void
395 396
set_preview_widget (GtkFileChooserDefault *impl,
		    GtkWidget             *preview_widget)
397 398 399 400 401
{
  if (preview_widget == impl->preview_widget)
    return;

  if (impl->preview_widget)
402 403
    gtk_container_remove (GTK_CONTAINER (impl->preview_frame),
			  impl->preview_widget);
404 405 406 407

  impl->preview_widget = preview_widget;
  if (impl->preview_widget)
    {
408 409 410
      gtk_widget_show (impl->preview_widget);
      gtk_container_add (GTK_CONTAINER (impl->preview_frame),
			 impl->preview_widget);
411
    }
412 413

  update_preview_widget_visibility (impl);
414 415
}

416 417 418
/* Clears the selection in the shortcuts tree */
static void
shortcuts_unselect_all (GtkFileChooserDefault *impl)
419
{
420
  GtkTreeSelection *selection;
421

422
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
423
  gtk_tree_selection_unselect_all (selection);
424 425 426 427
}

/* Convenience function to get the display name and icon info for a path */
static GtkFileInfo *
428
get_file_info (GtkFileSystem *file_system, const GtkFilePath *path, GError **error)
429 430 431 432 433 434 435 436 437
{
  GtkFilePath *parent_path;
  GtkFileFolder *parent_folder;
  GtkFileInfo *info;

  if (!gtk_file_system_get_parent (file_system, path, &parent_path, error))
    return NULL;

  parent_folder = gtk_file_system_get_folder (file_system, parent_path,
438
#if 0
439
					      GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_ICON,
440 441 442
#else
					      GTK_FILE_INFO_DISPLAY_NAME,
#endif
443 444 445 446 447 448 449 450 451 452 453 454
					      error);
  gtk_file_path_free (parent_path);

  if (!parent_folder)
    return NULL;

  info = gtk_file_folder_get_info (parent_folder, path, error);
  g_object_unref (parent_folder);

  return info;
}

455 456 457
/* Inserts a path in the shortcuts tree, making a copy of it.  A position of -1
 * indicates the end of the tree.  If the label is NULL, then the display name
 * of a GtkFileInfo is used.
458
 */
459
static gboolean
460 461 462 463 464 465
shortcuts_insert_path (GtkFileChooserDefault *impl,
		       int                    pos,
		       const GtkFilePath     *path,
		       gboolean               is_root,
		       const char            *label,
		       GError               **error)
466 467 468 469 470 471 472 473 474 475 476 477 478
{
  GtkFileInfo *info;
  GtkFilePath *path_copy;
  GdkPixbuf *pixbuf;
  GtkTreeIter iter;

  /* FIXME: what if someone adds a shortcut to a root?  get_file_info() will not
   * work in that case, I think...
   */

  if (is_root)
    info = gtk_file_system_get_root_info (impl->file_system,
					  path,
479
#if 0
480
					  GTK_FILE_INFO_DISPLAY_NAME | GTK_FILE_INFO_ICON,
481 482 483
#else
					  GTK_FILE_INFO_DISPLAY_NAME,
#endif
484
					  error);
485
  else
486
    info = get_file_info (impl->file_system, path, error);
487 488

  if (!info)
489
    return FALSE;
490
#if 0
491
  pixbuf = gtk_file_info_render_icon (info, impl->shortcuts_tree, ICON_SIZE);
492 493 494
#endif
  /* FIXME: NULL GError */
  pixbuf = gtk_file_system_render_icon (impl->file_system, path, GTK_WIDGET (impl), ICON_SIZE, NULL);
495

496
  gtk_tree_store_insert (impl->shortcuts_model, &iter, NULL, pos);
497 498 499 500 501 502 503 504 505 506 507
  path_copy = gtk_file_path_copy (path);

  if (!label)
    label = gtk_file_info_get_display_name (info);

  gtk_tree_store_set (impl->shortcuts_model, &iter,
		      SHORTCUTS_COL_PIXBUF, pixbuf,
		      SHORTCUTS_COL_NAME, label,
		      SHORTCUTS_COL_PATH, path_copy,
		      -1);

Federico Mena Quintero's avatar
Federico Mena Quintero committed
508 509
  gtk_file_info_free (info);

510 511
  if (pixbuf)
    g_object_unref (pixbuf);
512 513

  return TRUE;
514 515
}

516 517 518 519 520 521 522 523 524 525 526 527 528
/* Displays an error message about not being able to get information for a file.
 * Frees the GError as well.
 */
static void
error_getting_info_dialog (GtkFileChooserDefault *impl,
			   const GtkFilePath     *path,
			   GError                *error)
{
  error_dialog (impl,
		_("Could not retrieve information about %s:\n%s"),
		path, error);
}

529 530
/* Appends an item for the user's home directory to the shortcuts model */
static void
531
shortcuts_append_home (GtkFileChooserDefault *impl)
532 533 534 535 536
{
  const char *name;
  const char *home;
  GtkFilePath *home_path;
  char *label;
537
  GError *error;
538 539

  name = g_get_user_name ();
540
  label = g_strdup_printf (_("%s's Home"), name);
541 542 543 544

  home = g_get_home_dir ();
  home_path = gtk_file_system_filename_to_path (impl->file_system, home);

545 546 547 548
  error = NULL;
  impl->has_home = shortcuts_insert_path (impl, -1, home_path, FALSE, label, &error);
  if (!impl->has_home)
    error_getting_info_dialog (impl, home_path, error);
549 550 551 552 553

  g_free (label);
  gtk_file_path_free (home_path);
}

554 555
/* Appends the ~/Desktop directory to the shortcuts model */
static void
556
shortcuts_append_desktop (GtkFileChooserDefault *impl)
557 558 559
{
  char *name;
  GtkFilePath *path;
560
  GError *error;
561 562 563

  /* FIXME: What is the Right Way of finding the desktop directory? */

564
  name = g_build_filename (g_get_home_dir (), _("Desktop"), NULL);
565 566 567
  path = gtk_file_system_filename_to_path (impl->file_system, name);
  g_free (name);

568 569 570 571 572
  error = NULL;
  impl->has_desktop = shortcuts_insert_path (impl, -1, path, FALSE, NULL, &error);
  if (!impl->has_desktop)
    error_getting_info_dialog (impl, path, error);

573 574 575
  gtk_file_path_free (path);
}

576 577 578 579 580
/* Appends a list of GtkFilePath to the shortcuts model; returns how many were inserted */
static int
shortcuts_append_paths (GtkFileChooserDefault *impl,
			GSList                *paths,
			gboolean               is_file_system_root)
581
{
582
  int num_inserted;
583

584
  num_inserted = 0;
585

586
  for (; paths; paths = paths->next)
587
    {
588
      GtkFilePath *path;
589 590 591 592
      GError *error;

      path = paths->data;
      error = NULL;
593

594 595 596 597
      if (shortcuts_insert_path (impl, -1, path, is_file_system_root, NULL, &error))
	num_inserted++;
      else
	error_getting_info_dialog (impl, path, error);
598
    }
599

600 601 602 603 604 605 606 607 608 609 610 611 612
  return num_inserted;
}

/* Appends all the file system roots to the shortcuts model */
static void
shortcuts_append_file_system_roots (GtkFileChooserDefault *impl)
{
  GSList *roots;

  roots = gtk_file_system_list_roots (impl->file_system);
  /* FIXME: handle the roots-changed signal on the file system */

  impl->num_roots = shortcuts_append_paths (impl, roots, TRUE);
613 614
  gtk_file_paths_free (roots);
}
615

616 617 618 619
/* Removes the bookmarks separator node and all the bookmarks from the tree
 * model.
 */
static void
620
remove_bookmark_rows (GtkFileChooserDefault *impl)
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
{
  GtkTreePath *path;
  GtkTreeIter iter;

  if (!impl->bookmarks_set)
    return;

  /* Ugh.  Is there a better way to do this? */

  path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &impl->bookmarks_iter);

  while (gtk_tree_model_get_iter (GTK_TREE_MODEL (impl->shortcuts_model), &iter, path))
    gtk_tree_store_remove (impl->shortcuts_model, &impl->bookmarks_iter);

  impl->bookmarks_set = FALSE;
}

/* Appends the bookmarks separator node and the bookmarks from the file system. */
639
static void
640
shortcuts_append_bookmarks (GtkFileChooserDefault *impl)
641
{
642
  GSList *bookmarks;
643

644
  remove_bookmark_rows (impl);
645 646 647

  gtk_tree_store_append (impl->shortcuts_model, &impl->bookmarks_iter, NULL);
  gtk_tree_store_set (impl->shortcuts_model, &impl->bookmarks_iter,
648 649
		      SHORTCUTS_COL_PIXBUF, NULL,
		      SHORTCUTS_COL_NAME, NULL,
650 651 652 653 654 655
		      SHORTCUTS_COL_PATH, NULL,
		      -1);
  impl->bookmarks_set = TRUE;

  bookmarks = gtk_file_system_list_bookmarks (impl->file_system);

656 657 658
  /* FIXME: How do we know if a bookmark is a file system root? */
  impl->num_bookmarks = shortcuts_append_paths (impl, bookmarks, FALSE);
  gtk_file_paths_free (bookmarks);
659 660 661 662
}

/* Creates the GtkTreeStore used as the shortcuts model */
static void
663
create_shortcuts_model (GtkFileChooserDefault *impl)
664
{
665 666
  if (impl->shortcuts_model)
    g_object_unref (impl->shortcuts_model);
667

668
  /* Keep this order in sync with the SHORCUTS_COL_* enum values */
669 670 671 672 673
  impl->shortcuts_model = gtk_tree_store_new (SHORTCUTS_COL_NUM_COLUMNS,
					      GDK_TYPE_PIXBUF,	/* pixbuf */
					      G_TYPE_STRING,	/* name */
					      G_TYPE_POINTER);	/* path */

674 675 676
  if (impl->file_system)
    {
      shortcuts_append_home (impl);
677
      shortcuts_append_desktop (impl);
678 679 680
      shortcuts_append_file_system_roots (impl);
      shortcuts_append_bookmarks (impl);
    }
681

682
  gtk_tree_view_set_model (GTK_TREE_VIEW (impl->shortcuts_tree), GTK_TREE_MODEL (impl->shortcuts_model));
683 684
}

685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
/* Callback used when the "Up" toolbar button is clicked */
static void
toolbar_up_cb (GtkToolButton         *button,
	       GtkFileChooserDefault *impl)
{
  GtkFilePath *parent_path;
  GError *error;

  error = NULL;
  if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, &error))
    {
      if (parent_path) /* If we were on a root, parent_path will be NULL */
	{
	  _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (impl), parent_path);
	  gtk_file_path_free (parent_path);
	}
    }
  else
703 704 705 706
    error_dialog (impl,
		  _("Could not go to the parent folder of %s:\n%s"),
		  impl->current_folder,
		  error);
707 708 709
}

/* Appends an item to the toolbar */
710
static GtkToolItem *
711 712 713 714 715 716 717 718 719 720
toolbar_add_item (GtkFileChooserDefault *impl,
		  const char            *stock_id,
		  GCallback              callback)
{
  GtkToolItem *item;

  item = gtk_tool_button_new_from_stock (stock_id);
  g_signal_connect (item, "clicked", callback, impl);
  gtk_toolbar_insert (GTK_TOOLBAR (impl->toolbar), item, -1);
  gtk_widget_show (GTK_WIDGET (item));
721 722

  return item;
723 724 725 726 727 728 729
}

/* Creates the toolbar widget */
static GtkWidget *
toolbar_create (GtkFileChooserDefault *impl)
{
  impl->toolbar = gtk_toolbar_new ();
730 731
  gtk_toolbar_set_style (GTK_TOOLBAR (impl->toolbar), GTK_TOOLBAR_ICONS);

732
  impl->up_button = toolbar_add_item (impl, GTK_STOCK_GO_UP, G_CALLBACK (toolbar_up_cb));
733 734 735 736

  return impl->toolbar;
}

737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
/* Sets the sensitivity of the toolbar buttons */
static void
toolbar_check_sensitivity (GtkFileChooserDefault *impl)
{
  GtkFilePath *parent_path;
  gboolean has_parent;

  has_parent = FALSE;

  /* I don't think we need to check GError here, do we? */
  if (gtk_file_system_get_parent (impl->file_system, impl->current_folder, &parent_path, NULL))
    {
      if (parent_path)
	{
	  gtk_file_path_free (parent_path);
	  has_parent = TRUE;
	}
    }

  gtk_widget_set_sensitive (GTK_WIDGET (impl->up_button), has_parent);
}

759
/* Creates the widgets for the filter combo box */
760
static GtkWidget *
761
create_filter (GtkFileChooserDefault *impl)
762
{
763 764
  GtkWidget *hbox;
  GtkWidget *label;
765

Owen Taylor's avatar
Owen Taylor committed
766 767
  impl->filter_alignment = gtk_alignment_new (0.0, 0.5, 0.0, 1.0);
  gtk_alignment_set_padding (GTK_ALIGNMENT (impl->filter_alignment), 0, 6, 0, 0);
768
  /* Don't show filter initially -- don't gtk_widget_show() the filter_alignment here */
Owen Taylor's avatar
Owen Taylor committed
769 770 771 772 773

  hbox = gtk_hbox_new (FALSE, 6);
  gtk_container_add (GTK_CONTAINER (impl->filter_alignment), hbox);
  gtk_widget_show (hbox);

774
  label = gtk_label_new_with_mnemonic (_("Files of _type:"));
Owen Taylor's avatar
Owen Taylor committed
775 776 777
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  gtk_widget_show (label);

778 779 780
  impl->filter_combo = gtk_combo_box_new_text ();
  gtk_box_pack_start (GTK_BOX (hbox), impl->filter_combo, FALSE, FALSE, 0);
  gtk_widget_show (impl->filter_combo);
Owen Taylor's avatar
Owen Taylor committed
781

782
  gtk_label_set_mnemonic_widget (GTK_LABEL (label), impl->filter_combo);
Owen Taylor's avatar
Owen Taylor committed
783

784 785
  g_signal_connect (impl->filter_combo, "changed",
		    G_CALLBACK (filter_combo_changed), impl);
Owen Taylor's avatar
Owen Taylor committed
786

787 788 789
  return impl->filter_alignment;
}

790
/* Creates the widgets for the folder tree */
791
static GtkWidget *
792
create_folder_tree (GtkFileChooserDefault *impl)
793 794 795 796
{
  GtkTreeSelection *selection;

  /* Scrolled window */
Owen Taylor's avatar
Owen Taylor committed
797 798 799 800

  impl->tree_scrollwin = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
801 802
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->tree_scrollwin),
				       GTK_SHADOW_IN);
803 804
  if (impl->folder_mode)
    gtk_widget_show (impl->tree_scrollwin);
805 806 807

  /* Tree */

Owen Taylor's avatar
Owen Taylor committed
808 809
  impl->tree = gtk_tree_view_new ();
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->tree), FALSE);
810

Owen Taylor's avatar
Owen Taylor committed
811 812 813 814 815 816
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->tree));
  g_signal_connect (selection, "changed",
		    G_CALLBACK (tree_selection_changed), impl);

  gtk_container_add (GTK_CONTAINER (impl->tree_scrollwin), impl->tree);
  gtk_widget_show (impl->tree);
817 818 819 820 821 822 823 824 825 826 827 828 829

  /* Model */

  impl->tree_model = _gtk_file_system_model_new (impl->file_system, NULL, -1,
						 GTK_FILE_INFO_DISPLAY_NAME);
  _gtk_file_system_model_set_show_files (impl->tree_model, FALSE);

  gtk_tree_view_set_model (GTK_TREE_VIEW (impl->tree),
			   GTK_TREE_MODEL (impl->tree_model));

  /* Column */

  gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (impl->tree), 0,
830
					      _("File name"),
831 832 833 834 835 836 837 838
					      gtk_cell_renderer_text_new (),
					      tree_name_data_func, impl, NULL);
  gtk_tree_view_set_search_column (GTK_TREE_VIEW (impl->tree),
				   GTK_FILE_SYSTEM_MODEL_DISPLAY_NAME);

  return impl->tree_scrollwin;
}

839 840 841
/* Callback used when the "Add bookmark" button is clicked */
static void
add_bookmark_button_clicked_cb (GtkButton *button,
842
				GtkFileChooserDefault *impl)
843
{
844 845 846 847 848 849 850 851
  GError *error;

  error = NULL;
  if (!gtk_file_system_add_bookmark (impl->file_system, impl->current_folder, &error))
    error_dialog (impl,
		  _("Could not add bookmark for %s:\n%s"),
		  impl->current_folder,
		  error);
852 853
}

854 855 856
/* Callback used when the "Remove bookmark" button is clicked */
static void
remove_bookmark_button_clicked_cb (GtkButton *button,
857
				   GtkFileChooserDefault *impl)
858 859 860 861
{
  GtkTreeSelection *selection;
  GtkTreeIter iter;
  GtkFilePath *path;
862
  GError *error;
863 864 865 866 867 868 869 870

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));

  if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
    return;

  gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);

871 872 873 874 875 876
  error = NULL;
  if (!gtk_file_system_remove_bookmark (impl->file_system, path, &error))
    error_dialog (impl,
		  _("Could not remove bookmark for %s:\n%s"),
		  path,
		  error);
877 878 879 880 881 882
}

/* Sensitize the "add bookmark" button if the current folder is not in the
 * bookmarks list, or de-sensitize it otherwise.
 */
static void
883
bookmarks_check_add_sensitivity (GtkFileChooserDefault *impl)
884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
{
  GtkTreeIter iter;
  gboolean exists;

  exists = FALSE;

  if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (impl->shortcuts_model), &iter))
    do
      {
	GtkFilePath *path;

	gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), &iter, SHORTCUTS_COL_PATH, &path, -1);

	if (path && gtk_file_path_compare (path, impl->current_folder) == 0)
	  {
	    exists = TRUE;
	    break;
	  }
      }
    while (gtk_tree_model_iter_next (GTK_TREE_MODEL (impl->shortcuts_model), &iter));

  gtk_widget_set_sensitive (impl->add_bookmark_button, !exists);
}

/* Sets the sensitivity of the "remove bookmark" button depending on whether a
 * bookmark row is selected in the shortcuts tree.
 */
static void
912
bookmarks_check_remove_sensitivity (GtkFileChooserDefault *impl)
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939
{
  GtkTreeSelection *selection;
  GtkTreeIter iter;
  gboolean is_bookmark;

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));

  if (gtk_tree_selection_get_selected (selection, NULL, &iter))
    {
      GtkTreePath *bookmarks_path;
      GtkTreePath *sel_path;

      bookmarks_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model),
						&impl->bookmarks_iter);
      sel_path = gtk_tree_model_get_path (GTK_TREE_MODEL (impl->shortcuts_model), &iter);

      is_bookmark = (gtk_tree_path_compare (bookmarks_path, sel_path) < 0);

      gtk_tree_path_free (bookmarks_path);
      gtk_tree_path_free (sel_path);
    }
  else
    is_bookmark = FALSE;

  gtk_widget_set_sensitive (impl->remove_bookmark_button, is_bookmark);
}

940 941
/* Creates the widgets for the shortcuts and bookmarks tree */
static GtkWidget *
942
create_shortcuts_tree (GtkFileChooserDefault *impl)
943
{
944
  GtkWidget *vbox;
945
  GtkWidget *hbox;
946 947
  GtkWidget *hbox2;
  GtkWidget *widget;
948 949 950
  GtkTreeSelection *selection;
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;
951
  GtkWidget *image;
952

953 954 955
  vbox = gtk_vbox_new (FALSE, 12);
  gtk_widget_show (vbox);

956 957 958 959 960 961 962
  /* Scrolled window */

  impl->shortcuts_scrollwin = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->shortcuts_scrollwin),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->shortcuts_scrollwin),
				       GTK_SHADOW_IN);
963
  gtk_box_pack_start (GTK_BOX (vbox), impl->shortcuts_scrollwin, TRUE, TRUE, 0);
964 965 966 967 968 969 970 971
  gtk_widget_show (impl->shortcuts_scrollwin);

  /* Tree */

  impl->shortcuts_tree = gtk_tree_view_new ();
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (impl->shortcuts_tree), FALSE);

  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->shortcuts_tree));
972
  gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
973 974 975 976 977 978
  gtk_tree_selection_set_select_function (selection,
					  shortcuts_select_func,
					  impl, NULL);
  
  g_signal_connect (impl->shortcuts_tree, "row_activated",
		    G_CALLBACK (shortcuts_row_activated), impl);
979 980 981 982 983 984 985 986 987 988 989

  gtk_container_add (GTK_CONTAINER (impl->shortcuts_scrollwin), impl->shortcuts_tree);
  gtk_widget_show (impl->shortcuts_tree);

  /* Model */

  create_shortcuts_model (impl);

  /* Column */

  column = gtk_tree_view_column_new ();
990
  gtk_tree_view_column_set_title (column, _("Folder"));
991 992

  renderer = gtk_cell_renderer_pixbuf_new ();
993
  gtk_tree_view_column_pack_start (column, renderer, FALSE);
994
  gtk_tree_view_column_set_attributes (column, renderer,
995
				       "pixbuf", SHORTCUTS_COL_PIXBUF,
996 997
				       NULL);

998
  renderer = _gtk_cell_renderer_sep_text_new ();
999 1000
  gtk_tree_view_column_pack_start (column, renderer, TRUE);
  gtk_tree_view_column_set_attributes (column, renderer,
1001
				       "text", SHORTCUTS_COL_NAME,
1002 1003 1004 1005
				       NULL);

  gtk_tree_view_append_column (GTK_TREE_VIEW (impl->shortcuts_tree), column);

1006 1007 1008 1009 1010
  /* Bookmark buttons */

  hbox = gtk_hbox_new (FALSE, 12);
  gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  gtk_widget_show (hbox);
1011

1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
  /* "Add bookmark" */

  impl->add_bookmark_button = gtk_button_new ();

  hbox2 = gtk_hbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (impl->add_bookmark_button), hbox2);
  widget = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
  gtk_box_pack_start (GTK_BOX (hbox2), widget, FALSE, FALSE, 0);
  widget = gtk_label_new (_("Add bookmark"));
  gtk_box_pack_start (GTK_BOX (hbox2), widget, FALSE, FALSE, 0);
  gtk_widget_show_all (impl->add_bookmark_button);

1024 1025
  g_signal_connect (impl->add_bookmark_button, "clicked",
		    G_CALLBACK (add_bookmark_button_clicked_cb), impl);
1026
  gtk_box_pack_start (GTK_BOX (hbox), impl->add_bookmark_button, FALSE, FALSE, 0);
1027
  gtk_widget_set_sensitive (impl->add_bookmark_button, FALSE);
1028
  gtk_widget_show (impl->add_bookmark_button);
1029

1030 1031
  /* "Remove bookmark" */

1032 1033 1034 1035 1036 1037 1038
  impl->remove_bookmark_button = gtk_button_new ();
  g_signal_connect (impl->remove_bookmark_button, "clicked",
		    G_CALLBACK (remove_bookmark_button_clicked_cb), impl);
  image = gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (impl->remove_bookmark_button), image);
  gtk_widget_set_sensitive (impl->remove_bookmark_button, FALSE);
  gtk_box_pack_start (GTK_BOX (hbox), impl->remove_bookmark_button, FALSE, FALSE, 0);
1039
  gtk_widget_show_all (impl->remove_bookmark_button);
1040

1041
  return vbox;
1042 1043
}

1044
/* Creates the widgets for the file list */
1045
static GtkWidget *
1046
create_file_list (GtkFileChooserDefault *impl)
1047 1048 1049 1050 1051 1052 1053
{
  GtkTreeSelection *selection;
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;

  /* Scrolled window */

Owen Taylor's avatar
Owen Taylor committed
1054 1055 1056
  impl->list_scrollwin = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
				  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1057 1058
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (impl->list_scrollwin),
				       GTK_SHADOW_IN);
1059 1060
  if (!impl->folder_mode)
    gtk_widget_show (impl->list_scrollwin);
1061 1062 1063

  /* Tree/list view */

Owen Taylor's avatar
Owen Taylor committed
1064
  impl->list = gtk_tree_view_new ();
Owen Taylor's avatar
Owen Taylor committed
1065
  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (impl->list), TRUE);
Owen Taylor's avatar
Owen Taylor committed
1066
  gtk_container_add (GTK_CONTAINER (impl->list_scrollwin), impl->list);
1067 1068
  g_signal_connect (impl->list, "row_activated",
		    G_CALLBACK (list_row_activated), impl);
Owen Taylor's avatar
Owen Taylor committed
1069
  gtk_widget_show (impl->list);
1070

Owen Taylor's avatar
Owen Taylor committed
1071 1072 1073
  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->list));
  g_signal_connect (selection, "changed",
		    G_CALLBACK (list_selection_changed), impl);
1074

1075 1076