diff options
-rw-r--r-- | ChangeLog | 23 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 23 | ||||
-rw-r--r-- | ChangeLog.pre-2-6 | 23 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 23 | ||||
-rw-r--r-- | gtk/Makefile.am | 3 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtk.symbols | 8 | ||||
-rw-r--r-- | gtk/gtkfilechooserbutton.c | 1122 | ||||
-rw-r--r-- | gtk/gtkfilechooserbutton.h | 89 | ||||
-rw-r--r-- | gtk/gtkfilechooserdefault.c | 4 | ||||
-rw-r--r-- | gtk/gtkfilechooserentry.c | 14 | ||||
-rw-r--r-- | gtk/gtkfilechooserentry.h | 2 | ||||
-rw-r--r-- | gtk/gtkfilechooserutils.c | 14 | ||||
-rw-r--r-- | gtk/gtkfilechooserutils.h | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 7 | ||||
-rw-r--r-- | tests/testfilechooserbutton.c | 167 |
16 files changed, 1520 insertions, 7 deletions
@@ -1,3 +1,26 @@ +2004-08-17 Matthias Clasen <mclasen@redhat.com> + + * gtk/gtkfilechooserbutton.[hc]: New widget to go along with + GtkFontButton and GtkColorButton for use in preference dialogs. + Replaces GnomeFileEntry. (#148108, James M. Cape) + + * gtk/gtk.h: Include gtkfilechooserbutton.h + + * gtk/Makefile.am (gtk_public_h_sources): Add gtkfilechooserbutton.h + (gtk_c_sources): Add gtkfilechooserbutton.c + + * gtk/gtk.symbols: Add the GtkFileChooserButton symbols. + + * gtk/gtkfilechooserutils.[hc]: Make the delegate quark available. + + * gtk/gtkfilechooserentry.[hc] (_gtk_file_chooser_entry_new): Allow + to suppress tab-eating using the new eat_tabs argument. Adjust all + callers. + + * tests/testfilechooserbutton.c: Test for GtkFileChooserButton. + + * tests/Makefile.am (noinst_PROGRAMS): Add testfilechooserbutton + 2004-08-16 Matthias Clasen <mclasen@redhat.com> * gtk/gtkwidget.c (gtk_widget_render_icon): Doc update. diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index ec2d48de32..ef058165c6 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,26 @@ +2004-08-17 Matthias Clasen <mclasen@redhat.com> + + * gtk/gtkfilechooserbutton.[hc]: New widget to go along with + GtkFontButton and GtkColorButton for use in preference dialogs. + Replaces GnomeFileEntry. (#148108, James M. Cape) + + * gtk/gtk.h: Include gtkfilechooserbutton.h + + * gtk/Makefile.am (gtk_public_h_sources): Add gtkfilechooserbutton.h + (gtk_c_sources): Add gtkfilechooserbutton.c + + * gtk/gtk.symbols: Add the GtkFileChooserButton symbols. + + * gtk/gtkfilechooserutils.[hc]: Make the delegate quark available. + + * gtk/gtkfilechooserentry.[hc] (_gtk_file_chooser_entry_new): Allow + to suppress tab-eating using the new eat_tabs argument. Adjust all + callers. + + * tests/testfilechooserbutton.c: Test for GtkFileChooserButton. + + * tests/Makefile.am (noinst_PROGRAMS): Add testfilechooserbutton + 2004-08-16 Matthias Clasen <mclasen@redhat.com> * gtk/gtkwidget.c (gtk_widget_render_icon): Doc update. diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index ec2d48de32..ef058165c6 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,26 @@ +2004-08-17 Matthias Clasen <mclasen@redhat.com> + + * gtk/gtkfilechooserbutton.[hc]: New widget to go along with + GtkFontButton and GtkColorButton for use in preference dialogs. + Replaces GnomeFileEntry. (#148108, James M. Cape) + + * gtk/gtk.h: Include gtkfilechooserbutton.h + + * gtk/Makefile.am (gtk_public_h_sources): Add gtkfilechooserbutton.h + (gtk_c_sources): Add gtkfilechooserbutton.c + + * gtk/gtk.symbols: Add the GtkFileChooserButton symbols. + + * gtk/gtkfilechooserutils.[hc]: Make the delegate quark available. + + * gtk/gtkfilechooserentry.[hc] (_gtk_file_chooser_entry_new): Allow + to suppress tab-eating using the new eat_tabs argument. Adjust all + callers. + + * tests/testfilechooserbutton.c: Test for GtkFileChooserButton. + + * tests/Makefile.am (noinst_PROGRAMS): Add testfilechooserbutton + 2004-08-16 Matthias Clasen <mclasen@redhat.com> * gtk/gtkwidget.c (gtk_widget_render_icon): Doc update. diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index ec2d48de32..ef058165c6 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,26 @@ +2004-08-17 Matthias Clasen <mclasen@redhat.com> + + * gtk/gtkfilechooserbutton.[hc]: New widget to go along with + GtkFontButton and GtkColorButton for use in preference dialogs. + Replaces GnomeFileEntry. (#148108, James M. Cape) + + * gtk/gtk.h: Include gtkfilechooserbutton.h + + * gtk/Makefile.am (gtk_public_h_sources): Add gtkfilechooserbutton.h + (gtk_c_sources): Add gtkfilechooserbutton.c + + * gtk/gtk.symbols: Add the GtkFileChooserButton symbols. + + * gtk/gtkfilechooserutils.[hc]: Make the delegate quark available. + + * gtk/gtkfilechooserentry.[hc] (_gtk_file_chooser_entry_new): Allow + to suppress tab-eating using the new eat_tabs argument. Adjust all + callers. + + * tests/testfilechooserbutton.c: Test for GtkFileChooserButton. + + * tests/Makefile.am (noinst_PROGRAMS): Add testfilechooserbutton + 2004-08-16 Matthias Clasen <mclasen@redhat.com> * gtk/gtkwidget.c (gtk_widget_render_icon): Doc update. diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 26e223ac9a..2feac995f1 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -159,6 +159,7 @@ gtk_public_h_sources = \ gtkeventbox.h \ gtkexpander.h \ gtkfilechooser.h \ + gtkfilechooserbutton.h \ gtkfilechooserdialog.h \ gtkfilechooserwidget.h \ gtkfilefilter.h \ @@ -363,6 +364,7 @@ gtk_c_sources = \ gtkeventbox.c \ gtkexpander.c \ gtkfilechooser.c \ + gtkfilechooserbutton.c \ gtkfilechooserdialog.c \ gtkfilechooserembed.c \ gtkfilechooserentry.c \ @@ -731,4 +733,3 @@ EXTRA_DIST += \ abicheck.sh install-data-local: - @@ -78,6 +78,7 @@ #include <gtk/gtkexpander.h> #include <gtk/gtkfilesel.h> #include <gtk/gtkfixed.h> +#include <gtk/gtkfilechooserbutton.h> #include <gtk/gtkfilechooserdialog.h> #include <gtk/gtkfilechooserwidget.h> #include <gtk/gtkfontbutton.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index 026ad7cd8c..78e906706a 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -749,6 +749,14 @@ gtk_file_chooser_action_get_type gtk_file_chooser_add_filter gtk_file_chooser_add_shortcut_folder gtk_file_chooser_add_shortcut_folder_uri +gtk_file_chooser_button_get_active +gtk_file_chooser_button_get_type +gtk_file_chooser_button_get_title +gtk_file_chooser_button_new +gtk_file_chooser_button_new_with_backend +gtk_file_chooser_button_new_with_dialog +gtk_file_chooser_button_set_active +gtk_file_chooser_button_set_title gtk_file_chooser_dialog_get_type gtk_file_chooser_dialog_new gtk_file_chooser_dialog_new_with_backend diff --git a/gtk/gtkfilechooserbutton.c b/gtk/gtkfilechooserbutton.c new file mode 100644 index 0000000000..dfc1b68c41 --- /dev/null +++ b/gtk/gtkfilechooserbutton.c @@ -0,0 +1,1122 @@ +/* -*- 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. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string.h> + +#include "gtkalias.h" +#include "gtkintl.h" +#include "gtkdnd.h" +#include "gtkentry.h" +#include "gtkhbox.h" +#include "gtkicontheme.h" +#include "gtkimage.h" +#include "gtklabel.h" +#include "gtkstock.h" +#include "gtktogglebutton.h" +#include "gtkvseparator.h" +#include "gtkfilechooserdialog.h" +#include "gtkfilechooserentry.h" +#include "gtkfilechooserprivate.h" +#include "gtkfilechooserutils.h" + +#include "gtkfilechooserbutton.h" + + +/* **************** * + * Private Macros * + * **************** */ + +#define GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE(object) (GTK_FILE_CHOOSER_BUTTON ((object))->priv) + +#define DEFAULT_TITLE N_("Select a File") +#define DEFAULT_FILENAME N_("(None)") +#define DEFAULT_SPACING 0 + + +/* ********************** * + * Private Enumerations * + * ********************** */ + +/* Property IDs */ +enum +{ + PROP_0, + + PROP_DIALOG, + PROP_TITLE, + PROP_ACTIVE +}; + + +/* ******************** * + * Private Structures * + * ******************** */ + +struct _GtkFileChooserButtonPrivate +{ + GtkWidget *dialog; + GtkWidget *entry; + GtkWidget *label; + GtkWidget *separator; + GtkWidget *button; + + gchar *filesystem; + gulong entry_changed_id; + gulong dialog_file_activated_id; + gulong dialog_folder_changed_id; + gulong dialog_selection_changed_id; + guint update_id; +}; + + +/* ************* * + * DnD Support * + * ************* */ + +enum +{ + TEXT_URI_LIST, + TEXT_PLAIN +}; + +static const GtkTargetEntry drop_targets[] = { + { "text/uri-list", 0, TEXT_URI_LIST }, + { "text/plain", 0, TEXT_PLAIN } +}; + + +/* ********************* * + * Function Prototypes * + * ********************* */ + +/* GObject Functions */ +static void gtk_file_chooser_button_set_property (GObject *object, + guint id, + const GValue *value, + GParamSpec *pspec); +static void gtk_file_chooser_button_get_property (GObject *object, + guint id, + GValue *value, + GParamSpec *pspec); + +/* 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, + guint info, + guint drag_time); +static void gtk_file_chooser_button_show_all (GtkWidget *widget); +static void gtk_file_chooser_button_hide_all (GtkWidget *widget); + +/* Child Widget Callbacks */ +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 (GtkFileChooser *dialog, + gint response, + gpointer user_data); + +static void button_toggled_cb (GtkToggleButton *real_button, + gpointer user_data); +static void button_notify_active_cb (GObject *real_button, + GParamSpec *pspec, + gpointer user_data); + +static void entry_size_allocate_cb (GtkWidget *entry, + GtkAllocation *allocation, + gpointer user_data); +static void entry_changed_cb (GtkEditable *chooser_entry, + gpointer user_data); + +/* Utility Functions */ +static void gtk_file_chooser_button_set_dialog (GObject *object, + GtkWidget *dialog); + +static gboolean update_dialog (gpointer user_data); +static void update_entry (GtkFileChooserButton *button); + + +/* ******************* * + * GType Declaration * + * ******************* */ + +G_DEFINE_TYPE_WITH_CODE (GtkFileChooserButton, gtk_file_chooser_button, GTK_TYPE_HBOX, { \ + G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_CHOOSER, _gtk_file_chooser_delegate_iface_init) \ +}); + + +/* ***************** * + * 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); + + gobject_class->set_property = gtk_file_chooser_button_set_property; + gobject_class->get_property = gtk_file_chooser_button_get_property; + + 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; + + g_object_class_install_property (gobject_class, PROP_DIALOG, + g_param_spec_object ("dialog", + P_("Dialog"), + P_("The file chooser dialog to use."), + GTK_TYPE_FILE_CHOOSER_DIALOG, + (G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY))); + g_object_class_install_property (gobject_class, PROP_TITLE, + g_param_spec_string ("title", + P_("Title"), + P_("The title of the file chooser dialog."), + _(DEFAULT_TITLE), + G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_ACTIVE, + g_param_spec_boolean ("active", + P_("Active"), + P_("Whether the browse dialog is visible or not."), + FALSE, G_PARAM_READWRITE)); + + _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; + GtkWidget *box, *image; + + gtk_box_set_spacing (GTK_BOX (button), DEFAULT_SPACING); + + priv = G_TYPE_INSTANCE_GET_PRIVATE (button, GTK_TYPE_FILE_CHOOSER_BUTTON, + GtkFileChooserButtonPrivate); + button->priv = priv; + + gtk_widget_push_composite_child (); + + priv->entry = _gtk_file_chooser_entry_new (FALSE); + gtk_container_add (GTK_CONTAINER (button), priv->entry); + + priv->button = gtk_toggle_button_new (); + g_signal_connect (priv->button, "toggled", + G_CALLBACK (button_toggled_cb), button); + g_signal_connect (priv->button, "notify::active", + G_CALLBACK (button_notify_active_cb), button); + g_signal_connect (priv->entry, "size-allocate", + G_CALLBACK (entry_size_allocate_cb), priv->button); + gtk_box_pack_start (GTK_BOX (button), priv->button, TRUE, TRUE, 0); + gtk_widget_show (priv->button); + + box = gtk_hbox_new (FALSE, 4); + gtk_container_add (GTK_CONTAINER (priv->button), box); + gtk_widget_show (box); + + priv->label = gtk_label_new (_(DEFAULT_FILENAME)); + gtk_label_set_ellipsize (GTK_LABEL (priv->label), PANGO_ELLIPSIZE_START); + gtk_misc_set_alignment (GTK_MISC (priv->label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (box), priv->label, TRUE, TRUE, 2); + gtk_widget_show (priv->label); + + image = gtk_image_new_from_stock (GTK_STOCK_OPEN, + GTK_ICON_SIZE_SMALL_TOOLBAR); + gtk_box_pack_end (GTK_BOX (box), image, FALSE, FALSE, 0); + gtk_widget_show (image); + + priv->separator = gtk_vseparator_new (); + gtk_box_pack_end (GTK_BOX (box), priv->separator, FALSE, FALSE, 0); + gtk_widget_show (priv->separator); + + gtk_widget_pop_composite_child (); + + /* DnD */ + gtk_drag_dest_unset (priv->entry); + gtk_drag_dest_set (GTK_WIDGET (button), + (GTK_DEST_DEFAULT_MOTION | + GTK_DEST_DEFAULT_HIGHLIGHT | + GTK_DEST_DEFAULT_DROP), + drop_targets, G_N_ELEMENTS (drop_targets), + GDK_ACTION_COPY); +} + + +/* ******************* * + * GObject Functions * + * ******************* */ + + +static void +gtk_file_chooser_button_set_property (GObject *object, + guint id, + const GValue *value, + GParamSpec *pspec) +{ + GtkFileChooserButtonPrivate *priv; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object); + + switch (id) + { + case PROP_DIALOG: + { + GtkWidget *widget; + + widget = g_value_get_object (value); + + if (widget == NULL) + { + widget = g_object_new (GTK_TYPE_FILE_CHOOSER_DIALOG, + "file-system-backend", priv->filesystem, NULL); + g_free (priv->filesystem); + priv->filesystem = NULL; + + gtk_dialog_add_button (GTK_DIALOG (widget), + GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT); + gtk_dialog_add_button (GTK_DIALOG (widget), + GTK_STOCK_OK, GTK_RESPONSE_ACCEPT); + } + + gtk_file_chooser_button_set_dialog (object, widget); + } + break; + case PROP_ACTIVE: + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), + g_value_get_boolean (value)); + break; + + case GTK_FILE_CHOOSER_PROP_ACTION: + g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); + + switch (g_value_get_enum (value)) + { + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); + /* Fall through to set the widget states */ + case GTK_FILE_CHOOSER_ACTION_OPEN: + gtk_widget_hide (priv->entry); + gtk_widget_show (priv->label); + gtk_widget_show (priv->separator); + gtk_box_set_child_packing (GTK_BOX (object), priv->button, + TRUE, TRUE, 0, GTK_PACK_START); + break; + + case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER: + gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog)); + /* Fall through to set the widget states */ + case GTK_FILE_CHOOSER_ACTION_SAVE: + gtk_widget_show (priv->entry); + gtk_widget_hide (priv->label); + gtk_widget_hide (priv->separator); + gtk_box_set_child_packing (GTK_BOX (object), priv->button, + FALSE, FALSE, 0, GTK_PACK_START); + break; + } + break; + + case PROP_TITLE: + 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_SHOW_HIDDEN: + g_object_set_property (G_OBJECT (priv->dialog), pspec->name, value); + break; + + case GTK_FILE_CHOOSER_PROP_FILE_SYSTEM_BACKEND: + /* Construct-only */ + priv->filesystem = g_value_dup_string (value); + 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: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); + break; + } +} + + +static void +gtk_file_chooser_button_get_property (GObject *object, + guint id, + GValue *value, + GParamSpec *pspec) +{ + GtkFileChooserButtonPrivate *priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object); + + switch (id) + { + case PROP_ACTIVE: + g_value_set_boolean (value, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button))); + break; + + 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: + g_object_get_property (G_OBJECT (priv->dialog), pspec->name, value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec); + break; + } +} + + +/* ********************* * + * GtkObject Functions * + * ********************* */ + +static void +gtk_file_chooser_button_destroy (GtkObject * object) +{ + GtkFileChooserButtonPrivate *priv; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object); + + if (priv->dialog != NULL) + gtk_widget_destroy (priv->dialog); + + if (priv->update_id != 0) + g_source_remove (priv->update_id); + + 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 * + * ********************* */ + +static void +gtk_file_chooser_button_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint drag_time) +{ + GtkFileChooserButtonPrivate *priv; + + 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, + data, info, + drag_time); + + if (widget == NULL || context == NULL || data == NULL || data->length < 0) + return; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (widget); + + switch (info) + { + case TEXT_URI_LIST: + { + gchar **uris; + guint i; + gboolean selected; + + uris = g_strsplit (data->data, "\r\n", -1); + + if (uris == NULL) + break; + + selected = FALSE; + g_signal_handler_block (priv->entry, priv->entry_changed_id); + for (i = 0; !selected && uris[i] != NULL; i++) + { + GtkFileSystem *fs; + GtkFilePath *path, *base_path; + + fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)); + path = gtk_file_system_uri_to_path (fs, uris[i]); + + base_path = NULL; + if (path != NULL && + gtk_file_system_get_parent (fs, path, &base_path, NULL)) + { + GtkFileFolder *folder; + GtkFileInfo *info; + + folder = gtk_file_system_get_folder (fs, base_path, + GTK_FILE_INFO_IS_FOLDER, + NULL); + + info = gtk_file_folder_get_info (folder, base_path, NULL); + + if (info != NULL) + { + GtkFileChooserAction action; + + g_object_get (priv->dialog, "action", &action, NULL); + + selected = + ((((action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER || + action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER) && + gtk_file_info_get_is_folder (info)) || + ((action == GTK_FILE_CHOOSER_ACTION_OPEN || + action == GTK_FILE_CHOOSER_ACTION_SAVE) && + !gtk_file_info_get_is_folder (info))) && + _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog), + path, NULL)); + + gtk_file_info_free (info); + } + else + selected = FALSE; + + gtk_file_path_free (base_path); + } + + gtk_file_path_free (path); + } + g_signal_handler_unblock (priv->entry, priv->entry_changed_id); + + g_strfreev (uris); + } + break; + + case TEXT_PLAIN: + gtk_entry_set_text (GTK_ENTRY (priv->entry), data->data); + break; + } + + gtk_drag_finish (context, FALSE, FALSE, drag_time); +} + + +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); +} + + +/* ************************************************************************** * + * Public API * + * ************************************************************************** */ + +/** + * gtk_file_chooser_button_new: + * @title: the title of the browse dialog. + * + * Creates a new file-selecting button widget. + * + * Returns: a new button widget. + * + * Since: 2.6 + **/ +GtkWidget * +gtk_file_chooser_button_new (const gchar *title) +{ + return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON, "title", title, NULL); +} + +/** + * gtk_file_chooser_button_new_with_backend: + * @title: the title of the browse dialog. + * @backend: the name of the #GtkFileSystem backend to use. + * + * Creates a new file-selecting button widget using @backend. + * + * Returns: a new button widget. + * + * Since: 2.6 + **/ +GtkWidget * +gtk_file_chooser_button_new_with_backend (const gchar *title, + const gchar *backend) +{ + return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON, "title", title, + "file-system-backend", backend, NULL); +} + + +/** + * gtk_file_chooser_button_new_with_dialog: + * @dialog: the #GtkDialog widget to use. + * + * Creates a #GtkFileChooserButton widget which uses @dialog as it's + * file-picking window. Note that @dialog must be a #GtkFileChooserDialog (or + * subclass). + * + * Returns: a new button widget. + * + * Since: 2.6 + **/ +GtkWidget * +gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog) +{ + g_return_val_if_fail (GTK_IS_FILE_CHOOSER_DIALOG (dialog), NULL); + + return g_object_new (GTK_TYPE_FILE_CHOOSER_BUTTON, "dialog", dialog, NULL); +} + + +/** + * gtk_file_chooser_button_set_title: + * @button: the button widget to modify. + * @title: the new browse dialog title. + * + * Modifies the @title of the browse dialog used by @button. + * + * Since: 2.6 + **/ +void +gtk_file_chooser_button_set_title (GtkFileChooserButton *button, + const gchar *title) +{ + g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button)); + + gtk_window_set_title (GTK_WINDOW (button->priv->dialog), title); + g_object_notify (G_OBJECT (button), "title"); +} + + +/** + * gtk_file_chooser_button_get_title: + * @button: the button widget to examine. + * + * Retrieves the title of the browse dialog used by @button. The returned value + * should not be modified or freed. + * + * Returns: a pointer to the browse dialog's title. + * + * Since: 2.6 + **/ +G_CONST_RETURN gchar * +gtk_file_chooser_button_get_title (GtkFileChooserButton *button) +{ + g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), NULL); + + return gtk_window_get_title (GTK_WINDOW (button->priv->dialog)); +} + + +/** + * gtk_file_chooser_button_set_active: + * @button: the button widget to modify. + * @is_active: whether or not the dialog is visible. + * + * Modifies whether or not the dialog attached to @button is visible or not. + * + * Since: 2.6 + **/ +void +gtk_file_chooser_button_set_active (GtkFileChooserButton *button, + gboolean is_active) +{ + g_return_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button)); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button->priv->button), is_active); +} + + +/** + * gtk_file_chooser_button_get_active: + * @button: the button widget to examine. + * + * Retrieves whether or not the dialog attached to @button is visible. + * + * Returns: a boolean whether the dialog is visible or not. + * + * Since: 2.6 + **/ +gboolean +gtk_file_chooser_button_get_active (GtkFileChooserButton *button) +{ + g_return_val_if_fail (GTK_IS_FILE_CHOOSER_BUTTON (button), FALSE); + + return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button->priv->button)); +} + + +/* ******************* * + * Utility Functions * + * ******************* */ + +static void +gtk_file_chooser_button_set_dialog (GObject *object, + GtkWidget *dialog) +{ + GtkFileChooserButtonPrivate *priv; + GtkFilePath *path; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (object); + + priv->dialog = dialog; + + g_signal_connect (priv->dialog, "delete-event", + 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), + (gpointer *) (&priv->dialog)); + + /* Kinda ugly to set this here... */ + _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (priv->entry), + _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog))); + path = gtk_file_path_new_steal ("file:///"); + _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (priv->entry), + path); + priv->entry_changed_id = g_signal_connect_after (priv->entry, "changed", + G_CALLBACK (entry_changed_cb), + object); +} + + +static gchar * +get_display_name (gchar *filename) +{ + const gchar *home_dir; + gchar *tmp; + gsize filename_len, home_dir_len; + + filename_len = strlen (filename); + + if (g_file_test (filename, G_FILE_TEST_IS_DIR)) + { + tmp = g_new (gchar, filename_len + 2); + strcpy (tmp, filename); + tmp[filename_len] = '/'; + tmp[filename_len + 1] = '\0'; + g_free (filename); + filename = tmp; + } + + home_dir = g_get_home_dir (); + if (home_dir != NULL) + { + home_dir_len = strlen (home_dir); + + if (strncmp (home_dir, filename, home_dir_len) == 0) + { + tmp = g_build_filename ("~", filename + home_dir_len, NULL); + g_free (filename); + filename = tmp; + } + } + + return filename; +} + + +static void +update_entry (GtkFileChooserButton *button) +{ + gchar *filename; + + switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (button->priv->dialog))) + { + case GTK_FILE_CHOOSER_ACTION_OPEN: + case GTK_FILE_CHOOSER_ACTION_SAVE: + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (button->priv->dialog)); + break; + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER: + filename = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (button->priv->dialog)); + break; + default: + g_assert_not_reached (); + filename = NULL; + break; + } + + if (filename != NULL) + filename = get_display_name (filename); + + g_signal_handler_block (button->priv->entry, button->priv->entry_changed_id); + if (filename != NULL) + gtk_entry_set_text (GTK_ENTRY (button->priv->entry), filename); + else + gtk_entry_set_text (GTK_ENTRY (button->priv->entry), ""); + g_signal_handler_unblock (button->priv->entry, button->priv->entry_changed_id); + + if (filename != NULL) + gtk_label_set_text (GTK_LABEL (button->priv->label), filename); + else + gtk_label_set_text (GTK_LABEL (button->priv->label), _(DEFAULT_FILENAME)); + g_free (filename); +} + + +static gboolean +update_dialog (gpointer user_data) +{ + GtkFileChooserButtonPrivate *priv; + const GtkFilePath *folder_path; + const gchar *file_part; + gchar *full_uri; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data); + folder_path = + _gtk_file_chooser_entry_get_current_folder (GTK_FILE_CHOOSER_ENTRY (priv->entry)); + file_part = + _gtk_file_chooser_entry_get_file_part (GTK_FILE_CHOOSER_ENTRY (priv->entry)); + + if (folder_path != NULL) + full_uri = g_build_filename (gtk_file_path_get_string (folder_path), + file_part, NULL); + else if (file_part != NULL) + full_uri = g_build_filename ("file://", file_part, NULL); + else + full_uri = NULL; + + if (full_uri != NULL) + { + gchar *display_name; + + display_name = g_filename_from_uri (full_uri, NULL, NULL); + display_name = get_display_name (display_name); + gtk_label_set_text (GTK_LABEL (priv->label), display_name); + g_free (display_name); + } + else + { + gtk_label_set_text (GTK_LABEL (priv->label), _(DEFAULT_FILENAME)); + } + + switch (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog))) + { + case GTK_FILE_CHOOSER_ACTION_OPEN: + if (folder_path != NULL) + { + GtkFileSystem *fs; + GtkFileFolder *folder; + GtkFilePath *full_path; + GtkFileInfo *info; + + fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)); + folder = gtk_file_system_get_folder (fs, folder_path, + GTK_FILE_INFO_IS_FOLDER, NULL); + + full_path = gtk_file_system_make_path (fs, folder_path, file_part, NULL); + info = gtk_file_folder_get_info (folder, full_path, NULL); + + /* Entry contents don't exist. */ + if (info == NULL) + _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog), + folder_path, NULL); + /* Entry contents are a folder */ + else if (gtk_file_info_get_is_folder (info)) + _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog), + full_path, NULL); + /* Entry contents must be a file. */ + else + _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog), + full_path, NULL); + + gtk_file_info_free (info); + gtk_file_path_free (full_path); + } + else + g_free (full_uri); + break; + case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER: + if (folder_path != NULL) + { + GtkFileSystem *fs; + GtkFilePath *full_path; + + fs = _gtk_file_chooser_get_file_system (GTK_FILE_CHOOSER (priv->dialog)); + full_path = gtk_file_system_make_path (fs, folder_path, file_part, NULL); + + /* Entry contents don't exist. */ + if (full_path != NULL) + _gtk_file_chooser_select_path (GTK_FILE_CHOOSER (priv->dialog), + full_path, NULL); + else + _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog), + folder_path, NULL); + + gtk_file_path_free (full_path); + } + else + g_free (full_uri); + break; + + case GTK_FILE_CHOOSER_ACTION_SAVE: + case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER: + if (folder_path != NULL) + _gtk_file_chooser_set_current_folder_path (GTK_FILE_CHOOSER (priv->dialog), + folder_path, NULL); + + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (priv->dialog), + file_part); + g_free (full_uri); + break; + } + + priv->update_id = 0; + return FALSE; +} + +/* ************************ * + * Child-Widget Callbacks * + * ************************ */ + +static void +dialog_current_folder_changed_cb (GtkFileChooser *dialog, + gpointer user_data) +{ + g_signal_emit_by_name (user_data, "current-folder-changed"); +} + + +static void +dialog_file_activated_cb (GtkFileChooser *dialog, + gpointer user_data) +{ + g_signal_emit_by_name (user_data, "file-activated"); +} + + +static void +dialog_selection_changed_cb (GtkFileChooser *dialog, + gpointer user_data) +{ + update_entry (user_data); + g_signal_emit_by_name (user_data, "selection-changed"); +} + + +static void +dialog_update_preview_cb (GtkFileChooser *dialog, + gpointer user_data) +{ + g_signal_emit_by_name (user_data, "update-preview"); +} + + +static void +dialog_notify_cb (GObject *dialog, + GParamSpec *pspec, + gpointer user_data) +{ + gpointer iface; + + iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (dialog)), + GTK_TYPE_FILE_CHOOSER); + if (g_object_interface_find_property (iface, pspec->name)) + g_object_notify (user_data, pspec->name); +} + + +static gboolean +dialog_delete_event_cb (GtkWidget *dialog, + GdkEvent *event, + gpointer user_data) +{ + g_signal_emit_by_name (dialog, "response", GTK_RESPONSE_DELETE_EVENT); + + return TRUE; +} + + +static void +dialog_response_cb (GtkFileChooser *dialog, + gint response, + gpointer user_data) +{ + GtkFileChooserButtonPrivate *priv; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data); + + if (response == GTK_RESPONSE_ACCEPT) + { + update_entry (user_data); + + g_signal_emit_by_name (user_data, "current-folder-changed"); + g_signal_emit_by_name (user_data, "selection-changed"); + } + else + { + update_dialog (user_data); + } + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE); +} + + +static void +button_toggled_cb (GtkToggleButton *real_button, + gpointer user_data) +{ + GtkFileChooserButtonPrivate *priv; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data); + + if (gtk_toggle_button_get_active (real_button)) + { + /* Setup the dialog parent to be chooser button's toplevel, and be modal + as needed. */ + if (!GTK_WIDGET_VISIBLE (priv->dialog)) + { + GtkWidget *toplevel; + + toplevel = gtk_widget_get_toplevel (user_data); + + if (GTK_WIDGET_TOPLEVEL (toplevel) && GTK_IS_WINDOW (toplevel)) + { + if (GTK_WINDOW (toplevel) != + gtk_window_get_transient_for (GTK_WINDOW (priv->dialog))) + { + gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), + GTK_WINDOW (toplevel)); + } + gtk_window_set_modal (GTK_WINDOW (priv->dialog), + gtk_window_get_modal (GTK_WINDOW (toplevel))); + } + } + + g_signal_handler_block (priv->dialog, + priv->dialog_folder_changed_id); + g_signal_handler_block (priv->dialog, + priv->dialog_file_activated_id); + g_signal_handler_block (priv->dialog, + priv->dialog_selection_changed_id); + gtk_widget_set_sensitive (priv->entry, FALSE); + gtk_window_present (GTK_WINDOW (priv->dialog)); + } + else + { + g_signal_handler_unblock (priv->dialog, + priv->dialog_folder_changed_id); + g_signal_handler_unblock (priv->dialog, + priv->dialog_file_activated_id); + g_signal_handler_unblock (priv->dialog, + priv->dialog_selection_changed_id); + gtk_widget_set_sensitive (priv->entry, TRUE); + gtk_widget_hide (priv->dialog); + } +} + + +static void +button_notify_active_cb (GObject *real_button, + GParamSpec *pspec, + gpointer user_data) +{ + g_object_notify (user_data, "active"); +} + + +/* Ensure the button height == entry height */ +static void +entry_size_allocate_cb (GtkWidget *entry, + GtkAllocation *allocation, + gpointer user_data) +{ + gtk_widget_set_size_request (user_data, -1, allocation->height); +} + + +static void +entry_changed_cb (GtkEditable *chooser_entry, + gpointer user_data) +{ + GtkFileChooserButtonPrivate *priv; + + priv = GTK_FILE_CHOOSER_BUTTON_GET_PRIVATE (user_data); + + /* We do this in an idle handler to avoid totally screwing up chooser_entry's + * completion */ + if (priv->update_id != 0) + priv->update_id = g_idle_add (update_dialog, user_data); +} diff --git a/gtk/gtkfilechooserbutton.h b/gtk/gtkfilechooserbutton.h new file mode 100644 index 0000000000..151bbaf5e9 --- /dev/null +++ b/gtk/gtkfilechooserbutton.h @@ -0,0 +1,89 @@ +/* GTK+: gtkfilechooserbutton.h + * + * 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. + */ + +#ifndef __GTK_FILE_CHOOSER_BUTTON_H__ +#define __GTK_FILE_CHOOSER_BUTTON_H__ 1 + +#include "gtkhbox.h" +#include "gtkfilechooser.h" + +G_BEGIN_DECLS + + +#define GTK_TYPE_FILE_CHOOSER_BUTTON \ + (gtk_file_chooser_button_get_type ()) +#define GTK_FILE_CHOOSER_BUTTON(object) \ + (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_FILE_CHOOSER_BUTTON, GtkFileChooserButton)) +#define GTK_FILE_CHOOSER_BUTTON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_CHOOSER_BUTTON, GtkFileChooserButtonClass)) +#define GTK_IS_FILE_CHOOSER_BUTTON(object) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_FILE_CHOOSER_BUTTON)) +#define GTK_IS_FILE_CHOOSER_BUTTON_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_CHOOSER_BUTTON)) +#define GTK_FILE_CHOOSER_BUTTON_GET_CLASS(object) \ + (G_TYPE_INSTANCE_GET_CLASS ((object), GTK_TYPE_FILE_CHOOSER_BUTTON, GtkFileChooserButtonClass)) + + +typedef struct _GtkFileChooserButton GtkFileChooserButton; +typedef struct _GtkFileChooserButtonPrivate GtkFileChooserButtonPrivate; +typedef struct _GtkFileChooserButtonClass GtkFileChooserButtonClass; + +struct _GtkFileChooserButton +{ + /* <private> */ + GtkHBox parent; + + GtkFileChooserButtonPrivate *priv; +}; + +struct _GtkFileChooserButtonClass +{ + /* <private> */ + GtkHBoxClass parent_class; + + void (*__gtk_reserved1); + void (*__gtk_reserved2); + void (*__gtk_reserved3); + void (*__gtk_reserved4); + void (*__gtk_reserved5); + void (*__gtk_reserved6); + void (*__gtk_reserved7); + void (*__gtk_reserved8); +}; + + +GType gtk_file_chooser_button_get_type (void) G_GNUC_CONST; + +GtkWidget *gtk_file_chooser_button_new (const gchar *title); +GtkWidget *gtk_file_chooser_button_new_with_backend (const gchar *title, + const gchar *backend); +GtkWidget *gtk_file_chooser_button_new_with_dialog (GtkWidget *dialog); + +G_CONST_RETURN gchar *gtk_file_chooser_button_get_title (GtkFileChooserButton *button); +void gtk_file_chooser_button_set_title (GtkFileChooserButton *button, + const gchar *title); +gboolean gtk_file_chooser_button_get_active (GtkFileChooserButton *button); +void gtk_file_chooser_button_set_active (GtkFileChooserButton *button, + gboolean is_active); + + +G_END_DECLS + +#endif /* !__GTK_FILE_CHOOSER_BUTTON_H__ */ diff --git a/gtk/gtkfilechooserdefault.c b/gtk/gtkfilechooserdefault.c index f0a9225410..80defef934 100644 --- a/gtk/gtkfilechooserdefault.c +++ b/gtk/gtkfilechooserdefault.c @@ -3172,7 +3172,7 @@ save_widgets_create (GtkFileChooserDefault *impl) 0, 0); gtk_widget_show (widget); - impl->save_file_name_entry = _gtk_file_chooser_entry_new (); + impl->save_file_name_entry = _gtk_file_chooser_entry_new (TRUE); _gtk_file_chooser_entry_set_file_system (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), impl->file_system); gtk_entry_set_width_chars (GTK_ENTRY (impl->save_file_name_entry), 45); @@ -5467,7 +5467,7 @@ location_entry_create (GtkFileChooserDefault *impl) { GtkWidget *entry; - entry = _gtk_file_chooser_entry_new (); + entry = _gtk_file_chooser_entry_new (TRUE); /* Pick a good width for the entry */ gtk_entry_set_width_chars (GTK_ENTRY (entry), 30); gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE); diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index 24a18c4a88..1d942a099d 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -57,6 +57,7 @@ struct _GtkFileChooserEntry guint has_completion : 1; guint in_change : 1; + guint eat_tabs : 1; }; enum @@ -633,6 +634,9 @@ gtk_file_chooser_entry_focus (GtkWidget *widget, GdkModifierType state; gboolean control_pressed = FALSE; + if (!chooser_entry->eat_tabs) + return GTK_WIDGET_CLASS (parent_class)->focus (widget, direction); + if (gtk_get_current_event_state (&state)) { if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK) @@ -789,6 +793,7 @@ clear_completion_callback (GtkFileChooserEntry *chooser_entry, /** * _gtk_file_chooser_entry_new: + * @eat_tabs: If %FALSE, allow focus navigation with the tab key. * * Creates a new #GtkFileChooserEntry object. #GtkFileChooserEntry * is an internal implementation widget for the GTK+ file chooser @@ -798,9 +803,14 @@ clear_completion_callback (GtkFileChooserEntry *chooser_entry, * Return value: the newly created #GtkFileChooserEntry **/ GtkWidget * -_gtk_file_chooser_entry_new (void) +_gtk_file_chooser_entry_new (gboolean eat_tabs) { - return g_object_new (GTK_TYPE_FILE_CHOOSER_ENTRY, NULL); + GtkFileChooserEntry *chooser_entry; + + chooser_entry = g_object_new (GTK_TYPE_FILE_CHOOSER_ENTRY, NULL); + chooser_entry->eat_tabs = (eat_tabs != FALSE); + + return GTK_WIDGET (chooser_entry); } /** diff --git a/gtk/gtkfilechooserentry.h b/gtk/gtkfilechooserentry.h index 6c960b5bff..3a47869c02 100644 --- a/gtk/gtkfilechooserentry.h +++ b/gtk/gtkfilechooserentry.h @@ -33,7 +33,7 @@ G_BEGIN_DECLS typedef struct _GtkFileChooserEntry GtkFileChooserEntry; GType _gtk_file_chooser_entry_get_type (void); -GtkWidget * _gtk_file_chooser_entry_new (void); +GtkWidget * _gtk_file_chooser_entry_new (gboolean eat_tab); void _gtk_file_chooser_entry_set_file_system (GtkFileChooserEntry *chooser_entry, GtkFileSystem *file_system); void _gtk_file_chooser_entry_set_base_folder (GtkFileChooserEntry *chooser_entry, diff --git a/gtk/gtkfilechooserutils.c b/gtk/gtkfilechooserutils.c index 917a4cd487..2ba38fbf52 100644 --- a/gtk/gtkfilechooserutils.c +++ b/gtk/gtkfilechooserutils.c @@ -175,10 +175,22 @@ _gtk_file_chooser_set_delegate (GtkFileChooser *receiver, G_CALLBACK (delegate_file_activated), receiver); } +GQuark +_gtk_file_chooser_delegate_get_quark (void) +{ + static GQuark quark = 0; + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_static_string ("gtk-file-chooser-delegate"); + + return quark; +} + static GtkFileChooser * get_delegate (GtkFileChooser *receiver) { - return g_object_get_data (G_OBJECT (receiver), "gtk-file-chooser-delegate"); + return g_object_get_qdata (G_OBJECT (receiver), + GTK_FILE_CHOOSER_DELEGATE_QUARK); } static gboolean diff --git a/gtk/gtkfilechooserutils.h b/gtk/gtkfilechooserutils.h index 604564ece5..41e3158545 100644 --- a/gtk/gtkfilechooserutils.h +++ b/gtk/gtkfilechooserutils.h @@ -26,6 +26,8 @@ G_BEGIN_DECLS +#define GTK_FILE_CHOOSER_DELEGATE_QUARK (_gtk_file_chooser_delegate_get_quark ()) + typedef enum { GTK_FILE_CHOOSER_PROP_FIRST = 0x1000, GTK_FILE_CHOOSER_PROP_ACTION = GTK_FILE_CHOOSER_PROP_FIRST, @@ -47,6 +49,8 @@ void _gtk_file_chooser_delegate_iface_init (GtkFileChooserIface *iface); void _gtk_file_chooser_set_delegate (GtkFileChooser *receiver, GtkFileChooser *delegate); +GQuark _gtk_file_chooser_delegate_get_quark (void) G_GNUC_CONST; + G_END_DECLS #endif /* __GTK_FILE_CHOOSER_UTILS_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index f03fc56f4c..41b194802f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -35,6 +35,7 @@ noinst_PROGRAMS = \ testellipsise \ testentrycompletion \ testfilechooser \ + testfilechooserbutton \ testgtk \ testiconview \ testicontheme \ @@ -76,6 +77,7 @@ testdnd_DEPENDENCIES = $(TEST_DEPS) testellipsise_DEPENDENCIES = $(TEST_DEPS) testentrycompletion_DEPENDENCIES = $(TEST_DEPS) testfilechooser_DEPENDENCIES = $(TEST_DEPS) +testfilechooserbutton_DEPENDENCIES = $(TEST_DEPS) testgtk_DEPENDENCIES = $(TEST_DEPS) testinput_DEPENDENCIES = $(TEST_DEPS) testmenus_DEPENDENCIES = $(TEST_DEPS) @@ -108,6 +110,7 @@ testdnd_LDADD = $(LDADDS) testellipsise_LDADD = $(LDADDS) testentrycompletion_LDADD = $(LDADDS) testfilechooser_LDADD = $(LDADDS) +testfilechooserbutton_LDADD = $(LDADDS) testgtk_LDADD = $(LDADDS) testicontheme_LDADD = $(LDADDS) testiconview_LDADD = $(LDADDS) @@ -144,6 +147,10 @@ testfilechooser_SOURCES = \ prop-editor.c \ testfilechooser.c +testfilechooserbutton_SOURCES = \ + prop-editor.c \ + testfilechooserbutton.c + testgtk_SOURCES = \ prop-editor.c \ testgtk.c diff --git a/tests/testfilechooserbutton.c b/tests/testfilechooserbutton.c new file mode 100644 index 0000000000..9635e04a15 --- /dev/null +++ b/tests/testfilechooserbutton.c @@ -0,0 +1,167 @@ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string.h> + +#include <gtk/gtk.h> + +#include "prop-editor.h" + + +static void +win_style_set_cb (GtkWidget *win) +{ + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (win)->vbox), 12); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (win)->vbox), 24); + gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (win)->action_area), 0); + gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (win)->action_area), 6); +} + + +static gboolean +editor_delete_event_cb (GtkWidget *editor, gint response, gpointer user_data) +{ + gtk_widget_hide (editor); + + return TRUE; +} + + +static void +properties_button_clicked_cb (GtkWidget *button, GObject *entry) +{ + GtkWidget *editor; + + editor = g_object_get_data (entry, "properties-dialog"); + + if (editor == NULL) + { + editor = create_prop_editor (G_OBJECT (entry), G_TYPE_INVALID); + gtk_window_set_transient_for (GTK_WINDOW (editor), + GTK_WINDOW (gtk_widget_get_toplevel (button))); + g_signal_connect (editor, "delete-event", G_CALLBACK (editor_delete_event_cb), NULL); + g_object_set_data (entry, "properties-dialog", editor); + } + + gtk_window_present (GTK_WINDOW (editor)); +} + + +static void +chooser_current_folder_changed_cb (GtkFileChooser *chooser, gpointer user_data) +{ + gchar *folder, *filename; + + folder = gtk_file_chooser_get_current_folder (chooser); + filename = gtk_file_chooser_get_filename (chooser); + g_message ("%s:%s:\n`%s`\n\tFolder: `%s'\n\tFilename: `%s'", G_STRFUNC, G_STRLOC, + G_OBJECT_TYPE_NAME (chooser), folder, filename); + g_free (folder); + g_free (filename); +} + + +static void +chooser_selection_changed_cb (GtkFileChooser *chooser, gpointer user_data) +{ + GSList *selection; + + g_warning ("%s:%s:\n`%s` Selection:", G_STRFUNC, G_STRLOC, G_OBJECT_TYPE_NAME (chooser)); + for (selection = gtk_file_chooser_get_filenames (chooser); selection != NULL; + selection = g_slist_remove_link (selection, selection)) + { + g_print ("`%s'\n", (const gchar *) selection->data); + g_free (selection->data); + } + g_print ("Done.\n"); +} + + +static void +chooser_file_activated_cb (GtkFileChooser *chooser, gpointer user_data) +{ + gchar *folder, *filename; + + folder = gtk_file_chooser_get_current_folder (chooser); + filename = gtk_file_chooser_get_filename (chooser); + g_warning ("%s:%s:\n`%s`\nFolder: `%s'\nFilename: `%s'", G_STRFUNC, G_STRLOC, G_OBJECT_TYPE_NAME (chooser), folder, filename); + g_free (folder); + g_free (filename); +} + + +static void +chooser_update_preview_cb (GtkFileChooser *chooser, gpointer user_data) +{ + gchar *filename; + + filename = gtk_file_chooser_get_preview_filename (chooser); + g_warning ("%s:%s:\n`%s`\nPreview Filename: `%s'", G_STRFUNC, G_STRLOC, G_OBJECT_TYPE_NAME (chooser), filename); + g_free (filename); +} + + +int +main (int argc, char *argv[]) +{ + GtkWidget *win, *vbox, *frame, *alignment, *group_box, *hbox, *label, *chooser, *button; + GtkSizeGroup *label_group; + + gtk_init (&argc, &argv); + + win = gtk_dialog_new_with_buttons ("TestFileChooserButton", NULL, GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_QUIT, GTK_RESPONSE_CLOSE, NULL); + g_signal_connect (win, "style-set", G_CALLBACK (win_style_set_cb), NULL); + g_signal_connect (win, "response", G_CALLBACK (gtk_main_quit), NULL); + + vbox = gtk_vbox_new (FALSE, 18); + gtk_container_add (GTK_CONTAINER (GTK_DIALOG (win)->vbox), vbox); + + frame = gtk_frame_new ("<b>GtkFileChooserButton</b>"); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE); + gtk_label_set_use_markup (GTK_LABEL (gtk_frame_get_label_widget (GTK_FRAME (frame))), TRUE); + gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0); + + alignment = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 12, 0); + gtk_container_add (GTK_CONTAINER (frame), alignment); + + label_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); + + group_box = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (alignment), group_box); + + hbox = gtk_hbox_new (FALSE, 12); + gtk_container_add (GTK_CONTAINER (group_box), hbox); + + label = gtk_label_new ("Open:"); + gtk_size_group_add_widget (GTK_SIZE_GROUP (label_group), label); + gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + chooser = gtk_file_chooser_button_new_with_backend ("Select A File - testfilechooserbutton", + "gtk+"); + g_signal_connect (chooser, "current-folder-changed", + G_CALLBACK (chooser_current_folder_changed_cb), NULL); + g_signal_connect (chooser, "selection-changed", G_CALLBACK (chooser_selection_changed_cb), NULL); + g_signal_connect (chooser, "file-activated", G_CALLBACK (chooser_file_activated_cb), NULL); + g_signal_connect (chooser, "update-preview", G_CALLBACK (chooser_update_preview_cb), NULL); + gtk_container_add (GTK_CONTAINER (hbox), chooser); + + button = gtk_button_new_with_label ("Properties..."); + g_signal_connect (button, "clicked", G_CALLBACK (properties_button_clicked_cb), chooser); + gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); + + g_object_unref (label_group); + + gtk_widget_show_all (win); + gtk_window_present (GTK_WINDOW (win)); + + gtk_main (); + + gtk_widget_destroy (win); + + return 0; +} |