* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
+#include <config.h>
#include "gtkbutton.h"
#include "gtkdialog.h"
#include "gtkhbbox.h"
+#include "gtklabel.h"
#include "gtkhseparator.h"
#include "gtkmarshalers.h"
#include "gtkvbox.h"
#include "gtkmain.h"
#include "gtkintl.h"
#include "gtkbindings.h"
+#include "gtkprivate.h"
+#include "gtkalias.h"
-static void gtk_dialog_class_init (GtkDialogClass *klass);
-static void gtk_dialog_init (GtkDialog *dialog);
+#define GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_DIALOG, GtkDialogPrivate))
+
+typedef struct {
+ guint ignore_separator : 1;
+} GtkDialogPrivate;
+
+typedef struct _ResponseData ResponseData;
+
+struct _ResponseData
+{
+ gint response_id;
+};
static void gtk_dialog_add_buttons_valist (GtkDialog *dialog,
const gchar *first_button_text,
static void gtk_dialog_close (GtkDialog *dialog);
+static ResponseData* get_response_data (GtkWidget *widget,
+ gboolean create);
+
enum {
PROP_0,
PROP_HAS_SEPARATOR
LAST_SIGNAL
};
-static gpointer parent_class;
static guint dialog_signals[LAST_SIGNAL];
-GType
-gtk_dialog_get_type (void)
-{
- static GType dialog_type = 0;
-
- if (!dialog_type)
- {
- static const GTypeInfo dialog_info =
- {
- sizeof (GtkDialogClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_dialog_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkDialog),
- 0, /* n_preallocs */
- (GInstanceInitFunc) gtk_dialog_init,
- };
-
- dialog_type = g_type_register_static (GTK_TYPE_WINDOW, "GtkDialog",
- &dialog_info, 0);
- }
-
- return dialog_type;
-}
+G_DEFINE_TYPE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW)
static void
gtk_dialog_class_init (GtkDialogClass *class)
gobject_class = G_OBJECT_CLASS (class);
widget_class = GTK_WIDGET_CLASS (class);
- parent_class = g_type_class_peek_parent (class);
-
gobject_class->set_property = gtk_dialog_set_property;
gobject_class->get_property = gtk_dialog_get_property;
class->close = gtk_dialog_close;
+ g_type_class_add_private (gobject_class, sizeof (GtkDialogPrivate));
+
g_object_class_install_property (gobject_class,
PROP_HAS_SEPARATOR,
- g_param_spec_boolean ("has_separator",
- _("Has separator"),
- _("The dialog has a separator bar above its buttons"),
+ g_param_spec_boolean ("has-separator",
+ P_("Has separator"),
+ P_("The dialog has a separator bar above its buttons"),
TRUE,
- G_PARAM_READWRITE));
+ GTK_PARAM_READWRITE));
dialog_signals[RESPONSE] =
- g_signal_new ("response",
+ g_signal_new (I_("response"),
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkDialogClass, response),
G_TYPE_INT);
dialog_signals[CLOSE] =
- g_signal_new ("close",
+ g_signal_new (I_("close"),
G_OBJECT_CLASS_TYPE (class),
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (GtkDialogClass, close),
G_TYPE_NONE, 0);
gtk_widget_class_install_style_property (widget_class,
- g_param_spec_int ("content_area_border",
- _("Content area border"),
- _("Width of border around the main dialog area"),
+ g_param_spec_int ("content-area-border",
+ P_("Content area border"),
+ P_("Width of border around the main dialog area"),
0,
G_MAXINT,
2,
- G_PARAM_READABLE));
+ GTK_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
- g_param_spec_int ("button_spacing",
- _("Button spacing"),
- _("Spacing between buttons"),
+ g_param_spec_int ("button-spacing",
+ P_("Button spacing"),
+ P_("Spacing between buttons"),
0,
G_MAXINT,
- 10,
- G_PARAM_READABLE));
+ 6,
+ GTK_PARAM_READABLE));
gtk_widget_class_install_style_property (widget_class,
- g_param_spec_int ("action_area_border",
- _("Action area border"),
- _("Width of border around the button area at the bottom of the dialog"),
+ g_param_spec_int ("action-area-border",
+ P_("Action area border"),
+ P_("Width of border around the button area at the bottom of the dialog"),
0,
G_MAXINT,
5,
- G_PARAM_READABLE));
+ GTK_PARAM_READABLE));
binding_set = gtk_binding_set_by_class (class);
widget = GTK_WIDGET (dialog);
gtk_widget_style_get (widget,
- "content_area_border",
- &content_area_border,
- "button_spacing",
- &button_spacing,
- "action_area_border",
- &action_area_border,
+ "content-area-border", &content_area_border,
+ "button-spacing", &button_spacing,
+ "action-area-border", &action_area_border,
NULL);
gtk_container_set_border_width (GTK_CONTAINER (dialog->vbox),
static void
gtk_dialog_init (GtkDialog *dialog)
{
+ GtkDialogPrivate *priv;
+
+ priv = GET_PRIVATE (dialog);
+ priv->ignore_separator = FALSE;
+
/* To avoid breaking old code that prevents destroy on delete event
* by connecting a handler, we have to have the FIRST signal
* connection on the dialog.
* widget in the tab chain, but if this results in the focus
* ending up on one of the response widgets _other_ than the
* default response, we focus the default response instead.
+ *
+ * Additionally, skip selectable labels when looking for the
+ * right initial focus widget.
*/
static void
gtk_dialog_map (GtkWidget *widget)
GtkWindow *window = GTK_WINDOW (widget);
GtkDialog *dialog = GTK_DIALOG (widget);
- GTK_WIDGET_CLASS (parent_class)->map (widget);
+ GTK_WIDGET_CLASS (gtk_dialog_parent_class)->map (widget);
if (!window->focus_widget)
{
GList *children, *tmp_list;
+ GtkWidget *first_focus = NULL;
- g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
+ do
+ {
+ g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
- tmp_list = children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
+ if (first_focus == NULL)
+ first_focus = window->focus_widget;
+ else if (first_focus == window->focus_widget)
+ break;
+
+ if (!GTK_IS_LABEL (window->focus_widget))
+ break;
+ else
+ gtk_label_select_region (GTK_LABEL (window->focus_widget), 0, 0);
+ }
+ while (TRUE);
+ tmp_list = children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
+
while (tmp_list)
{
GtkWidget *child = tmp_list->data;
-
- if (child == window->focus_widget && child != window->default_widget && window->default_widget)
+
+ if ((window->focus_widget == NULL ||
+ child == window->focus_widget) &&
+ child != window->default_widget &&
+ window->default_widget)
{
gtk_widget_grab_focus (window->default_widget);
break;
tmp_list = tmp_list->next;
}
-
+
g_list_free (children);
}
}
update_spacings (GTK_DIALOG (widget));
}
+static GtkWidget *
+dialog_find_button (GtkDialog *dialog,
+ gint response_id)
+{
+ GList *children, *tmp_list;
+ GtkWidget *child = NULL;
+
+ children = gtk_container_get_children (GTK_CONTAINER (dialog->action_area));
+
+ for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
+ {
+ ResponseData *rd = get_response_data (tmp_list->data, FALSE);
+
+ if (rd && rd->response_id == response_id)
+ {
+ child = tmp_list->data;
+ break;
+ }
+ }
+
+ g_list_free (children);
+
+ return child;
+}
+
static void
gtk_dialog_close (GtkDialog *dialog)
{
/* Synthesize delete_event to close dialog. */
- GdkEvent *event = gdk_event_new (GDK_DELETE);
- GtkWidget *widget;
+ GtkWidget *widget = GTK_WIDGET (dialog);
+ GdkEvent *event;
- widget = GTK_WIDGET (dialog);
+ event = gdk_event_new (GDK_DELETE);
event->any.window = g_object_ref (widget->window);
event->any.send_event = TRUE;
return GTK_WIDGET (dialog);
}
-typedef struct _ResponseData ResponseData;
-
-struct _ResponseData
+static void
+response_data_free (gpointer data)
{
- gint response_id;
-};
+ g_slice_free (ResponseData, data);
+}
static ResponseData*
-get_response_data (GtkWidget *widget)
+get_response_data (GtkWidget *widget,
+ gboolean create)
{
ResponseData *ad = g_object_get_data (G_OBJECT (widget),
"gtk-dialog-response-data");
- if (ad == NULL)
+ if (ad == NULL && create)
{
- ad = g_new (ResponseData, 1);
+ ad = g_slice_new (ResponseData);
g_object_set_data_full (G_OBJECT (widget),
- "gtk-dialog-response-data",
+ I_("gtk-dialog-response-data"),
ad,
- g_free);
+ response_data_free);
}
return ad;
static void
action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
{
- ResponseData *ad;
gint response_id;
- g_return_if_fail (GTK_IS_DIALOG (dialog));
-
- response_id = GTK_RESPONSE_NONE;
-
- ad = get_response_data (widget);
-
- g_assert (ad != NULL);
-
- response_id = ad->response_id;
+ response_id = gtk_dialog_get_response_for_widget (dialog, widget);
gtk_dialog_response (dialog, response_id);
}
gint response_id)
{
ResponseData *ad;
- gint signal_id = 0;
+ guint signal_id;
g_return_if_fail (GTK_IS_DIALOG (dialog));
g_return_if_fail (GTK_IS_WIDGET (child));
- ad = get_response_data (child);
+ ad = get_response_data (child, TRUE);
ad->response_id = response_id;
if (GTK_IS_BUTTON (child))
signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
else
- signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal != 0;
+ signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
if (signal_id)
{
}
static void
-gtk_dialog_add_buttons_valist(GtkDialog *dialog,
- const gchar *first_button_text,
- va_list args)
+gtk_dialog_add_buttons_valist (GtkDialog *dialog,
+ const gchar *first_button_text,
+ va_list args)
{
const gchar* text;
gint response_id;
while (tmp_list != NULL)
{
GtkWidget *widget = tmp_list->data;
- ResponseData *rd = g_object_get_data (G_OBJECT (widget),
- "gtk-dialog-response-data");
+ ResponseData *rd = get_response_data (widget, FALSE);
if (rd && rd->response_id == response_id)
gtk_widget_set_sensitive (widget, setting);
while (tmp_list != NULL)
{
GtkWidget *widget = tmp_list->data;
- ResponseData *rd = g_object_get_data (G_OBJECT (widget),
- "gtk-dialog-response-data");
+ ResponseData *rd = get_response_data (widget, FALSE);
if (rd && rd->response_id == response_id)
gtk_widget_grab_default (widget);
gtk_dialog_set_has_separator (GtkDialog *dialog,
gboolean setting)
{
+ GtkDialogPrivate *priv;
+
g_return_if_fail (GTK_IS_DIALOG (dialog));
+ priv = GET_PRIVATE (dialog);
+
/* this might fail if we get called before _init() somehow */
g_assert (dialog->vbox != NULL);
+
+ if (priv->ignore_separator)
+ {
+ g_warning ("Ignoring the separator setting");
+ return;
+ }
if (setting && dialog->separator == NULL)
{
dialog->separator = NULL;
}
- g_object_notify (G_OBJECT (dialog), "has_separator");
+ g_object_notify (G_OBJECT (dialog), "has-separator");
}
/**
* gtk_widget_destroy (dialog);
* </programlisting></informalexample>
*
+ * Note that even though the recursive main loop gives the effect of a
+ * modal dialog (it prevents the user from interacting with other
+ * windows in the same window group while the dialog is run), callbacks
+ * such as timeouts, IO channel watches, DND drops, etc, <emphasis>will</emphasis>
+ * be triggered during a gtk_dialog_run() call.
+ *
* Return value: response ID
**/
gint
gtk_dialog_run (GtkDialog *dialog)
{
- RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL };
+ RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
gboolean was_modal;
gulong response_handler;
gulong unmap_handler;
g_object_ref (dialog);
- if (!GTK_WIDGET_VISIBLE (dialog))
- gtk_widget_show (GTK_WIDGET (dialog));
-
was_modal = GTK_WINDOW (dialog)->modal;
if (!was_modal)
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ if (!GTK_WIDGET_VISIBLE (dialog))
+ gtk_widget_show (GTK_WIDGET (dialog));
+
response_handler =
g_signal_connect (dialog,
"response",
g_main_loop_unref (ri.loop);
ri.loop = NULL;
- ri.destroyed = FALSE;
if (!ri.destroyed)
{
return ri.response_id;
}
+
+void
+_gtk_dialog_set_ignore_separator (GtkDialog *dialog,
+ gboolean ignore_separator)
+{
+ GtkDialogPrivate *priv;
+
+ priv = GET_PRIVATE (dialog);
+ priv->ignore_separator = ignore_separator;
+}
+
+/**
+ * gtk_dialog_get_response_for_widget:
+ * @dialog: a #GtkDialog
+ * @widget: a widget in the action area of @dialog
+ *
+ * Gets the response id of a widget in the action area
+ * of a dialog.
+ *
+ * Returns: the response id of @widget, or %GTK_RESPONSE_NONE
+ * if @widget doesn't have a response id set.
+ *
+ * Since: 2.8
+ */
+gint
+gtk_dialog_get_response_for_widget (GtkDialog *dialog,
+ GtkWidget *widget)
+{
+ ResponseData *rd;
+
+ rd = get_response_data (widget, FALSE);
+ if (!rd)
+ return GTK_RESPONSE_NONE;
+ else
+ return rd->response_id;
+}
+
+/**
+ * gtk_alternative_dialog_button_order:
+ * @screen: a #GdkScreen, or %NULL to use the default screen
+ *
+ * Returns %TRUE if dialogs are expected to use an alternative
+ * button order on the screen @screen. See
+ * gtk_dialog_set_alternative_button_order() for more details
+ * about alternative button order.
+ *
+ * If you need to use this function, you should probably connect
+ * to the ::notify:gtk-alternative-button-order signal on the
+ * #GtkSettings object associated to @screen, in order to be
+ * notified if the button order setting changes.
+ *
+ * Returns: Whether the alternative button order should be used
+ *
+ * Since: 2.6
+ */
+gboolean
+gtk_alternative_dialog_button_order (GdkScreen *screen)
+{
+ GtkSettings *settings;
+ gboolean result;
+
+ if (screen)
+ settings = gtk_settings_get_for_screen (screen);
+ else
+ settings = gtk_settings_get_default ();
+
+ g_object_get (settings,
+ "gtk-alternative-button-order", &result, NULL);
+
+ return result;
+}
+
+static void
+gtk_dialog_set_alternative_button_order_valist (GtkDialog *dialog,
+ gint first_response_id,
+ va_list args)
+{
+ GtkWidget *child;
+ gint response_id;
+ gint position;
+
+ response_id = first_response_id;
+ position = 0;
+ while (response_id != -1)
+ {
+ /* reorder child with response_id to position */
+ child = dialog_find_button (dialog, response_id);
+ gtk_box_reorder_child (GTK_BOX (dialog->action_area), child, position);
+
+ response_id = va_arg (args, gint);
+ position++;
+ }
+}
+
+/**
+ * gtk_dialog_set_alternative_button_order:
+ * @dialog: a #GtkDialog
+ * @first_response_id: a response id used by one @dialog's buttons
+ * @Varargs: a list of more response ids of @dialog's buttons, terminated by -1
+ *
+ * Sets an alternative button order. If the gtk-alternative-button-order
+ * setting is set to %TRUE, the dialog buttons are reordered according to
+ * the order of the response ids passed to this function.
+ *
+ * By default, GTK+ dialogs use the button order advocated by the Gnome
+ * <ulink url="http://developer.gnome.org/projects/gup/hig/2.0/">Human
+ * Interface Guidelines</ulink> with the affirmative button at the far
+ * right, and the cancel button left of it. But the builtin GTK+ dialogs
+ * and #GtkMessageDialog<!-- -->s do provide an alternative button order,
+ * which is more suitable on some platforms, e.g. Windows.
+ *
+ * Use this function after adding all the buttons to your dialog, as the
+ * following example shows:
+ * <informalexample><programlisting>
+ * cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+ * GTK_STOCK_CANCEL,
+ * GTK_RESPONSE_CANCEL);
+ *
+ * ok_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+ * GTK_STOCK_OK,
+ * GTK_RESPONSE_OK);
+ *
+ * gtk_widget_grab_default (ok_button);
+ *
+ * help_button = gtk_dialog_add_button (GTK_DIALOG (dialog),
+ * GTK_STOCK_HELP,
+ * GTK_RESPONSE_HELP);
+ *
+ * gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
+ * GTK_RESPONSE_OK,
+ * GTK_RESPONSE_CANCEL,
+ * GTK_RESPONSE_HELP,
+ * -1);
+ * </programlisting></informalexample>
+ *
+ * Since: 2.6
+ */
+void
+gtk_dialog_set_alternative_button_order (GtkDialog *dialog,
+ gint first_response_id,
+ ...)
+{
+ GdkScreen *screen;
+ va_list args;
+
+ g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
+ if (!gtk_alternative_dialog_button_order (screen))
+ return;
+
+ va_start (args, first_response_id);
+
+ gtk_dialog_set_alternative_button_order_valist (dialog,
+ first_response_id,
+ args);
+ va_end (args);
+}
+/**
+ * gtk_dialog_set_alternative_button_order_from_array:
+ * @dialog: a #GtkDialog
+ * @n_params: the number of response ids in @new_order
+ * @new_order: an array of response ids of @dialog's buttons
+ *
+ * Sets an alternative button order. If the gtk-alternative-button-order
+ * setting is set to %TRUE, the dialog buttons are reordered according to
+ * the order of the response ids in @new_order.
+ *
+ * See gtk_dialog_set_alternative_button_order() for more information.
+ *
+ * This function is for use by language bindings.
+ *
+ * Since: 2.6
+ */
+void
+gtk_dialog_set_alternative_button_order_from_array (GtkDialog *dialog,
+ gint n_params,
+ gint *new_order)
+{
+ GdkScreen *screen;
+ GtkWidget *child;
+ gint position;
+
+ g_return_if_fail (GTK_IS_DIALOG (dialog));
+ g_return_if_fail (new_order != NULL);
+
+ screen = gtk_widget_get_screen (GTK_WIDGET (dialog));
+ if (!gtk_alternative_dialog_button_order (screen))
+ return;
+
+ for (position = 0; position < n_params; position++)
+ {
+ /* reorder child with response_id to position */
+ child = dialog_find_button (dialog, new_order[position]);
+ gtk_box_reorder_child (GTK_BOX (dialog->action_area), child, position);
+ }
+}
+
+#define __GTK_DIALOG_C__
+#include "gtkaliasdef.c"