diff options
author | Matthias Clasen <mclasen@redhat.com> | 2009-06-05 14:56:08 -0400 |
---|---|---|
committer | Matthias Clasen <mclasen@redhat.com> | 2009-06-05 18:00:26 -0400 |
commit | a85fac714563f632a097d1c30332d10f6de50f63 (patch) | |
tree | 5c7cba14c3c71bc9867316525c1db02c5afe16ca | |
parent | 4d7bbd058ebf4451d8f5856f2193bf176df87644 (diff) | |
download | gtk+-a85fac714563f632a097d1c30332d10f6de50f63.tar.gz |
Add a message area widget
It is called GtkInfoBar. See bug 555344.
-rw-r--r-- | demos/gtk-demo/appwindow.c | 196 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-docs.sgml | 1 | ||||
-rw-r--r-- | docs/reference/gtk/gtk-sections.txt | 29 | ||||
-rw-r--r-- | docs/reference/gtk/gtk.types | 1 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtk.symbols | 17 | ||||
-rw-r--r-- | gtk/gtkenums.h | 9 | ||||
-rw-r--r-- | gtk/gtkinfobar.c | 1262 | ||||
-rw-r--r-- | gtk/gtkinfobar.h | 116 | ||||
-rw-r--r-- | gtk/gtkmessagedialog.h | 10 | ||||
-rw-r--r-- | gtk/tests/builder.c | 51 |
12 files changed, 1597 insertions, 98 deletions
diff --git a/demos/gtk-demo/appwindow.c b/demos/gtk-demo/appwindow.c index 43870d43e6..8ff90651cb 100644 --- a/demos/gtk-demo/appwindow.c +++ b/demos/gtk-demo/appwindow.c @@ -8,6 +8,8 @@ #include "demo-common.h" static GtkWidget *window = NULL; +static GtkWidget *infobar = NULL; +static GtkWidget *messagelabel = NULL; static void activate_action (GtkAction *action) @@ -16,7 +18,7 @@ activate_action (GtkAction *action) const gchar *typename = G_OBJECT_TYPE_NAME (action); GtkWidget *dialog; - + dialog = gtk_message_dialog_new (GTK_WINDOW (window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, @@ -29,7 +31,7 @@ activate_action (GtkAction *action) "response", G_CALLBACK (gtk_widget_destroy), NULL); - + gtk_widget_show (dialog); } @@ -41,37 +43,32 @@ activate_radio_action (GtkAction *action, GtkRadioAction *current) gboolean active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (current)); gint value = gtk_radio_action_get_current_value (GTK_RADIO_ACTION (current)); - if (active) + if (active) { - GtkWidget *dialog; - - dialog = gtk_message_dialog_new (GTK_WINDOW (window), - GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_INFO, - GTK_BUTTONS_CLOSE, - "You activated radio action: \"%s\" of type \"%s\".\n" - "Current value: %d", - name, typename, value); - - /* Close dialog on user response */ - g_signal_connect (dialog, - "response", - G_CALLBACK (gtk_widget_destroy), - NULL); - - gtk_widget_show (dialog); + gchar *text; + + text = g_strdup_printf ("You activated radio action: \"%s\" of type \"%s\".\n" + "Current value: %d", + name, typename, value); + gtk_label_set_text (GTK_LABEL (messagelabel), text); + gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), (GtkMessageType)value); + gtk_widget_show (infobar); + g_free (text); } } -static void +static void activate_email (GtkAboutDialog *about, const gchar *link, gpointer data) { - g_print ("send mail to %s\n", link); + gchar *text; + text = g_strdup_printf ("send mail to %s", link); + g_print ("%s\n", text); + g_free (text); } -static void +static void activate_url (GtkAboutDialog *about, const gchar *link, gpointer data) @@ -147,12 +144,12 @@ about_cb (GtkAction *action, g_object_unref (transparent); } -typedef struct +typedef struct { GtkAction action; } ToolMenuAction; -typedef struct +typedef struct { GtkActionClass parent_class; } ToolMenuActionClass; @@ -179,30 +176,30 @@ static GtkActionEntry entries[] = { { "HelpMenu", NULL, "_Help" }, /* name, stock id, label */ { "New", GTK_STOCK_NEW, /* name, stock id */ "_New", "<control>N", /* label, accelerator */ - "Create a new file", /* tooltip */ - G_CALLBACK (activate_action) }, + "Create a new file", /* tooltip */ + G_CALLBACK (activate_action) }, { "File1", NULL, /* name, stock id */ - "File1", NULL, /* label, accelerator */ + "File1", NULL, /* label, accelerator */ "Open first file", /* tooltip */ - G_CALLBACK (activate_action) }, + G_CALLBACK (activate_action) }, { "Save", GTK_STOCK_SAVE, /* name, stock id */ - "_Save","<control>S", /* label, accelerator */ + "_Save","<control>S", /* label, accelerator */ "Save current file", /* tooltip */ G_CALLBACK (activate_action) }, { "SaveAs", GTK_STOCK_SAVE, /* name, stock id */ - "Save _As...", NULL, /* label, accelerator */ + "Save _As...", NULL, /* label, accelerator */ "Save to a file", /* tooltip */ G_CALLBACK (activate_action) }, { "Quit", GTK_STOCK_QUIT, /* name, stock id */ - "_Quit", "<control>Q", /* label, accelerator */ + "_Quit", "<control>Q", /* label, accelerator */ "Quit", /* tooltip */ G_CALLBACK (activate_action) }, { "About", NULL, /* name, stock id */ - "_About", "<control>A", /* label, accelerator */ - "About", /* tooltip */ + "_About", "<control>A", /* label, accelerator */ + "About", /* tooltip */ G_CALLBACK (about_cb) }, { "Logo", "demo-gtk-logo", /* name, stock id */ - NULL, NULL, /* label, accelerator */ + NULL, NULL, /* label, accelerator */ "GTK+", /* tooltip */ G_CALLBACK (activate_action) }, }; @@ -211,9 +208,9 @@ static guint n_entries = G_N_ELEMENTS (entries); static GtkToggleActionEntry toggle_entries[] = { { "Bold", GTK_STOCK_BOLD, /* name, stock id */ - "_Bold", "<control>B", /* label, accelerator */ + "_Bold", "<control>B", /* label, accelerator */ "Bold", /* tooltip */ - G_CALLBACK (activate_action), + G_CALLBACK (activate_action), TRUE }, /* is_active */ }; static guint n_toggle_entries = G_N_ELEMENTS (toggle_entries); @@ -226,13 +223,13 @@ enum { static GtkRadioActionEntry color_entries[] = { { "Red", NULL, /* name, stock id */ - "_Red", "<control>R", /* label, accelerator */ + "_Red", "<control>R", /* label, accelerator */ "Blood", COLOR_RED }, /* tooltip, value */ { "Green", NULL, /* name, stock id */ - "_Green", "<control>G", /* label, accelerator */ + "_Green", "<control>G", /* label, accelerator */ "Grass", COLOR_GREEN }, /* tooltip, value */ { "Blue", NULL, /* name, stock id */ - "_Blue", "<control>B", /* label, accelerator */ + "_Blue", "<control>B", /* label, accelerator */ "Sky", COLOR_BLUE }, /* tooltip, value */ }; static guint n_color_entries = G_N_ELEMENTS (color_entries); @@ -245,18 +242,18 @@ enum { static GtkRadioActionEntry shape_entries[] = { { "Square", NULL, /* name, stock id */ - "_Square", "<control>S", /* label, accelerator */ + "_Square", "<control>S", /* label, accelerator */ "Square", SHAPE_SQUARE }, /* tooltip, value */ { "Rectangle", NULL, /* name, stock id */ - "_Rectangle", "<control>R", /* label, accelerator */ + "_Rectangle", "<control>R", /* label, accelerator */ "Rectangle", SHAPE_RECTANGLE }, /* tooltip, value */ { "Oval", NULL, /* name, stock id */ - "_Oval", "<control>O", /* label, accelerator */ - "Egg", SHAPE_OVAL }, /* tooltip, value */ + "_Oval", "<control>O", /* label, accelerator */ + "Egg", SHAPE_OVAL }, /* tooltip, value */ }; static guint n_shape_entries = G_N_ELEMENTS (shape_entries); -static const gchar *ui_info = +static const gchar *ui_info = "<ui>" " <menubar name='MenuBar'>" " <menu action='FileMenu'>" @@ -288,7 +285,7 @@ static const gchar *ui_info = " <toolitem action='Open'>" " <menu action='OpenMenu'>" " <menuitem action='File1'/>" -" </menu>" +" </menu>" " </toolitem>" " <toolitem action='Quit'/>" " <separator action='Sep1'/>" @@ -308,7 +305,7 @@ static void register_stock_icons (void) { static gboolean registered = FALSE; - + if (!registered) { GdkPixbuf *pixbuf; @@ -320,12 +317,12 @@ register_stock_icons (void) "_GTK!", 0, 0, NULL } }; - + registered = TRUE; /* Register our stock items */ gtk_stock_add (items, G_N_ELEMENTS (items)); - + /* Add our custom icon factory to the list of defaults */ factory = gtk_icon_factory_new (); gtk_icon_factory_add_default (factory); @@ -350,7 +347,7 @@ register_stock_icons (void) /* The gtk-logo-rgb icon has a white background, make it transparent */ transparent = gdk_pixbuf_add_alpha (pixbuf, TRUE, 0xff, 0xff, 0xff); - + icon_set = gtk_icon_set_new_from_pixbuf (transparent); gtk_icon_factory_add (factory, "demo-gtk-logo", icon_set); gtk_icon_set_unref (icon_set); @@ -359,7 +356,7 @@ register_stock_icons (void) } else g_warning ("failed to load GTK logo for toolbar"); - + /* Drop our reference to the factory, GTK will hold a reference. */ g_object_unref (factory); } @@ -373,9 +370,9 @@ update_statusbar (GtkTextBuffer *buffer, gint row, col; gint count; GtkTextIter iter; - - gtk_statusbar_pop (statusbar, 0); /* clear any previous message, - * underflow is allowed + + gtk_statusbar_pop (statusbar, 0); /* clear any previous message, + * underflow is allowed */ count = gtk_text_buffer_get_char_count (buffer); @@ -409,21 +406,21 @@ update_resize_grip (GtkWidget *widget, GdkEventWindowState *event, GtkStatusbar *statusbar) { - if (event->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED | + if (event->changed_mask & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) { gboolean maximized; - maximized = event->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED | + maximized = event->new_window_state & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN); gtk_statusbar_set_has_resize_grip (statusbar, !maximized); } } - + GtkWidget * do_appwindow (GtkWidget *do_widget) -{ +{ if (!window) { GtkWidget *table; @@ -438,10 +435,10 @@ do_appwindow (GtkWidget *do_widget) GError *error = NULL; register_stock_icons (); - + /* Create the toplevel window */ - + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_screen (GTK_WINDOW (window), gtk_widget_get_screen (do_widget)); @@ -453,15 +450,15 @@ do_appwindow (GtkWidget *do_widget) G_CALLBACK (gtk_widget_destroyed), &window); - table = gtk_table_new (1, 4, FALSE); - + table = gtk_table_new (1, 5, FALSE); + gtk_container_add (GTK_CONTAINER (window), table); - + /* Create the menubar and toolbar */ - + action_group = gtk_action_group_new ("AppWindowActions"); - open_action = g_object_new (tool_menu_action_get_type (), + open_action = g_object_new (tool_menu_action_get_type (), "name", "Open", "label", "_Open", "tooltip", "Open a file", @@ -469,30 +466,30 @@ do_appwindow (GtkWidget *do_widget) NULL); gtk_action_group_add_action (action_group, open_action); g_object_unref (open_action); - gtk_action_group_add_actions (action_group, - entries, n_entries, + gtk_action_group_add_actions (action_group, + entries, n_entries, window); - gtk_action_group_add_toggle_actions (action_group, - toggle_entries, n_toggle_entries, + gtk_action_group_add_toggle_actions (action_group, + toggle_entries, n_toggle_entries, NULL); - gtk_action_group_add_radio_actions (action_group, - color_entries, n_color_entries, + gtk_action_group_add_radio_actions (action_group, + color_entries, n_color_entries, COLOR_RED, - G_CALLBACK (activate_radio_action), + G_CALLBACK (activate_radio_action), NULL); - gtk_action_group_add_radio_actions (action_group, - shape_entries, n_shape_entries, + gtk_action_group_add_radio_actions (action_group, + shape_entries, n_shape_entries, SHAPE_SQUARE, - G_CALLBACK (activate_radio_action), + G_CALLBACK (activate_radio_action), NULL); merge = gtk_ui_manager_new (); - g_object_set_data_full (G_OBJECT (window), "ui-manager", merge, + g_object_set_data_full (G_OBJECT (window), "ui-manager", merge, g_object_unref); gtk_ui_manager_insert_action_group (merge, action_group, 0); - gtk_window_add_accel_group (GTK_WINDOW (window), + gtk_window_add_accel_group (GTK_WINDOW (window), gtk_ui_manager_get_accel_group (merge)); - + if (!gtk_ui_manager_add_ui_from_string (merge, ui_info, -1, &error)) { g_message ("building menus failed: %s", error->message); @@ -502,7 +499,7 @@ do_appwindow (GtkWidget *do_widget) bar = gtk_ui_manager_get_widget (merge, "/MenuBar"); gtk_widget_show (bar); gtk_table_attach (GTK_TABLE (table), - bar, + bar, /* X direction */ /* Y direction */ 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, 0, @@ -511,7 +508,7 @@ do_appwindow (GtkWidget *do_widget) bar = gtk_ui_manager_get_widget (merge, "/ToolBar"); gtk_widget_show (bar); gtk_table_attach (GTK_TABLE (table), - bar, + bar, /* X direction */ /* Y direction */ 0, 1, 1, 2, GTK_EXPAND | GTK_FILL, 0, @@ -520,6 +517,25 @@ do_appwindow (GtkWidget *do_widget) /* Create document */ + infobar = gtk_info_bar_new (); + gtk_widget_set_no_show_all (infobar, TRUE); + messagelabel = gtk_label_new (""); + gtk_widget_show (messagelabel); + gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar))), + messagelabel, + TRUE, TRUE, 0); + gtk_info_bar_add_button (GTK_INFO_BAR (infobar), + GTK_STOCK_OK, GTK_RESPONSE_OK); + g_signal_connect (infobar, "response", + G_CALLBACK (gtk_widget_hide), NULL); + + gtk_table_attach (GTK_TABLE (table), + infobar, + /* X direction */ /* Y direction */ + 0, 1, 2, 3, + GTK_EXPAND | GTK_FILL, 0, + 0, 0); + sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), @@ -528,20 +544,20 @@ do_appwindow (GtkWidget *do_widget) gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN); - + gtk_table_attach (GTK_TABLE (table), sw, /* X direction */ /* Y direction */ - 0, 1, 2, 3, + 0, 1, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); - + contents = gtk_text_view_new (); gtk_widget_grab_focus (contents); - + gtk_container_add (GTK_CONTAINER (sw), contents); @@ -551,7 +567,7 @@ do_appwindow (GtkWidget *do_widget) gtk_table_attach (GTK_TABLE (table), statusbar, /* X direction */ /* Y direction */ - 0, 1, 3, 4, + 0, 1, 4, 5, GTK_EXPAND | GTK_FILL, 0, 0, 0); @@ -570,12 +586,12 @@ do_appwindow (GtkWidget *do_widget) statusbar, 0); - g_signal_connect_object (window, - "window_state_event", + g_signal_connect_object (window, + "window_state_event", G_CALLBACK (update_resize_grip), statusbar, 0); - + update_statusbar (buffer, GTK_STATUSBAR (statusbar)); } @@ -584,9 +600,11 @@ do_appwindow (GtkWidget *do_widget) gtk_widget_show_all (window); } else - { + { gtk_widget_destroy (window); window = NULL; + infobar = NULL; + messagelabel = NULL; } return window; diff --git a/docs/reference/gtk/gtk-docs.sgml b/docs/reference/gtk/gtk-docs.sgml index 0d27e5ff45..16a59b0f1e 100644 --- a/docs/reference/gtk/gtk-docs.sgml +++ b/docs/reference/gtk/gtk-docs.sgml @@ -159,6 +159,7 @@ that is, GUI components such as #GtkButton or #GtkTextView. <xi:include href="xml/gtklabel.xml" /> <xi:include href="xml/gtkprogressbar.xml" /> <xi:include href="xml/gtkstatusbar.xml" /> + <xi:include href="xml/gtkinfobar.xml" /> <xi:include href="xml/gtkstatusicon.xml" /> </chapter> diff --git a/docs/reference/gtk/gtk-sections.txt b/docs/reference/gtk/gtk-sections.txt index d300ea6322..83e998c63f 100644 --- a/docs/reference/gtk/gtk-sections.txt +++ b/docs/reference/gtk/gtk-sections.txt @@ -2589,6 +2589,35 @@ gtk_message_dialog_get_type </SECTION> <SECTION> +<FILE>gtkinfobar</FILE> +<TITLE>GtkInfoBar</TITLE> +GtkInfoBar +gtk_info_bar_new +gtk_info_bar_new_with_buttons +gtk_info_bar_set_contents +gtk_info_bar_add_action_widget +gtk_info_bar_add_button +gtk_info_bar_add_buttons +gtk_info_bar_set_response_sensitive +gtk_info_bar_set_default_response +gtk_info_bar_response +gtk_info_bar_set_message_type +gtk_info_bar_get_message_type + +<SUBSECTION Standard> +GTK_TYPE_INFO_BAR +GTK_INFO_BAR +GTK_INFO_BAR_CLASS +GTK_IS_INFO_BAR +GTK_IS_INFO_BAR_CLASS +GTK_INFO_BAR_GET_CLASS + +<SUBSECTION Private> +gtk_info_bar_get_type +GtkMessageAreaPrivate +</SECTION> + +<SECTION> <FILE>gtkmisc</FILE> <TITLE>GtkMisc</TITLE> GtkMisc diff --git a/docs/reference/gtk/gtk.types b/docs/reference/gtk/gtk.types index 24dd79b285..126f673bb1 100644 --- a/docs/reference/gtk/gtk.types +++ b/docs/reference/gtk/gtk.types @@ -81,6 +81,7 @@ gtk_image_menu_item_get_type gtk_im_context_get_type gtk_im_context_simple_get_type gtk_im_multicontext_get_type +gtk_info_bar_get_type gtk_input_dialog_get_type gtk_invisible_get_type gtk_item_factory_get_type diff --git a/gtk/Makefile.am b/gtk/Makefile.am index d80dc7f56a..6d7a03865d 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -230,6 +230,7 @@ gtk_public_h_sources = \ gtkimcontextsimple.h \ gtkimmodule.h \ gtkimmulticontext.h \ + gtkinfobar.h \ gtkinputdialog.h \ gtkinvisible.h \ gtkitem.h \ @@ -486,6 +487,7 @@ gtk_base_c_sources = \ gtkimcontextsimple.c \ gtkimmodule.c \ gtkimmulticontext.c \ + gtkinfobar.c \ gtkinputdialog.c \ gtkinvisible.c \ gtkitem.c \ @@ -110,6 +110,7 @@ #include <gtk/gtkimcontext.h> #include <gtk/gtkimcontextsimple.h> #include <gtk/gtkimmulticontext.h> +#include <gtk/gtkinfobar.h> #include <gtk/gtkinputdialog.h> #include <gtk/gtkinvisible.h> #include <gtk/gtkitem.h> diff --git a/gtk/gtk.symbols b/gtk/gtk.symbols index d9ddbf8691..404c1162e9 100644 --- a/gtk/gtk.symbols +++ b/gtk/gtk.symbols @@ -5155,6 +5155,23 @@ gtk_win32_embed_widget_get_type G_GNUC_CONST #endif #endif +#if IN_HEADER(__GTK_INFO_BAR_H__) +#if IN_FILE(__GTK_INFO_BAR_C__) +gtk_info_bar_get_type G_GNUC_CONST +gtk_info_bar_new +gtk_info_bar_new_with_buttons +gtk_info_bar_get_action_area +gtk_info_bar_get_content_area +gtk_info_bar_add_action_widget +gtk_info_bar_add_button +gtk_info_bar_add_buttons +gtk_info_bar_set_response_sensitive +gtk_info_bar_set_default_response +gtk_info_bar_set_message_type +gtk_info_bar_get_message_type +#endif +#endif + #ifdef INCLUDE_VARIABLES gtk_binary_age gtk_interface_age diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h index 9262aeaf78..1d9467b651 100644 --- a/gtk/gtkenums.h +++ b/gtk/gtkenums.h @@ -208,6 +208,15 @@ typedef enum typedef enum { + GTK_MESSAGE_INFO, + GTK_MESSAGE_WARNING, + GTK_MESSAGE_QUESTION, + GTK_MESSAGE_ERROR, + GTK_MESSAGE_OTHER +} GtkMessageType; + +typedef enum +{ GTK_PIXELS, GTK_INCHES, GTK_CENTIMETERS diff --git a/gtk/gtkinfobar.c b/gtk/gtkinfobar.c new file mode 100644 index 0000000000..cf1cb42ff4 --- /dev/null +++ b/gtk/gtkinfobar.c @@ -0,0 +1,1262 @@ +/* + * gtkinfobar.c + * This file is part of GTK+ + * + * Copyright (C) 2005 - Paolo Maggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the AUTHORS file for a + * list of people on the gtk Team. + * See the gedit ChangeLog files for a list of changes. + * + * Modified by the GTK+ team, 2008-2009. + */ + + +#include "config.h" + +#include <stdlib.h> + +#include "gtkinfobar.h" +#include "gtkaccessible.h" +#include "gtkbuildable.h" +#include "gtkbox.h" +#include "gtkvbbox.h" +#include "gtklabel.h" +#include "gtkbutton.h" +#include "gtkenums.h" +#include "gtkbindings.h" +#include "gtkdialog.h" +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtkstock.h" +#include "gdkkeysyms.h" +#include "gtkalias.h" + + +/** + * SECTION:gtkinfobar + * @short_description: Report important messages to the user + * @include: gtk/gtk.h + * @see_also: #GtkStatusbar, #GtkMessageDialog + * + * #GtkInfoBar is a widget that can be used to show messages to + * the user without showing a dialog. It is often temporarily shown + * at the top or bottom of a document. In contrast to #GtkDialog, which + * has a horizontal action area at the bottom, #GtkInfoBar has a + * vertical action area at the side. + * + * The API of #GtkInfoBar is very similar to #GtkDialog, allowing you + * to add buttons to the action area with gtk_info_bar_add_button() or + * gtk_info_bar_new_with_buttons(). The sensitivity of action widgets + * can be controlled with gtk_info_bar_set_response_sensitive(). + * To add widgets to the main content area of a #GtkInfoBar, use + * gtk_info_bar_set_contents(). + * + * Similar to #GtkMessageDialog, the contents of a #GtkInfoBar can by + * classified as error message, warning, informational message, etc, + * by using gtk_info_bar_set_message_type(). GTK+ uses the message type + * to determine the background color of the message area. + * + * <example> + * <title>Simple GtkInfoBar usage.</title> + * <programlisting> + * /* set up info bar */ + * info_bar = gtk_info_bar_new (); + * gtk_widget_set_no_show_all (info_bar, TRUE); + * message_label = gtk_label_new (""); + * gtk_widget_show (message_label); + * gtk_info_bar_set_contents (GTK_INFO_BAR (info_bar), message_label); + * gtk_info_bar_add_button (GTK_INFO_BAR (info_bar), + * GTK_STOCK_OK, GTK_RESPONSE_OK); + * g_signal_connect (info_bar, "response", + * G_CALLBACK (gtk_widget_hide), NULL); + * gtk_table_attach (GTK_TABLE (table), + * info_bar, + * 0, 1, 2, 3, + * GTK_EXPAND | GTK_FILL, 0, + * 0, 0); + * + * /* ... */ + * + * /* show an error message */ + * gtk_label_set_text (GTK_LABEL (message_label), error_message); + * gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), + * GTK_MESSAGE_ERROR); + * gtk_widget_show (info_bar); + * </programlisting> + * </example> + * + * <refsect2 id="GtkInfoBar-BUILDER-UI"> + * <title>GtkInfoBar as GtkBuildable</title> + * <para> + * The GtkInfoBar implementation of the GtkBuildable interface exposes + * the content area and action area as internal children with the names + * "content_area" and "action_area". + * </para> + * <para> + * GtkInfoBar supports a custom <action-widgets> element, which + * can contain multiple <action-widget> elements. The "response" + * attribute specifies a numeric response, and the content of the element + * is the id of widget (which should be a child of the dialogs @action_area). + * </para> + * </refsect2> + */ + +#define GTK_INFO_BAR_GET_PRIVATE(object) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((object), \ + GTK_TYPE_INFO_BAR, \ + GtkInfoBarPrivate)) + +enum +{ + PROP_0, + PROP_MESSAGE_TYPE +}; + +struct _GtkInfoBarPrivate +{ + GtkWidget *content_area; + GtkWidget *action_area; + + GtkMessageType message_type; +}; + +typedef struct _ResponseData ResponseData; + +struct _ResponseData +{ + gint response_id; +}; + +enum +{ + RESPONSE, + CLOSE, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL]; + + +static void gtk_info_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_info_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_info_bar_style_set (GtkWidget *widget, + GtkStyle *prev_style); +static gboolean gtk_info_bar_expose (GtkWidget *widget, + GdkEventExpose *event); +static void gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface); +static GObject *gtk_info_bar_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname); +static gboolean gtk_info_bar_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data); + + +G_DEFINE_TYPE_WITH_CODE (GtkInfoBar, gtk_info_bar, GTK_TYPE_HBOX, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_info_bar_buildable_interface_init)) + +static void +gtk_info_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkInfoBar *info_bar; + GtkInfoBarPrivate *priv; + + info_bar = GTK_INFO_BAR (object); + priv = GTK_INFO_BAR_GET_PRIVATE (info_bar); + + switch (prop_id) + { + case PROP_MESSAGE_TYPE: + gtk_info_bar_set_message_type (info_bar, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_info_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkInfoBar *info_bar; + GtkInfoBarPrivate *priv; + + info_bar = GTK_INFO_BAR (object); + priv = GTK_INFO_BAR_GET_PRIVATE (info_bar); + + switch (prop_id) + { + case PROP_MESSAGE_TYPE: + g_value_set_enum (value, gtk_info_bar_get_message_type (info_bar)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_info_bar_finalize (GObject *object) +{ + G_OBJECT_CLASS (gtk_info_bar_parent_class)->finalize (object); +} + +static void +response_data_free (gpointer data) +{ + g_slice_free (ResponseData, data); +} + +static ResponseData * +get_response_data (GtkWidget *widget, + gboolean create) +{ + ResponseData *ad = g_object_get_data (G_OBJECT (widget), + "gtk-info-bar-response-data"); + + if (ad == NULL && create) + { + ad = g_slice_new (ResponseData); + + g_object_set_data_full (G_OBJECT (widget), + I_("gtk-info-bar-response-data"), + ad, + response_data_free); + } + + return ad; +} + +static GtkWidget * +find_button (GtkInfoBar *info_bar, + gint response_id) +{ + GList *children, *list; + GtkWidget *child = NULL; + + children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area)); + + for (list = children; list; list = list->next) + { + ResponseData *rd = get_response_data (list->data, FALSE); + + if (rd && rd->response_id == response_id) + { + child = list->data; + break; + } + } + + g_list_free (children); + + return child; +} + +static void +gtk_info_bar_close (GtkInfoBar *info_bar) +{ + if (!find_button (info_bar, GTK_RESPONSE_CANCEL)) + return; + + gtk_info_bar_response (GTK_INFO_BAR (info_bar), + GTK_RESPONSE_CANCEL); +} + +static gboolean +gtk_info_bar_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkInfoBarPrivate *priv = GTK_INFO_BAR_GET_PRIVATE (widget); + gboolean use_tooltip_style; + const char* type_detail[] = { + "infobar-info", + "infobar-warning", + "infobar-question", + "infobar-error", + "infobar" + }; + const char *detail; + + gtk_widget_style_get (widget, + "use-tooltip-style", &use_tooltip_style, + NULL); + + if (use_tooltip_style) + detail = "toolbar"; + else + detail = type_detail[priv->message_type]; + + gtk_paint_flat_box (widget->style, + widget->window, + GTK_STATE_NORMAL, + GTK_SHADOW_OUT, + NULL, + widget, + detail, + widget->allocation.x, + widget->allocation.y, + widget->allocation.width + 1, + widget->allocation.height + 1); + + if (GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->expose_event) + GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->expose_event (widget, event); + + return FALSE; +} + +static void +gtk_info_bar_class_init (GtkInfoBarClass *klass) +{ + GtkWidgetClass *widget_class; + GObjectClass *object_class; + GtkBindingSet *binding_set; + + widget_class = GTK_WIDGET_CLASS (klass); + object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = gtk_info_bar_get_property; + object_class->set_property = gtk_info_bar_set_property; + object_class->finalize = gtk_info_bar_finalize; + + widget_class->style_set = gtk_info_bar_style_set; + widget_class->expose_event = gtk_info_bar_expose; + + klass->close = gtk_info_bar_close; + + /** + * GtkInfoBar:message-type: + * + * The type of the message. + * + * The type is used to determine the colors to use in the info bar. + * The following symbolic color names can by used to customize + * these colors: + * "info_fg_color", "info_bg_color", + * "warning_fg_color", "warning_bg_color", + * "question_fg_color", "question_bg_color", + * "error_fg_color", "error_bg_color", + * "other_fg_color", "other_bg_color". + * + * Since: 2.18 + */ + g_object_class_install_property (object_class, + PROP_MESSAGE_TYPE, + g_param_spec_enum ("message-type", + P_("Message Type"), + P_("The type of message"), + GTK_TYPE_MESSAGE_TYPE, + GTK_MESSAGE_OTHER, + GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + /** + * GtkInfoBar::response: + * @info_bar: the object on which the signal is emitted + * @response_id: the response ID + * + * Emitted when an action widget is clicked or the application programmer + * calls gtk_dialog_response(). The @response_id depends on which action + * widget was clicked. + * + * Since: 2.18 + */ + signals[RESPONSE] = g_signal_new (I_("response"), + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkInfoBarClass, response), + NULL, NULL, + g_cclosure_marshal_VOID__INT, + G_TYPE_NONE, 1, + G_TYPE_INT); + + /** + * GtkInfoBar::close: + * + * The ::close signal is a + * <link linkend="keybinding-signals">keybinding signal</link> + * which gets emitted when the user uses a keybinding to dismiss + * the info bar. + * + * The default binding for this signal is the Escape key. + * + * Since: 2.18 + */ + signals[CLOSE] = g_signal_new (I_("close"), + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GtkInfoBarClass, close), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /** + * GtkInfoBar:content-area-border: + * + * The width of the border around the content + * content area of the info bar. + * + * Since: 2.18 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("content-area-border", + P_("Content area border"), + P_("Width of border around the content area"), + 0, + G_MAXINT, + 8, + GTK_PARAM_READABLE)); + + /** + * GtkInfoBar:content-area-spacing: + * + * The default spacing used between elements of the + * content area of the info bar. + * + * Since: 2.18 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("content-area-spacing", + P_("Content area spacing"), + P_("Spacing between elements of the area"), + 0, + G_MAXINT, + 16, + GTK_PARAM_READABLE)); + + /** + * GtkInfoBar:button-spacing: + * + * Spacing between buttons in the action area of the info bar. + * + * Since: 2.18 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("button-spacing", + P_("Button spacing"), + P_("Spacing between buttons"), + 0, + G_MAXINT, + 6, + GTK_PARAM_READABLE)); + + /** + * GtkInfoBar:action-area-border: + * + * Width of the border around the action area of the info bar. + * + * Since: 2.18 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("action-area-border", + P_("Action area border"), + P_("Width of border around the action area"), + 0, + G_MAXINT, + 5, + GTK_PARAM_READABLE)); + + /** + * GtkInfoBar:use-tooltip-style: + * + * When %TRUE, use the same background/foreground color as #GtkTooltip. + * Otherwise, GTK+ uses #GtkInfoBar::message-type to determine which + * symbolic colors to use. + * + * Since: 2.18 + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("use-tooltip-style", + P_("Use tooltip style"), + P_("Wether to use the same style as GtkTooltip for drawing"), + TRUE, + GTK_PARAM_READABLE)); + + binding_set = gtk_binding_set_by_class (klass); + + gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0, "close", 0); + + g_type_class_add_private (object_class, sizeof (GtkInfoBarPrivate)); +} + +static void +gtk_info_bar_update_colors (GtkInfoBar *info_bar) +{ + GtkWidget *widget = (GtkWidget*)info_bar; + GtkInfoBarPrivate *priv; + GdkColor default_border_color = { 0, 0xb800, 0xad00, 0x9d00 }; + GdkColor default_fill_color = { 0, 0xff00, 0xff00, 0xbf00 }; + GdkColor *fg, *bg; + GdkColor sym_fg, sym_bg; + gboolean use_tooltip_style; + GtkStyle *style; + const char* fg_color_name[] = { + "info_fg_color", + "warning_fg_color", + "question_fg_color", + "error_fg_color", + "other_fg_color" + }; + const char* bg_color_name[] = { + "info_bg_color", + "warning_bg_color", + "question_bg_color", + "error_bg_color", + "other_bg_color" + }; + gboolean has_color; + + priv = GTK_INFO_BAR_GET_PRIVATE (info_bar); + style = gtk_widget_get_style (widget); + + gtk_widget_style_get (widget, + "use-tooltip-style", &use_tooltip_style, NULL); + + has_color = FALSE; + + if (gtk_style_lookup_color (style, fg_color_name[priv->message_type], &sym_fg) && + gtk_style_lookup_color (style, bg_color_name[priv->message_type], &sym_bg)) + { + fg = &sym_fg; + bg = &sym_bg; + has_color = TRUE; + } + else if (use_tooltip_style) + { + style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget), + "gtk-tooltip", "GtkTooltip", G_TYPE_NONE); + if (style) + { + fg = &style->fg[GTK_STATE_NORMAL]; + bg = &style->bg[GTK_STATE_NORMAL]; + } + else + { + fg = &default_border_color; + bg = &default_fill_color; + } + + has_color = TRUE; + } + + if (has_color) + { + if (!gdk_color_equal (bg, &widget->style->bg[GTK_STATE_NORMAL])) + gtk_widget_modify_bg (widget, GTK_STATE_NORMAL, bg); + if (!gdk_color_equal (fg, &widget->style->fg[GTK_STATE_NORMAL])) + gtk_widget_modify_fg (widget, GTK_STATE_NORMAL, fg); + } +} + +static void +gtk_info_bar_style_set (GtkWidget *widget, + GtkStyle *prev_style) +{ + GtkInfoBar *info_bar = GTK_INFO_BAR (widget); + gint button_spacing; + gint action_area_border; + gint content_area_spacing; + gint content_area_border; + + gtk_widget_style_get (widget, + "button-spacing", &button_spacing, + "action-area-border", &action_area_border, + "content-area-spacing", &content_area_spacing, + "content-area-border", &content_area_border, + NULL); + + gtk_box_set_spacing (GTK_BOX (info_bar->priv->action_area), button_spacing); + gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->action_area), + action_area_border); + gtk_box_set_spacing (GTK_BOX (info_bar->priv->content_area), content_area_spacing); + gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->content_area), + content_area_border); + + gtk_info_bar_update_colors (info_bar); +} + +static void +gtk_info_bar_init (GtkInfoBar *info_bar) +{ + GtkWidget *content_area; + GtkWidget *action_area; + + gtk_widget_push_composite_child (); + + info_bar->priv = GTK_INFO_BAR_GET_PRIVATE (info_bar); + + content_area = gtk_hbox_new (FALSE, 0); + gtk_widget_show (content_area); + gtk_box_pack_start (GTK_BOX (info_bar), content_area, TRUE, TRUE, 0); + + action_area = gtk_vbutton_box_new (); + gtk_widget_show (action_area); + gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END); + gtk_box_pack_start (GTK_BOX (info_bar), action_area, FALSE, TRUE, 0); + + gtk_widget_set_app_paintable (GTK_WIDGET (info_bar), TRUE); + + info_bar->priv->content_area = content_area; + info_bar->priv->action_area = action_area; + + gtk_widget_pop_composite_child (); +} + +static GtkBuildableIface *parent_buildable_iface; + +static void +gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface) +{ + parent_buildable_iface = g_type_interface_peek_parent (iface); + iface->get_internal_child = gtk_info_bar_buildable_get_internal_child; + iface->custom_tag_start = gtk_info_bar_buildable_custom_tag_start; + iface->custom_finished = gtk_info_bar_buildable_custom_finished; +} + +static GObject * +gtk_info_bar_buildable_get_internal_child (GtkBuildable *buildable, + GtkBuilder *builder, + const gchar *childname) +{ + if (strcmp (childname, "content_area") == 0) + return G_OBJECT (GTK_INFO_BAR (buildable)->priv->content_area); + else if (strcmp (childname, "action_area") == 0) + return G_OBJECT (GTK_INFO_BAR (buildable)->priv->action_area); + + return parent_buildable_iface->get_internal_child (buildable, + builder, + childname); +} + +static gint +get_response_for_widget (GtkInfoBar *info_bar, + GtkWidget *widget) +{ + ResponseData *rd; + + rd = get_response_data (widget, FALSE); + if (!rd) + return GTK_RESPONSE_NONE; + else + return rd->response_id; +} + +static void +action_widget_activated (GtkWidget *widget, + GtkInfoBar *info_bar) +{ + gint response_id; + + response_id = get_response_for_widget (info_bar, widget); + gtk_info_bar_response (info_bar, response_id); +} + +/** + * gtk_info_bar_add_action_widget: + * @info_bar: a #GtkInfoBar + * @child: an activatable widget + * @response_id: response ID for @child + * + * Add an activatable widget to the action area of a #GtkInfoBar, + * connecting a signal handler that will emit the #GtkInfoBar::response + * signal on the message area when the widget is activated. The widget + * is appended to the end of the message areas action area. + * + * Since: 2.18 + */ +void +gtk_info_bar_add_action_widget (GtkInfoBar *info_bar, + GtkWidget *child, + gint response_id) +{ + ResponseData *ad; + guint signal_id; + + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + g_return_if_fail (GTK_IS_WIDGET (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; + + if (signal_id) + { + GClosure *closure; + + closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), + G_OBJECT (info_bar)); + g_signal_connect_closure_by_id (child, signal_id, 0, closure, FALSE); + } + else + g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkInfoBar"); + + gtk_box_pack_end (GTK_BOX (info_bar->priv->action_area), + child, FALSE, FALSE, 0); + if (response_id == GTK_RESPONSE_HELP) + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area), + child, TRUE); +} + +/** + * gtk_info_bar_get_action_area: + * @info_bar: a #GtkInfoBar + * + * Returns the action area of @info_bar. + * + * Returns: the action area. + * + * Since: 2.18 + */ +GtkWidget* +gtk_info_bar_get_action_area (GtkInfoBar *info_bar) +{ + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + return info_bar->priv->action_area; +} + +/** + * gtk_info_bar_get_content_area: + * @info_bar: a #GtkInfoBar + * + * Returns the content area of @info_bar. + * + * Returns: the content area. + * + * Since: 2.18 + */ +GtkWidget* +gtk_info_bar_get_content_area (GtkInfoBar *info_bar) +{ + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + return info_bar->priv->content_area; +} + +/** + * gtk_info_bar_add_button: + * @info_bar: a #GtkInfoBar + * @button_text: text of button, or stock ID + * @response_id: response ID for the button + * + * Adds a button with the given text (or a stock button, if button_text + * is a stock ID) and sets things up so that clicking the button will emit + * the "response" signal with the given response_id. The button is appended + * to the end of the info bars's action area. The button widget is + * returned, but usually you don't need it. + * + * Returns: the button widget that was added + * + * Since: 2.18 + */ +GtkWidget* +gtk_info_bar_add_button (GtkInfoBar *info_bar, + const gchar *button_text, + gint response_id) +{ + GtkWidget *button; + + g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL); + g_return_val_if_fail (button_text != NULL, NULL); + + button = gtk_button_new_from_stock (button_text); + + GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); + + gtk_widget_show (button); + + gtk_info_bar_add_action_widget (info_bar, button, response_id); + + return button; +} + +static void +add_buttons_valist (GtkInfoBar *info_bar, + const gchar *first_button_text, + va_list args) +{ + const gchar* text; + gint response_id; + + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + if (first_button_text == NULL) + return; + + text = first_button_text; + response_id = va_arg (args, gint); + + while (text != NULL) + { + gtk_info_bar_add_button (info_bar, text, response_id); + + text = va_arg (args, gchar*); + if (text == NULL) + break; + + response_id = va_arg (args, int); + } +} + +/** + * gtk_info_bar_add_buttons: + * @info_bar: a #GtkInfoBar + * @first_button_text: button text or stock ID + * @...: response ID for first button, then more text-response_id pairs, + * ending with %NULL + * + * Adds more buttons, same as calling gtk_info_bar_add_button() + * repeatedly. The variable argument list should be %NULL-terminated + * as with gtk_info_bar_new_with_buttons(). Each button must have both + * text and response ID. + * + * Since: 2.18 + */ +void +gtk_info_bar_add_buttons (GtkInfoBar *info_bar, + const gchar *first_button_text, + ...) +{ + va_list args; + + va_start (args, first_button_text); + add_buttons_valist (info_bar, first_button_text, args); + va_end (args); +} + +/** + * gtk_info_bar_new: + * + * Creates a new #GtkInfoBar object. + * + * Returns: a new #GtkInfoBar object + * + * Since: 2.18 + */ +GtkWidget * +gtk_info_bar_new (void) +{ + return g_object_new (GTK_TYPE_INFO_BAR, NULL); +} + +/** + * gtk_info_bar_new_with_buttons: + * @first_button_text: stock ID or text to go in first button, or %NULL + * @...: response ID for first button, then additional buttons, ending + * with %NULL + * + * Creates a new #GtkInfoBar with buttons. Button text/response ID + * pairs should be listed, with a %NULL pointer ending the list. + * Button text can be either a stock ID such as %GTK_STOCK_OK, or + * some arbitrary text. A response ID can be any positive number, + * or one of the values in the #GtkResponseType enumeration. If the + * user clicks one of these dialog buttons, GtkInfoBar will emit + * the "response" signal with the corresponding response ID. + * + * Returns: a new #GtkInfoBar + */ +GtkWidget* +gtk_info_bar_new_with_buttons (const gchar *first_button_text, + ...) +{ + GtkInfoBar *info_bar; + va_list args; + + info_bar = GTK_INFO_BAR (gtk_info_bar_new ()); + + va_start (args, first_button_text); + add_buttons_valist (info_bar, first_button_text, args); + va_end (args); + + return GTK_WIDGET (info_bar); +} + +/** + * gtk_info_bar_set_response_sensitive: + * @info_bar: a #GtkInfoBar + * @response_id: a response ID + * @setting: TRUE for sensitive + * + * Calls gtk_widget_set_sensitive (widget, setting) for each + * widget in the info bars's action area with the given response_id. + * A convenient way to sensitize/desensitize dialog buttons. + * + * Since: 2.18 + */ +void +gtk_info_bar_set_response_sensitive (GtkInfoBar *info_bar, + gint response_id, + gboolean setting) +{ + GList *children, *list; + + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area)); + + for (list = children; list; list = list->next) + { + GtkWidget *widget = list->data; + ResponseData *rd = get_response_data (widget, FALSE); + + if (rd && rd->response_id == response_id) + gtk_widget_set_sensitive (widget, setting); + } + + g_list_free (children); +} + +/** + * gtk_info_bar_set_default_response: + * @info_bar: a #GtkInfoBar + * @response_id: a response ID + * + * Sets the last widget in the info bar's action area with + * the given response_id as the default widget for the dialog. + * Pressing "Enter" normally activates the default widget. + * + * Since: 2.18 + */ +void +gtk_info_bar_set_default_response (GtkInfoBar *info_bar, + gint response_id) +{ + GList *children, *list; + + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area)); + + for (list = children; list; list = list->next) + { + GtkWidget *widget = list->data; + ResponseData *rd = get_response_data (widget, FALSE); + + if (rd && rd->response_id == response_id) + gtk_widget_grab_default (widget); + } + + g_list_free (children); +} + +/** + * gtk_info_bar_set_default_response: + * @info_bar: a #GtkInfoBar + * @response_id: a response ID + * + * Emits the 'response' signal with the given @response_id. + * + * Since: 2.18 + */ +void +gtk_info_bar_response (GtkInfoBar *info_bar, + gint response_id) +{ + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + g_signal_emit (info_bar, signals[RESPONSE], 0, response_id); +} + +typedef struct +{ + gchar *widget_name; + gchar *response_id; +} ActionWidgetInfo; + +typedef struct +{ + GtkInfoBar *info_bar; + GtkBuilder *builder; + GSList *items; + gchar *response; +} ActionWidgetsSubParserData; + +static void +attributes_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data; + guint i; + + if (strcmp (element_name, "action-widget") == 0) + { + for (i = 0; names[i]; i++) + if (strcmp (names[i], "response") == 0) + parser_data->response = g_strdup (values[i]); + } + else if (strcmp (element_name, "action-widgets") == 0) + return; + else + g_warning ("Unsupported tag for GtkInfoBar: %s\n", element_name); +} + +static void +attributes_text_element (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data; + ActionWidgetInfo *item; + + if (!parser_data->response) + return; + + item = g_new (ActionWidgetInfo, 1); + item->widget_name = g_strndup (text, text_len); + item->response_id = parser_data->response; + parser_data->items = g_slist_prepend (parser_data->items, item); + parser_data->response = NULL; +} + +static const GMarkupParser attributes_parser = +{ + attributes_start_element, + NULL, + attributes_text_element, +}; + +gboolean +gtk_info_bar_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + ActionWidgetsSubParserData *parser_data; + + if (child) + return FALSE; + + if (strcmp (tagname, "action-widgets") == 0) + { + parser_data = g_slice_new0 (ActionWidgetsSubParserData); + parser_data->info_bar = GTK_INFO_BAR (buildable); + parser_data->items = NULL; + + *parser = attributes_parser; + *data = parser_data; + return TRUE; + } + + return parent_buildable_iface->custom_tag_start (buildable, builder, child, + tagname, parser, data); +} + +static void +gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer user_data) +{ + GSList *l; + ActionWidgetsSubParserData *parser_data; + GObject *object; + GtkInfoBar *info_bar; + ResponseData *ad; + guint signal_id; + + if (strcmp (tagname, "action-widgets")) + { + parent_buildable_iface->custom_finished (buildable, builder, child, + tagname, user_data); + return; + } + + info_bar = GTK_INFO_BAR (buildable); + parser_data = (ActionWidgetsSubParserData*)user_data; + parser_data->items = g_slist_reverse (parser_data->items); + + for (l = parser_data->items; l; l = l->next) + { + ActionWidgetInfo *item = l->data; + + object = gtk_builder_get_object (builder, item->widget_name); + if (!object) + { + g_warning ("Unknown object %s specified in action-widgets of %s", + item->widget_name, + gtk_buildable_get_name (GTK_BUILDABLE (buildable))); + continue; + } + + ad = get_response_data (GTK_WIDGET (object), TRUE); + ad->response_id = atoi (item->response_id); + + if (GTK_IS_BUTTON (object)) + signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON); + else + signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal; + + if (signal_id) + { + GClosure *closure; + + closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), + G_OBJECT (info_bar)); + g_signal_connect_closure_by_id (object, + signal_id, + 0, + closure, + FALSE); + } + + if (ad->response_id == GTK_RESPONSE_HELP) + gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area), + GTK_WIDGET (object), TRUE); + + g_free (item->widget_name); + g_free (item->response_id); + g_free (item); + } + g_slist_free (parser_data->items); + g_slice_free (ActionWidgetsSubParserData, parser_data); +} + +/** + * gtk_info_bar_set_message_type: + * @info_bar: a #GtkInfoBar + * @message_type: a #GtkMessageType + * + * Sets the message type of the message area. + * GTK+ uses this type to determine what color to use + * when drawing the message area. + * + * Since: 2.18 + */ +void +gtk_info_bar_set_message_type (GtkInfoBar *info_bar, + GtkMessageType message_type) +{ + GtkInfoBarPrivate *priv; + AtkObject *atk_obj; + + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + priv = GTK_INFO_BAR_GET_PRIVATE (info_bar); + + if (priv->message_type != message_type) + { + priv->message_type = message_type; + + gtk_info_bar_update_colors (info_bar); + gtk_widget_queue_draw (GTK_WIDGET (info_bar)); + + atk_obj = gtk_widget_get_accessible (GTK_WIDGET (info_bar)); + if (GTK_IS_ACCESSIBLE (atk_obj)) + { + GtkStockItem item; + const char *stock_id = NULL; + + atk_object_set_role (atk_obj, ATK_ROLE_ALERT); + + switch (message_type) + { + case GTK_MESSAGE_INFO: + stock_id = GTK_STOCK_DIALOG_INFO; + break; + + case GTK_MESSAGE_QUESTION: + stock_id = GTK_STOCK_DIALOG_QUESTION; + break; + + case GTK_MESSAGE_WARNING: + stock_id = GTK_STOCK_DIALOG_WARNING; + break; + + case GTK_MESSAGE_ERROR: + stock_id = GTK_STOCK_DIALOG_ERROR; + break; + + case GTK_MESSAGE_OTHER: + break; + + default: + g_warning ("Unknown GtkMessageType %u", message_type); + break; + } + + if (stock_id) + { + gtk_stock_lookup (stock_id, &item); + atk_object_set_name (atk_obj, item.label); + } + } + + g_object_notify (G_OBJECT (info_bar), "message-type"); + } +} + +/** + * gtk_info_bar_get_message_type: + * @info_bar: a #GtkInfoBar + * + * Returns the message type of the message area. + * + * Returns: the message type of the message area. + * + * Since: 2.18 + */ +GtkMessageType +gtk_info_bar_get_message_type (GtkInfoBar *info_bar) +{ + GtkInfoBarPrivate *priv; + + g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); + + priv = GTK_INFO_BAR_GET_PRIVATE (info_bar); + + return priv->message_type; +} + + +#define __GTK_INFO_BAR_C__ +#include "gtkaliasdef.c" diff --git a/gtk/gtkinfobar.h b/gtk/gtkinfobar.h new file mode 100644 index 0000000000..cbc206a120 --- /dev/null +++ b/gtk/gtkinfobar.h @@ -0,0 +1,116 @@ +/* + * gtkinfobar.h + * This file is part of GTK+ + * + * Copyright (C) 2005 - Paolo Maggi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the gedit Team, 2005. See the gedit AUTHORS file for a + * list of people on the gedit Team. + * See the gedit ChangeLog files for a list of changes. + * + * Modified by the GTK+ Team, 2008-2009. + */ + +#ifndef __GTK_INFO_BAR_H__ +#define __GTK_INFO_BAR_H__ + +#include <gtk/gtkhbox.h> +#include <gtk/gtkenums.h> + +G_BEGIN_DECLS + +/* + * Type checking and casting macros + */ +#define GTK_TYPE_INFO_BAR (gtk_info_bar_get_type()) +#define GTK_INFO_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_INFO_BAR, GtkInfoBar)) +#define GTK_INFO_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_INFO_BAR, GtkInfoBarClass)) +#define GTK_IS_INFO_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_INFO_BAR)) +#define GTK_IS_INFO_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_INFO_BAR)) +#define GTK_INFO_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_TYPE_INFO_BAR, GtkInfoBarClass)) + + +typedef struct _GtkInfoBarPrivate GtkInfoBarPrivate; +typedef struct _GtkInfoBarClass GtkInfoBarClass; +typedef struct _GtkInfoBar GtkInfoBar; + + +struct _GtkInfoBar +{ + GtkHBox parent; + + /*< private > */ + GtkInfoBarPrivate *priv; +}; + + +struct _GtkInfoBarClass +{ + GtkHBoxClass parent_class; + + /* Signals */ + void (* response) (GtkInfoBar *info_bar, gint response_id); + + /* Keybinding signals */ + void (* close) (GtkInfoBar *info_bar); + + /* Padding for future expansion */ + void (*_gtk_reserved1) (void); + void (*_gtk_reserved2) (void); + void (*_gtk_reserved3) (void); + void (*_gtk_reserved4) (void); + void (*_gtk_reserved5) (void); + void (*_gtk_reserved6) (void); +}; + +GType gtk_info_bar_get_type (void) G_GNUC_CONST; +GtkWidget *gtk_info_bar_new (void); + +GtkWidget *gtk_info_bar_new_with_buttons (const gchar *first_button_text, + ...); + +GtkWidget *gtk_info_bar_get_action_area (GtkInfoBar *info_bar); +GtkWidget *gtk_info_bar_get_content_area (GtkInfoBar *info_bar); +void gtk_info_bar_add_action_widget (GtkInfoBar *info_bar, + GtkWidget *child, + gint response_id); +GtkWidget *gtk_info_bar_add_button (GtkInfoBar *info_bar, + const gchar *button_text, + gint response_id); +void gtk_info_bar_add_buttons (GtkInfoBar *info_bar, + const gchar *first_button_text, + ...); +void gtk_info_bar_set_response_sensitive (GtkInfoBar *info_bar, + gint response_id, + gboolean setting); +void gtk_info_bar_set_default_response (GtkInfoBar *info_bar, + gint response_id); + +/* Emit response signal */ +void gtk_info_bar_response (GtkInfoBar *info_bar, + gint response_id); + +void gtk_info_bar_set_message_type (GtkInfoBar *info_bar, + GtkMessageType message_type); +GtkMessageType gtk_info_bar_get_message_type (GtkInfoBar *info_bar); + +G_END_DECLS + +#endif /* __GTK_INFO_BAR_H__ */ diff --git a/gtk/gtkmessagedialog.h b/gtk/gtkmessagedialog.h index 947773c76d..1bb1ec5fc2 100644 --- a/gtk/gtkmessagedialog.h +++ b/gtk/gtkmessagedialog.h @@ -32,20 +32,12 @@ #define __GTK_MESSAGE_DIALOG_H__ #include <gtk/gtkdialog.h> +#include <gtk/gtkenums.h> G_BEGIN_DECLS typedef enum { - GTK_MESSAGE_INFO, - GTK_MESSAGE_WARNING, - GTK_MESSAGE_QUESTION, - GTK_MESSAGE_ERROR, - GTK_MESSAGE_OTHER -} GtkMessageType; - -typedef enum -{ GTK_BUTTONS_NONE, GTK_BUTTONS_OK, GTK_BUTTONS_CLOSE, diff --git a/gtk/tests/builder.c b/gtk/tests/builder.c index 7b2cc19b60..3dbf40d1e6 100644 --- a/gtk/tests/builder.c +++ b/gtk/tests/builder.c @@ -2483,6 +2483,56 @@ test_file (const gchar *filename) builder = NULL; } +static void +test_message_area (void) +{ + GtkBuilder *builder; + GError *error; + GObject *obj, *obj1; + const gchar buffer[] = + "<interface>" + " <object class=\"GtkInfoBar\" id=\"infobar1\">" + " <child internal-child=\"content_area\">" + " <object class=\"GtkHBox\" id=\"contentarea1\">" + " <child>" + " <object class=\"GtkLabel\" id=\"content\">" + " <property name=\"label\" translatable=\"yes\">Message</property>" + " </object>" + " </child>" + " </object>" + " </child>" + " <child internal-child=\"action_area\">" + " <object class=\"GtkVButtonBox\" id=\"actionarea1\">" + " <child>" + " <object class=\"GtkButton\" id=\"button_ok\">" + " <property name=\"label\">gtk-ok</property>" + " <property name=\"use-stock\">yes</property>" + " </object>" + " </child>" + " </object>" + " </child>" + " <action-widgets>" + " <action-widget response=\"1\">button_ok</action-widget>" + " </action-widgets>" + " </object>" + "</interface>"; + + error = NULL; + builder = builder_new_from_string (buffer, -1, &error); + g_assert (error == NULL); + obj = gtk_builder_get_object (builder, "infobar1"); + g_assert (GTK_IS_INFO_BAR (obj)); + obj1 = gtk_builder_get_object (builder, "content"); + g_assert (GTK_IS_LABEL (obj1)); + g_assert (gtk_widget_get_parent (gtk_widget_get_parent (obj1)) == obj); + + obj1 = gtk_builder_get_object (builder, "button_ok"); + g_assert (GTK_IS_BUTTON (obj1)); + g_assert (gtk_widget_get_parent (gtk_widget_get_parent (obj1)) == obj); + + g_object_unref (builder); +} + int main (int argc, char **argv) { @@ -2525,6 +2575,7 @@ main (int argc, char **argv) g_test_add_func ("/Builder/Requires", test_requires); g_test_add_func ("/Builder/AddObjects", test_add_objects); g_test_add_func ("/Builder/Menus", test_menus); + g_test_add_func ("/Builder/MessageArea", test_message_area); return g_test_run(); } |