]> Pileus Git - ~andy/gtk/commitdiff
New widget to go along with GtkFontButton and GtkColorButton for use in
authorMatthias Clasen <mclasen@redhat.com>
Tue, 17 Aug 2004 16:06:39 +0000 (16:06 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Tue, 17 Aug 2004 16:06:39 +0000 (16:06 +0000)
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

16 files changed:
ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/Makefile.am
gtk/gtk.h
gtk/gtk.symbols
gtk/gtkfilechooserbutton.c [new file with mode: 0644]
gtk/gtkfilechooserbutton.h [new file with mode: 0644]
gtk/gtkfilechooserdefault.c
gtk/gtkfilechooserentry.c
gtk/gtkfilechooserentry.h
gtk/gtkfilechooserutils.c
gtk/gtkfilechooserutils.h
tests/Makefile.am
tests/testfilechooserbutton.c [new file with mode: 0644]

index ec2d48de32c993812a846e914b3eeb0f88547b55..ef058165c6ad857800bae7e24c316f27425b8865 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
index ec2d48de32c993812a846e914b3eeb0f88547b55..ef058165c6ad857800bae7e24c316f27425b8865 100644 (file)
@@ -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.
index ec2d48de32c993812a846e914b3eeb0f88547b55..ef058165c6ad857800bae7e24c316f27425b8865 100644 (file)
@@ -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.
index ec2d48de32c993812a846e914b3eeb0f88547b55..ef058165c6ad857800bae7e24c316f27425b8865 100644 (file)
@@ -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.
index 26e223ac9a4437b28a32fa1a3d45a63f98832445..2feac995f117fac8a8d1a33c26a041232942cb88 100644 (file)
@@ -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:
-
index dd5a3d902a08e79c69d76b1d051090730f082e37..cce4ef82b662ed01298a448ce42cd810e0ddbf4f 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -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>
index 026ad7cd8c7f72df301d586532d7537d4085933a..78e906706ab8ac0f2517c6e4e2b0c43040efb4aa 100644 (file)
@@ -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 (file)
index 0000000..dfc1b68
--- /dev/null
@@ -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 (file)
index 0000000..151bbaf
--- /dev/null
@@ -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__ */
index f0a92254105ebbb09d55b58eb7780921d1808e52..80defef934459a01d269054db16e15dd7d8195be 100644 (file)
@@ -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);
index 24a18c4a88fa8313e10cb010d5648b2966b9d116..1d942a099d3d967ba1a71194f643b0e10f0580e6 100644 (file)
@@ -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);
 }
 
 /**
index 6c960b5bff81d3b3c96a1ef4f979005fb112a793..3a47869c024bb6ccdd02ef71f8c206af7b65d38e 100644 (file)
@@ -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,
index 917a4cd487502cb2ac7ed4633086806d1a2ed05a..2ba38fbf521ccdb3766418ef373a69253a8dbaa8 100644 (file)
@@ -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
index 604564ece56dc0c2647d834fa31fc5f3527a482a..41e3158545f131146230cdeefacdcfbc5280c119 100644 (file)
@@ -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__ */
index f03fc56f4ce116ac317fa6129ef1fd4b0a8efffb..41b194802f0558ecf58d99c4de709b61269518a7 100644 (file)
@@ -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 (file)
index 0000000..9635e04
--- /dev/null
@@ -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;
+}