1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
3 /* GTK+: gtkfilechooserbutton.c
5 * Copyright (c) 2004 James M. Cape <jcape@ignore-your.tv>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
25 #include <sys/types.h>
35 #include "gtkbutton.h"
37 #include "gtkicontheme.h"
38 #include "gtkiconfactory.h"
42 #include "gtkvseparator.h"
43 #include "gtkfilechooserdialog.h"
44 #include "gtkfilechooserprivate.h"
45 #include "gtkfilechooserutils.h"
47 #include "gtkfilechooserbutton.h"
54 #define GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE(object) (GTK_FILE_CHOOSER_BUTTON ((object))->priv)
56 #define DEFAULT_FILENAME N_("(None)")
57 #define MIN_LABEL_WIDTH 100
58 #define ENTRY_BUTTON_SPACING 0
59 #define FALLBACK_ICON_SIZE 20
60 #define FALLBACK_ICON_NAME "stock_unknown"
62 /* ********************** *
63 * Private Enumerations *
64 * ********************** */
77 /* ******************** *
78 * Private Structures *
79 * ******************** */
81 struct _GtkFileChooserButtonPrivate
88 GtkFilePath *old_path;
90 gulong dialog_file_activated_id;
91 gulong dialog_folder_changed_id;
92 gulong dialog_selection_changed_id;
95 /* Used for hiding/showing the dialog when the button is hidden */
110 /* ********************* *
111 * Function Prototypes *
112 * ********************* */
114 /* GObject Functions */
115 static GObject *gtk_file_chooser_button_constructor (GType type,
117 GObjectConstructParam *params);
118 static void gtk_file_chooser_button_set_property (GObject *object,
122 static void gtk_file_chooser_button_get_property (GObject *object,
127 /* GtkObject Functions */
128 static void gtk_file_chooser_button_destroy (GtkObject *object);
130 /* GtkWidget Functions */
131 static void gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
132 GdkDragContext *context,
135 GtkSelectionData *data,
138 static void gtk_file_chooser_button_show_all (GtkWidget *widget);
139 static void gtk_file_chooser_button_hide_all (GtkWidget *widget);
140 static void gtk_file_chooser_button_show (GtkWidget *widget);
141 static void gtk_file_chooser_button_hide (GtkWidget *widget);
142 static gboolean gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
143 gboolean group_cycling);
144 static void gtk_file_chooser_button_style_set (GtkWidget *widget,
145 GtkStyle *old_style);
146 static void gtk_file_chooser_button_screen_changed (GtkWidget *widget,
147 GdkScreen *old_screen);
149 /* Child Widget Callbacks */
150 static void dialog_update_preview_cb (GtkFileChooser *dialog,
152 static void dialog_selection_changed_cb (GtkFileChooser *dialog,
154 static void dialog_file_activated_cb (GtkFileChooser *dialog,
156 static void dialog_current_folder_changed_cb (GtkFileChooser *dialog,
158 static void dialog_notify_cb (GObject *dialog,
161 static gboolean dialog_delete_event_cb (GtkWidget *dialog,
164 static void dialog_response_cb (GtkDialog *dialog,
168 static void button_clicked_cb (GtkButton *real_button,
171 /* Utility Functions */
172 static void update_label_and_image (GtkFileChooserButton *button);
175 /* ******************* *
176 * GType Declaration *
177 * ******************* */
179 G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \
180 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, _gtk_file_chooser_delegate_iface_init) \
184 /* ***************** *
186 * ***************** */
189 gtk_file_chooser_button_class_init (GtkFileChooserButtonClass * class)
191 GObjectClass *gobject_class;
192 GtkObjectClass *gtkobject_class;
193 GtkWidgetClass *widget_class;
195 gobject_class = G_OBJECT_CLASS (class);
196 gtkobject_class = GTK_OBJECT_CLASS (class);
197 widget_class = GTK_WIDGET_CLASS (class);
199 gobject_class->constructor = gtk_file_chooser_button_constructor;
200 gobject_class->set_property = gtk_file_chooser_button_set_property;
201 gobject_class->get_property = gtk_file_chooser_button_get_property;
203 gtkobject_class->destroy = gtk_file_chooser_button_destroy;
205 widget_class->drag_data_received = gtk_file_chooser_button_drag_data_received;
206 widget_class->show_all = gtk_file_chooser_button_show_all;
207 widget_class->hide_all = gtk_file_chooser_button_hide_all;
208 widget_class->show = gtk_file_chooser_button_show;
209 widget_class->hide = gtk_file_chooser_button_hide;
210 widget_class->style_set = gtk_file_chooser_button_style_set;
211 widget_class->screen_changed = gtk_file_chooser_button_screen_changed;
212 widget_class->mnemonic_activate = gtk_file_chooser_button_mnemonic_activate;
215 * GtkFileChooserButton:dialog:
217 * Instance of the #GtkFileChooserDialog associated with the button.
221 g_object_class_install_property (gobject_class, PROP_DIALOG,
222 g_param_spec_object ("dialog",
224 P_("The file chooser dialog to use."),
225 GTK_TYPE_FILE_CHOOSER_DIALOG,
227 G_PARAM_CONSTRUCT_ONLY)));
230 * GtkFileChooserButton:title:
232 * Title to put on the #GtkFileChooserDialog associated with the button.
236 g_object_class_install_property (gobject_class, PROP_TITLE,
237 g_param_spec_string ("title",
239 P_("The title of the file chooser dialog."),
244 * GtkFileChooserButton:width-chars:
246 * The width of the entry and label inside the button, in characters.
250 g_object_class_install_property (gobject_class, PROP_WIDTH_CHARS,
251 g_param_spec_int ("width-chars",
252 P_("Width In Characters"),
253 P_("The desired width of the button widget, in characters."),
257 _gtk_file_chooser_install_properties (gobject_class);
259 g_type_class_add_private (class, sizeof (GtkFileChooserButtonPrivate));
264 gtk_file_chooser_button_init (GtkFileChooserButton *button)
266 GtkFileChooserButtonPrivate *priv;
267 GtkWidget *box, *image, *sep;
268 GtkTargetList *target_list;
270 priv = G_TYPE_INSTANCE_GET_PRIVATE (button, GTK_TYPE_FILE_CHOOSER_BUTTON,
271 GtkFileChooserButtonPrivate);
274 priv->icon_size = FALLBACK_ICON_SIZE;
276 gtk_widget_push_composite_child ();
278 priv->button = gtk_button_new ();
279 g_signal_connect (priv->button, "clicked", G_CALLBACK (button_clicked_cb),
281 gtk_container_add (GTK_CONTAINER (button), priv->button);
282 gtk_widget_show (priv->button);
284 box = gtk_hbox_new (FALSE, 4);
285 gtk_container_add (GTK_CONTAINER (priv->button), box);
286 gtk_widget_show (box);
288 priv->image = gtk_image_new ();
289 gtk_box_pack_start (GTK_BOX (box), priv->image, FALSE, FALSE, 0);
290 gtk_widget_show (priv->image);
292 priv->label = gtk_label_new (_(DEFAULT_FILENAME));
293 gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_START);
294 gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5);
295 gtk_container_add (GTK_CONTAINER (box), priv->label);
296 gtk_widget_show (priv->label);
298 sep = gtk_vseparator_new ();
299 gtk_box_pack_start (GTK_BOX (box), sep, FALSE, FALSE, 0);
300 gtk_widget_show (sep);
302 image = gtk_image_new_from_stock (GTK_STOCK_OPEN,
303 GTK_ICON_SIZE_SMALL_TOOLBAR);
304 gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 0);
305 gtk_widget_show (image);
307 gtk_widget_pop_composite_child ();
310 gtk_drag_dest_set (GTK_WIDGET (button),
311 (GTK_DEST_DEFAULT_ALL),
314 target_list = gtk_target_list_new (NULL, 0);
315 gtk_target_list_add_uri_targets (target_list, TEXT_URI_LIST);
316 gtk_target_list_add_text_targets (target_list, TEXT_PLAIN);
317 gtk_drag_dest_set_target_list (GTK_WIDGET (button), target_list);
318 gtk_target_list_unref (target_list);
322 /* ******************* *
323 * GObject Functions *
324 * ******************* */
327 gtk_file_chooser_button_constructor (GType type,
329 GObjectConstructParam *params)
332 GtkFileChooserButtonPrivate *priv;
334 object = (*G_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->constructor) (type,
337 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
342 priv->dialog = gtk_file_chooser_dialog_new_with_backend (NULL, NULL,
343 GTK_FILE_CHOOSER_ACTION_OPEN,
351 priv->dialog = gtk_file_chooser_dialog_new (NULL, NULL,
352 GTK_FILE_CHOOSER_ACTION_OPEN,
359 gtk_dialog_set_default_response (GTK_DIALOG (priv->dialog),
360 GTK_RESPONSE_ACCEPT);
361 gtk_dialog_set_alternative_button_order (GTK_DIALOG (priv->dialog),
367 g_free (priv->backend);
368 priv->backend = NULL;
370 g_signal_connect (priv->dialog, "delete-event",
371 G_CALLBACK (dialog_delete_event_cb), object);
372 g_signal_connect (priv->dialog, "response",
373 G_CALLBACK (dialog_response_cb), object);
375 /* This is used, instead of the standard delegate, to ensure that signals are only
376 * delegated when the OK button is pressed. */
377 g_object_set_qdata (object, GTK_FILE_CHOOSER_DELEGATE_QUARK, priv->dialog);
378 priv->dialog_folder_changed_id =
379 g_signal_connect (priv->dialog, "current-folder-changed",
380 G_CALLBACK (dialog_current_folder_changed_cb), object);
381 priv->dialog_file_activated_id =
382 g_signal_connect (priv->dialog, "file-activated",
383 G_CALLBACK (dialog_file_activated_cb), object);
384 priv->dialog_selection_changed_id =
385 g_signal_connect (priv->dialog, "selection-changed",
386 G_CALLBACK (dialog_selection_changed_cb), object);
387 g_signal_connect (priv->dialog, "update-preview",
388 G_CALLBACK (dialog_update_preview_cb), object);
389 g_signal_connect (priv->dialog, "notify",
390 G_CALLBACK (dialog_notify_cb), object);
391 g_object_add_weak_pointer (G_OBJECT (priv->dialog),
392 (gpointer *) (&priv->dialog));
398 gtk_file_chooser_button_set_property (GObject *object,
403 GtkFileChooserButtonPrivate *priv;
405 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
411 priv->dialog = g_value_get_object (value);
413 case PROP_WIDTH_CHARS:
414 gtk_file_chooser_button_set_width_chars (GTK_FILE_CHOOSER_BUTTON (object),
415 g_value_get_int (value));
418 case GTK_FILE_CHOOSER_PROP_ACTION:
419 switch (g_value_get_enum (value))
421 case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
422 case GTK_FILE_CHOOSER_ACTION_SAVE:
427 eclass = g_type_class_peek (GTK_TYPE_FILE_CHOOSER_ACTION);
428 eval = g_enum_get_value (eclass, g_value_get_enum (value));
429 g_warning ("%s: Choosers of type `%s' do not support `%s'.",
430 G_STRFUNC, G_OBJECT_TYPE_NAME (object), eval->value_name);
432 g_value_set_enum ((GValue *) value, GTK_FILE_CHOOSER_ACTION_OPEN);
437 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
438 update_label_and_image (GTK_FILE_CHOOSER_BUTTON (object));
442 case GTK_FILE_CHOOSER_PROP_FILTER:
443 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
444 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
445 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
446 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
447 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
448 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
449 g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value);
452 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
454 priv->backend = g_value_dup_string (value);
457 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
458 g_warning ("%s: Choosers of type `%s` do not support selecting multiple files.",
459 G_STRFUNC, G_OBJECT_TYPE_NAME (object));
462 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
468 gtk_file_chooser_button_get_property (GObject *object,
473 GtkFileChooserButtonPrivate *priv;
475 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
479 case PROP_WIDTH_CHARS:
480 g_value_set_int (value,
481 gtk_label_get_width_chars (GTK_LABEL (priv->label)));
485 case GTK_FILE_CHOOSER_PROP_ACTION:
486 case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND:
487 case GTK_FILE_CHOOSER_PROP_FILTER:
488 case GTK_FILE_CHOOSER_PROP_LOCAL_ONLY:
489 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET:
490 case GTK_FILE_CHOOSER_PROP_PREVIEW_WIDGET_ACTIVE:
491 case GTK_FILE_CHOOSER_PROP_USE_PREVIEW_LABEL:
492 case GTK_FILE_CHOOSER_PROP_EXTRA_WIDGET:
493 case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
494 case GTK_FILE_CHOOSER_PROP_SHOW_HIDDEN:
495 g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value);
499 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
505 /* ********************* *
506 * GtkObject Functions *
507 * ********************* */
510 gtk_file_chooser_button_destroy (GtkObject * object)
512 GtkFileChooserButtonPrivate *priv;
514 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object);
516 if (priv->dialog != NULL)
517 gtk_widget_destroy (priv->dialog);
520 gtk_file_path_free (priv->old_path);
522 if (GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy != NULL)
523 (*GTK_OBJECT_CLASS (gtk_file_chooser_button_parent_class)->destroy) (object);
527 /* ********************* *
528 * GtkWidget Functions *
529 * ********************* */
532 gtk_file_chooser_button_drag_data_received (GtkWidget *widget,
533 GdkDragContext *context,
536 GtkSelectionData *data,
540 GtkFileChooserButtonPrivate *priv;
545 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received != NULL)
546 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->drag_data_received) (widget,
552 if (widget == NULL || context == NULL || data == NULL || data->length < 0)
555 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
557 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
564 GtkFilePath *base_path;
568 uris = gtk_selection_data_get_uris (data);
574 for (i = 0; !selected && uris[i] != NULL; i++)
576 path = gtk_file_system_uri_to_path (fs, uris[i]);
580 gtk_file_system_get_parent (fs, path, &base_path, NULL))
582 GtkFileFolder *folder;
585 folder = gtk_file_system_get_folder (fs, base_path,
586 GTK_FILE_INFO_IS_FOLDER,
589 info = gtk_file_folder_get_info (folder, path, NULL);
593 GtkFileChooserAction action;
595 g_object_get (priv->dialog, "action", &action, NULL);
598 (((action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
599 gtk_file_info_get_is_folder (info)) ||
600 (action == GTK_FILE_CHOOSER_ACTION_OPEN &&
601 !gtk_file_info_get_is_folder (info))) &&
602 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog),
605 gtk_file_info_free (info);
610 gtk_file_path_free (base_path);
613 gtk_file_path_free (path);
621 text = gtk_selection_data_get_text (data);
622 path = gtk_file_path_new_steal (text);
623 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog), path,
625 gtk_file_path_free (path);
632 gtk_drag_finish (context, TRUE, FALSE, drag_time);
636 gtk_file_chooser_button_show_all (GtkWidget *widget)
638 gtk_widget_show (widget);
642 gtk_file_chooser_button_hide_all (GtkWidget *widget)
644 gtk_widget_hide (widget);
648 gtk_file_chooser_button_show (GtkWidget *widget)
650 GtkFileChooserButtonPrivate *priv;
652 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
654 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show)
655 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->show) (widget);
658 gtk_widget_show (priv->dialog);
662 gtk_file_chooser_button_hide (GtkWidget *widget)
664 GtkFileChooserButtonPrivate *priv;
666 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
668 gtk_widget_hide (priv->dialog);
670 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide)
671 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->hide) (widget);
675 gtk_file_chooser_button_mnemonic_activate (GtkWidget *widget,
676 gboolean group_cycling)
678 GtkFileChooserButtonPrivate *priv;
680 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget);
681 gtk_widget_grab_focus (priv->button);
686 /* Changes the icons wherever it is needed */
688 change_icon_theme (GtkFileChooserButton *button)
690 GtkSettings *settings;
693 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (button)));
695 if (gtk_icon_size_lookup_for_settings (settings, GTK_ICON_SIZE_SMALL_TOOLBAR,
697 button->priv->icon_size = MAX (width, height);
699 button->priv->icon_size = FALLBACK_ICON_SIZE;
701 update_label_and_image (button);
705 gtk_file_chooser_button_style_set (GtkWidget *widget,
708 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set)
709 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->style_set) (widget,
712 if (gtk_widget_has_screen (widget))
713 change_icon_theme (GTK_FILE_CHOOSER_BUTTON (widget));
717 gtk_file_chooser_button_screen_changed (GtkWidget *widget,
718 GdkScreen *old_screen)
720 GtkFileChooserButton *button;
722 button = GTK_FILE_CHOOSER_BUTTON (widget);
724 if (GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed)
725 (*GTK_WIDGET_CLASS (gtk_file_chooser_button_parent_class)->screen_changed) (widget,
728 change_icon_theme (button);
732 /* ************************************************************************** *
734 * ************************************************************************** */
737 * gtk_file_chooser_button_new:
738 * @title: the title of the browse dialog.
740 * Creates a new file-selecting button widget.
742 * Returns: a new button widget.
747 gtk_file_chooser_button_new (const gchar *title)
749 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
755 * gtk_file_chooser_button_new_with_backend:
756 * @title: the title of the browse dialog.
757 * @backend: the name of the #GtkFileSystem backend to use.
759 * Creates a new file-selecting button widget using @backend.
761 * Returns: a new button widget.
766 gtk_file_chooser_button_new_with_backend (const gchar *title,
767 const gchar *backend)
769 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
771 "file-system-backend", backend,
776 * gtk_file_chooser_button_new_with_dialog:
777 * @dialog: the #GtkDialog widget to use.
779 * Creates a #GtkFileChooserButton widget which uses @dialog as it's
780 * file-picking window. Note that @dialog must be a #GtkFileChooserDialog (or
783 * Returns: a new button widget.
788 gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog)
790 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL);
792 return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON,
798 * gtk_file_chooser_button_set_title:
799 * @button: the button widget to modify.
800 * @title: the new browse dialog title.
802 * Modifies the @title of the browse dialog used by @button.
807 gtk_file_chooser_button_set_title (GtkFileChooserButton *button,
810 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
812 gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title);
813 g_object_notify (G_OBJECT (button), "title");
817 * gtk_file_chooser_button_get_title:
818 * @button: the button widget to examine.
820 * Retrieves the title of the browse dialog used by @button. The returned value
821 * should not be modified or freed.
823 * Returns: a pointer to the browse dialog's title.
827 G_CONST_RETURN gchar *
828 gtk_file_chooser_button_get_title (GtkFileChooserButton *button)
830 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL);
832 return gtk_window_get_title (GTK_WINDOW (button->priv->dialog));
836 * gtk_file_chooser_button_get_width_chars:
837 * @button: the button widget to examine.
839 * Retrieves the width in characters of the @button widget's entry and/or label.
841 * Returns: an integer width (in characters) that the button will use to size itself.
846 gtk_file_chooser_button_get_width_chars (GtkFileChooserButton *button)
848 g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), -1);
850 return gtk_label_get_width_chars (GTK_LABEL (button->priv->label));
854 * gtk_file_chooser_button_set_width_chars:
855 * @button: the button widget to examine.
856 * @n_chars: the new width, in chracters.
858 * Sets the width (in characters) that @button will use to @n_chars.
863 gtk_file_chooser_button_set_width_chars (GtkFileChooserButton *button,
866 g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button));
868 gtk_label_set_width_chars (GTK_LABEL (button->priv->label), n_chars);
869 g_object_notify (G_OBJECT (button), "width-chars");
873 /* ******************* *
874 * Utility Functions *
875 * ******************* */
877 static inline GtkIconTheme *
878 get_icon_theme (GtkWidget *widget)
880 if (gtk_widget_has_screen (widget))
881 return gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
883 return gtk_icon_theme_get_default ();
887 update_label_and_image (GtkFileChooserButton *button)
889 GtkFileChooserButtonPrivate *priv;
894 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (button);
895 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (button->priv->dialog));
902 GtkFilePath *path, *parent_path;
903 GtkFileSystemVolume *volume;
904 GtkFileFolder *folder;
908 fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog));
910 volume = gtk_file_system_get_volume_for_path (fs, path);
913 GtkFilePath *base_path;
915 base_path = gtk_file_system_volume_get_base_path (fs, volume);
916 if (base_path && gtk_file_path_compare (base_path, path) == 0)
918 label_text = gtk_file_system_volume_get_display_name (fs, volume);
919 pixbuf = gtk_file_system_volume_render_icon (fs, volume,
926 gtk_file_path_free (base_path);
928 gtk_file_system_volume_free (fs, volume);
935 pixbuf = gtk_file_system_render_icon (fs, path, GTK_WIDGET (button),
936 priv->icon_size, NULL);
939 gtk_file_system_get_parent (fs, path, &parent_path, NULL);
941 folder = gtk_file_system_get_folder (fs, parent_path ? parent_path : path,
942 GTK_FILE_INFO_DISPLAY_NAME, NULL);
947 info = gtk_file_folder_get_info (folder, path, NULL);
948 g_object_unref (folder);
952 label_text = g_strdup (gtk_file_info_get_display_name (info));
953 gtk_file_info_free (info);
958 gtk_file_paths_free (paths);
963 gtk_label_set_text (GTK_LABEL (priv->label), label_text);
967 gtk_label_set_text (GTK_LABEL (priv->label), _(DEFAULT_FILENAME));
970 pixbuf = gtk_icon_theme_load_icon (get_icon_theme (GTK_WIDGET (button)),
972 priv->icon_size, 0, NULL);
974 gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
976 g_object_unref (pixbuf);
979 /* ************************ *
980 * Child-Widget Callbacks *
981 * ************************ */
984 dialog_current_folder_changed_cb (GtkFileChooser *dialog,
987 g_signal_emit_by_name (user_data, "current-folder-changed");
991 dialog_file_activated_cb (GtkFileChooser *dialog,
994 g_signal_emit_by_name (user_data, "file-activated");
998 dialog_selection_changed_cb (GtkFileChooser *dialog,
1001 update_label_and_image (user_data);
1002 g_signal_emit_by_name (user_data, "selection-changed");
1006 dialog_update_preview_cb (GtkFileChooser *dialog,
1009 g_signal_emit_by_name (user_data, "update-preview");
1013 dialog_notify_cb (GObject *dialog,
1019 iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)),
1020 GTK_TYPE_FILE_CHOOSER);
1021 if (g_object_interface_find_property (iface, pspec->name))
1022 g_object_notify (user_data, pspec->name);
1026 dialog_delete_event_cb (GtkWidget *dialog,
1030 g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT);
1036 dialog_response_cb (GtkDialog *dialog,
1040 GtkFileChooserButtonPrivate *priv;
1042 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1044 if (response == GTK_RESPONSE_ACCEPT)
1046 g_signal_emit_by_name (user_data, "current-folder-changed");
1047 g_signal_emit_by_name (user_data, "selection-changed");
1049 else if (priv->old_path)
1051 switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (dialog)))
1053 case GTK_FILE_CHOOSER_ACTION_OPEN:
1054 _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (dialog), priv->old_path,
1057 case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
1058 _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (dialog),
1059 priv->old_path, NULL);
1062 g_assert_not_reached ();
1069 gtk_file_path_free (priv->old_path);
1070 priv->old_path = NULL;
1073 update_label_and_image (user_data);
1075 g_signal_handler_unblock (priv->dialog,
1076 priv->dialog_folder_changed_id);
1077 g_signal_handler_unblock (priv->dialog,
1078 priv->dialog_file_activated_id);
1079 g_signal_handler_unblock (priv->dialog,
1080 priv->dialog_selection_changed_id);
1081 priv->active = FALSE;
1082 gtk_widget_hide (priv->dialog);
1087 button_clicked_cb (GtkButton *real_button,
1090 GtkFileChooserButtonPrivate *priv;
1092 priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data);
1098 /* Setup the dialog parent to be chooser button's toplevel, and be modal
1100 if (!GTK_WIDGET_VISIBLE (priv->dialog))
1102 GtkWidget *toplevel;
1104 toplevel = gtk_widget_get_toplevel (user_data);
1106 if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel))
1108 if (GTK_WINDOW (toplevel) != gtk_window_get_transient_for (GTK_WINDOW (priv->dialog)))
1109 gtk_window_set_transient_for (GTK_WINDOW (priv->dialog),
1110 GTK_WINDOW (toplevel));
1112 gtk_window_set_modal (GTK_WINDOW (priv->dialog),
1113 gtk_window_get_modal (GTK_WINDOW (toplevel)));
1117 g_signal_handler_block (priv->dialog,
1118 priv->dialog_folder_changed_id);
1119 g_signal_handler_block (priv->dialog,
1120 priv->dialog_file_activated_id);
1121 g_signal_handler_block (priv->dialog,
1122 priv->dialog_selection_changed_id);
1123 paths = _gtk_file_chooser_get_paths (GTK_FILE_CHOOSER (priv->dialog));
1127 priv->old_path = gtk_file_path_copy (paths->data);
1129 gtk_file_paths_free (paths);
1132 priv->active = TRUE;
1135 gtk_window_present (GTK_WINDOW (priv->dialog));