]> Pileus Git - ~andy/gtk/blob - gtk/gtkinfobar.c
Remove gtktypeutils altogether
[~andy/gtk] / gtk / gtkinfobar.c
1 /*
2  * gtkinfobar.c
3  * This file is part of GTK+
4  *
5  * Copyright (C) 2005 - Paolo Maggi
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /*
24  * Modified by the gedit Team, 2005. See the AUTHORS file for a
25  * list of people on the gtk Team.
26  * See the gedit ChangeLog files for a list of changes.
27  *
28  * Modified by the GTK+ team, 2008-2009.
29  */
30
31
32 #include "config.h"
33
34 #include <stdlib.h>
35
36 #include "gtkinfobar.h"
37 #include "gtkaccessible.h"
38 #include "gtkbuildable.h"
39 #include "gtkbox.h"
40 #include "gtkvbbox.h"
41 #include "gtklabel.h"
42 #include "gtkbutton.h"
43 #include "gtkenums.h"
44 #include "gtkbindings.h"
45 #include "gtkdialog.h"
46 #include "gtkintl.h"
47 #include "gtkprivate.h"
48 #include "gtkstock.h"
49 #include "gtktypebuiltins.h"
50
51 /**
52  * SECTION:gtkinfobar
53  * @short_description: Report important messages to the user
54  * @include: gtk/gtk.h
55  * @see_also: #GtkStatusbar, #GtkMessageDialog
56  *
57  * #GtkInfoBar is a widget that can be used to show messages to
58  * the user without showing a dialog. It is often temporarily shown
59  * at the top or bottom of a document. In contrast to #GtkDialog, which
60  * has a horizontal action area at the bottom, #GtkInfoBar has a
61  * vertical action area at the side.
62  *
63  * The API of #GtkInfoBar is very similar to #GtkDialog, allowing you
64  * to add buttons to the action area with gtk_info_bar_add_button() or
65  * gtk_info_bar_new_with_buttons(). The sensitivity of action widgets
66  * can be controlled with gtk_info_bar_set_response_sensitive().
67  * To add widgets to the main content area of a #GtkInfoBar, use
68  * gtk_info_bar_get_content_area() and add your widgets to the container.
69  *
70  * Similar to #GtkMessageDialog, the contents of a #GtkInfoBar can by
71  * classified as error message, warning, informational message, etc,
72  * by using gtk_info_bar_set_message_type(). GTK+ uses the message type
73  * to determine the background color of the message area.
74  *
75  * <example>
76  * <title>Simple GtkInfoBar usage.</title>
77  * <programlisting>
78  * /&ast; set up info bar &ast;/
79  * info_bar = gtk_info_bar_new ();
80  * gtk_widget_set_no_show_all (info_bar, TRUE);
81  * message_label = gtk_label_new ("");
82  * gtk_widget_show (message_label);
83  * content_area = gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar));
84  * gtk_container_add (GTK_CONTAINER (content_area), message_label);
85  * gtk_info_bar_add_button (GTK_INFO_BAR (info_bar),
86  *                          GTK_STOCK_OK, GTK_RESPONSE_OK);
87  * g_signal_connect (info_bar, "response",
88  *                   G_CALLBACK (gtk_widget_hide), NULL);
89  * gtk_table_attach (GTK_TABLE (table),
90  *                   info_bar,
91  *                   0, 1, 2, 3,
92  *                   GTK_EXPAND | GTK_FILL,  0,
93  *                   0,                      0);
94  *
95  * /&ast; ... &ast;/
96  *
97  * /&ast; show an error message &ast;/
98  * gtk_label_set_text (GTK_LABEL (message_label), error_message);
99  * gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar),
100  *                                GTK_MESSAGE_ERROR);
101  * gtk_widget_show (info_bar);
102  * </programlisting>
103  * </example>
104  *
105  * <refsect2 id="GtkInfoBar-BUILDER-UI">
106  * <title>GtkInfoBar as GtkBuildable</title>
107  * <para>
108  * The GtkInfoBar implementation of the GtkBuildable interface exposes
109  * the content area and action area as internal children with the names
110  * "content_area" and "action_area".
111  * </para>
112  * <para>
113  * GtkInfoBar supports a custom &lt;action-widgets&gt; element, which
114  * can contain multiple &lt;action-widget&gt; elements. The "response"
115  * attribute specifies a numeric response, and the content of the element
116  * is the id of widget (which should be a child of the dialogs @action_area).
117  * </para>
118  * </refsect2>
119  */
120
121 enum
122 {
123   PROP_0,
124   PROP_MESSAGE_TYPE
125 };
126
127 struct _GtkInfoBarPrivate
128 {
129   GtkWidget *content_area;
130   GtkWidget *action_area;
131
132   GtkMessageType message_type;
133 };
134
135 typedef struct _ResponseData ResponseData;
136
137 struct _ResponseData
138 {
139   gint response_id;
140 };
141
142 enum
143 {
144   RESPONSE,
145   CLOSE,
146   LAST_SIGNAL
147 };
148
149 static guint signals[LAST_SIGNAL];
150
151
152 static void     gtk_info_bar_set_property (GObject        *object,
153                                            guint           prop_id,
154                                            const GValue   *value,
155                                            GParamSpec     *pspec);
156 static void     gtk_info_bar_get_property (GObject        *object,
157                                            guint           prop_id,
158                                            GValue         *value,
159                                            GParamSpec     *pspec);
160 static void     gtk_info_bar_style_updated (GtkWidget      *widget);
161 static gboolean gtk_info_bar_draw         (GtkWidget      *widget,
162                                            cairo_t        *cr);
163 static void     gtk_info_bar_buildable_interface_init     (GtkBuildableIface *iface);
164 static GObject *gtk_info_bar_buildable_get_internal_child (GtkBuildable  *buildable,
165                                                            GtkBuilder    *builder,
166                                                            const gchar   *childname);
167 static gboolean  gtk_info_bar_buildable_custom_tag_start   (GtkBuildable  *buildable,
168                                                             GtkBuilder    *builder,
169                                                             GObject       *child,
170                                                             const gchar   *tagname,
171                                                             GMarkupParser *parser,
172                                                             gpointer      *data);
173 static void      gtk_info_bar_buildable_custom_finished    (GtkBuildable  *buildable,
174                                                             GtkBuilder    *builder,
175                                                             GObject       *child,
176                                                             const gchar   *tagname,
177                                                             gpointer       user_data);
178
179
180 G_DEFINE_TYPE_WITH_CODE (GtkInfoBar, gtk_info_bar, GTK_TYPE_HBOX,
181                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
182                                                 gtk_info_bar_buildable_interface_init))
183
184 static void
185 gtk_info_bar_set_property (GObject      *object,
186                            guint         prop_id,
187                            const GValue *value,
188                            GParamSpec   *pspec)
189 {
190   GtkInfoBar *info_bar;
191   GtkInfoBarPrivate *priv;
192
193   info_bar = GTK_INFO_BAR (object);
194   priv = info_bar->priv;
195
196   switch (prop_id)
197     {
198     case PROP_MESSAGE_TYPE:
199       gtk_info_bar_set_message_type (info_bar, g_value_get_enum (value));
200       break;
201     default:
202       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203       break;
204     }
205 }
206
207 static void
208 gtk_info_bar_get_property (GObject    *object,
209                            guint       prop_id,
210                            GValue     *value,
211                            GParamSpec *pspec)
212 {
213   GtkInfoBar *info_bar;
214   GtkInfoBarPrivate *priv;
215
216   info_bar = GTK_INFO_BAR (object);
217   priv = info_bar->priv;
218
219   switch (prop_id)
220     {
221     case PROP_MESSAGE_TYPE:
222       g_value_set_enum (value, gtk_info_bar_get_message_type (info_bar));
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227     }
228 }
229
230 static void
231 gtk_info_bar_finalize (GObject *object)
232 {
233   G_OBJECT_CLASS (gtk_info_bar_parent_class)->finalize (object);
234 }
235
236 static void
237 response_data_free (gpointer data)
238 {
239   g_slice_free (ResponseData, data);
240 }
241
242 static ResponseData *
243 get_response_data (GtkWidget *widget,
244                    gboolean   create)
245 {
246   ResponseData *ad = g_object_get_data (G_OBJECT (widget),
247                                         "gtk-info-bar-response-data");
248
249   if (ad == NULL && create)
250     {
251       ad = g_slice_new (ResponseData);
252
253       g_object_set_data_full (G_OBJECT (widget),
254                               I_("gtk-info-bar-response-data"),
255                               ad,
256                               response_data_free);
257     }
258
259   return ad;
260 }
261
262 static GtkWidget *
263 find_button (GtkInfoBar *info_bar,
264              gint        response_id)
265 {
266   GList *children, *list;
267   GtkWidget *child = NULL;
268
269   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
270
271   for (list = children; list; list = list->next)
272     {
273       ResponseData *rd = get_response_data (list->data, FALSE);
274
275       if (rd && rd->response_id == response_id)
276         {
277           child = list->data;
278           break;
279         }
280     }
281
282   g_list_free (children);
283
284   return child;
285 }
286
287 static void
288 gtk_info_bar_close (GtkInfoBar *info_bar)
289 {
290   if (!find_button (info_bar, GTK_RESPONSE_CANCEL))
291     return;
292
293   gtk_info_bar_response (GTK_INFO_BAR (info_bar),
294                          GTK_RESPONSE_CANCEL);
295 }
296
297 static gboolean
298 gtk_info_bar_draw (GtkWidget      *widget,
299                    cairo_t        *cr)
300 {
301   GtkInfoBarPrivate *priv = GTK_INFO_BAR (widget)->priv;
302   const char* type_class[] = {
303     GTK_STYLE_CLASS_INFO,
304     GTK_STYLE_CLASS_WARNING,
305     GTK_STYLE_CLASS_QUESTION,
306     GTK_STYLE_CLASS_ERROR,
307     NULL
308   };
309
310   if (priv->message_type != GTK_MESSAGE_OTHER)
311     {
312       GtkStyleContext *context;
313
314       context = gtk_widget_get_style_context (widget);
315
316       gtk_style_context_save (context);
317
318       if (type_class[priv->message_type])
319         gtk_style_context_add_class (context,
320                                      type_class[priv->message_type]);
321
322       gtk_render_background (context, cr, 0, 0,
323                              gtk_widget_get_allocated_width (widget),
324                              gtk_widget_get_allocated_height (widget));
325       gtk_render_frame (context, cr, 0, 0,
326                         gtk_widget_get_allocated_width (widget),
327                         gtk_widget_get_allocated_height (widget));
328
329       gtk_style_context_restore (context);
330     }
331
332   if (GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->draw)
333     GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->draw (widget, cr);
334
335   return FALSE;
336 }
337
338 static void
339 gtk_info_bar_class_init (GtkInfoBarClass *klass)
340 {
341   GtkWidgetClass *widget_class;
342   GObjectClass *object_class;
343   GtkBindingSet *binding_set;
344
345   widget_class = GTK_WIDGET_CLASS (klass);
346   object_class = G_OBJECT_CLASS (klass);
347
348   object_class->get_property = gtk_info_bar_get_property;
349   object_class->set_property = gtk_info_bar_set_property;
350   object_class->finalize = gtk_info_bar_finalize;
351
352   widget_class->style_updated = gtk_info_bar_style_updated;
353   widget_class->draw = gtk_info_bar_draw;
354
355   klass->close = gtk_info_bar_close;
356
357   /**
358    * GtkInfoBar:message-type:
359    *
360    * The type of the message.
361    *
362    * The type is used to determine the colors to use in the info bar.
363    * The following symbolic color names can by used to customize
364    * these colors:
365    * "info_fg_color", "info_bg_color",
366    * "warning_fg_color", "warning_bg_color",
367    * "question_fg_color", "question_bg_color",
368    * "error_fg_color", "error_bg_color".
369    * "other_fg_color", "other_bg_color".
370    *
371    * If the type is #GTK_MESSAGE_OTHER, no info bar is painted but the
372    * colors are still set.
373    *
374    * Since: 2.18
375    */
376   g_object_class_install_property (object_class,
377                                    PROP_MESSAGE_TYPE,
378                                    g_param_spec_enum ("message-type",
379                                                       P_("Message Type"),
380                                                       P_("The type of message"),
381                                                       GTK_TYPE_MESSAGE_TYPE,
382                                                       GTK_MESSAGE_INFO,
383                                                       GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
384   /**
385    * GtkInfoBar::response:
386    * @info_bar: the object on which the signal is emitted
387    * @response_id: the response ID
388    *
389    * Emitted when an action widget is clicked or the application programmer
390    * calls gtk_dialog_response(). The @response_id depends on which action
391    * widget was clicked.
392    *
393    * Since: 2.18
394    */
395   signals[RESPONSE] = g_signal_new (I_("response"),
396                                     G_OBJECT_CLASS_TYPE (klass),
397                                     G_SIGNAL_RUN_LAST,
398                                     G_STRUCT_OFFSET (GtkInfoBarClass, response),
399                                     NULL, NULL,
400                                     g_cclosure_marshal_VOID__INT,
401                                     G_TYPE_NONE, 1,
402                                     G_TYPE_INT);
403
404   /**
405    * GtkInfoBar::close:
406    *
407    * The ::close signal is a
408    * <link linkend="keybinding-signals">keybinding signal</link>
409    * which gets emitted when the user uses a keybinding to dismiss
410    * the info bar.
411    *
412    * The default binding for this signal is the Escape key.
413    *
414    * Since: 2.18
415    */
416   signals[CLOSE] =  g_signal_new (I_("close"),
417                                   G_OBJECT_CLASS_TYPE (klass),
418                                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
419                                   G_STRUCT_OFFSET (GtkInfoBarClass, close),
420                                   NULL, NULL,
421                                   g_cclosure_marshal_VOID__VOID,
422                                   G_TYPE_NONE, 0);
423
424   /**
425    * GtkInfoBar:content-area-border:
426    *
427    * The width of the border around the content
428    * content area of the info bar.
429    *
430    * Since: 2.18
431    */
432   gtk_widget_class_install_style_property (widget_class,
433                                            g_param_spec_int ("content-area-border",
434                                                              P_("Content area border"),
435                                                              P_("Width of border around the content area"),
436                                                              0,
437                                                              G_MAXINT,
438                                                              8,
439                                                              GTK_PARAM_READABLE));
440
441   /**
442    * GtkInfoBar:content-area-spacing:
443    *
444    * The default spacing used between elements of the
445    * content area of the info bar.
446    *
447    * Since: 2.18
448    */
449   gtk_widget_class_install_style_property (widget_class,
450                                            g_param_spec_int ("content-area-spacing",
451                                                              P_("Content area spacing"),
452                                                              P_("Spacing between elements of the area"),
453                                                              0,
454                                                              G_MAXINT,
455                                                              16,
456                                                              GTK_PARAM_READABLE));
457
458   /**
459    * GtkInfoBar:button-spacing:
460    *
461    * Spacing between buttons in the action area of the info bar.
462    *
463    * Since: 2.18
464    */
465   gtk_widget_class_install_style_property (widget_class,
466                                            g_param_spec_int ("button-spacing",
467                                                              P_("Button spacing"),
468                                                              P_("Spacing between buttons"),
469                                                              0,
470                                                              G_MAXINT,
471                                                              6,
472                                                              GTK_PARAM_READABLE));
473
474   /**
475    * GtkInfoBar:action-area-border:
476    *
477    * Width of the border around the action area of the info bar.
478    *
479    * Since: 2.18
480    */
481   gtk_widget_class_install_style_property (widget_class,
482                                            g_param_spec_int ("action-area-border",
483                                                              P_("Action area border"),
484                                                              P_("Width of border around the action area"),
485                                                              0,
486                                                              G_MAXINT,
487                                                              5,
488                                                              GTK_PARAM_READABLE));
489
490   binding_set = gtk_binding_set_by_class (klass);
491
492   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
493
494   g_type_class_add_private (object_class, sizeof (GtkInfoBarPrivate));
495 }
496
497 static void
498 gtk_info_bar_style_updated (GtkWidget *widget)
499 {
500   GtkInfoBar *info_bar = GTK_INFO_BAR (widget);
501   gint button_spacing;
502   gint action_area_border;
503   gint content_area_spacing;
504   gint content_area_border;
505
506   gtk_widget_style_get (widget,
507                         "button-spacing", &button_spacing,
508                         "action-area-border", &action_area_border,
509                         "content-area-spacing", &content_area_spacing,
510                         "content-area-border", &content_area_border,
511                         NULL);
512
513   gtk_box_set_spacing (GTK_BOX (info_bar->priv->action_area), button_spacing);
514   gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->action_area),
515                                   action_area_border);
516   gtk_box_set_spacing (GTK_BOX (info_bar->priv->content_area), content_area_spacing);
517   gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->content_area),
518                                   content_area_border);
519 }
520
521 static void
522 gtk_info_bar_init (GtkInfoBar *info_bar)
523 {
524   GtkWidget *content_area;
525   GtkWidget *action_area;
526
527   gtk_widget_push_composite_child ();
528
529   info_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (info_bar,
530                                                 GTK_TYPE_INFO_BAR,
531                                                 GtkInfoBarPrivate);
532
533   content_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
534   gtk_widget_show (content_area);
535   gtk_box_pack_start (GTK_BOX (info_bar), content_area, TRUE, TRUE, 0);
536
537   action_area = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
538   gtk_widget_show (action_area);
539   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
540   gtk_box_pack_start (GTK_BOX (info_bar), action_area, FALSE, TRUE, 0);
541
542   gtk_widget_set_app_paintable (GTK_WIDGET (info_bar), TRUE);
543   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (info_bar), TRUE);
544
545   info_bar->priv->content_area = content_area;
546   info_bar->priv->action_area = action_area;
547
548   gtk_widget_pop_composite_child ();
549 }
550
551 static GtkBuildableIface *parent_buildable_iface;
552
553 static void
554 gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface)
555 {
556   parent_buildable_iface = g_type_interface_peek_parent (iface);
557   iface->get_internal_child = gtk_info_bar_buildable_get_internal_child;
558   iface->custom_tag_start = gtk_info_bar_buildable_custom_tag_start;
559   iface->custom_finished = gtk_info_bar_buildable_custom_finished;
560 }
561
562 static GObject *
563 gtk_info_bar_buildable_get_internal_child (GtkBuildable *buildable,
564                                            GtkBuilder   *builder,
565                                            const gchar  *childname)
566 {
567   if (strcmp (childname, "content_area") == 0)
568     return G_OBJECT (GTK_INFO_BAR (buildable)->priv->content_area);
569   else if (strcmp (childname, "action_area") == 0)
570     return G_OBJECT (GTK_INFO_BAR (buildable)->priv->action_area);
571
572   return parent_buildable_iface->get_internal_child (buildable,
573                                                      builder,
574                                                      childname);
575 }
576
577 static gint
578 get_response_for_widget (GtkInfoBar *info_bar,
579                          GtkWidget  *widget)
580 {
581   ResponseData *rd;
582
583   rd = get_response_data (widget, FALSE);
584   if (!rd)
585     return GTK_RESPONSE_NONE;
586   else
587     return rd->response_id;
588 }
589
590 static void
591 action_widget_activated (GtkWidget  *widget,
592                          GtkInfoBar *info_bar)
593 {
594   gint response_id;
595
596   response_id = get_response_for_widget (info_bar, widget);
597   gtk_info_bar_response (info_bar, response_id);
598 }
599
600 /**
601  * gtk_info_bar_add_action_widget:
602  * @info_bar: a #GtkInfoBar
603  * @child: an activatable widget
604  * @response_id: response ID for @child
605  *
606  * Add an activatable widget to the action area of a #GtkInfoBar,
607  * connecting a signal handler that will emit the #GtkInfoBar::response
608  * signal on the message area when the widget is activated. The widget
609  * is appended to the end of the message areas action area.
610  *
611  * Since: 2.18
612  */
613 void
614 gtk_info_bar_add_action_widget (GtkInfoBar *info_bar,
615                                 GtkWidget  *child,
616                                 gint        response_id)
617 {
618   ResponseData *ad;
619   guint signal_id;
620
621   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
622   g_return_if_fail (GTK_IS_WIDGET (child));
623
624   ad = get_response_data (child, TRUE);
625
626   ad->response_id = response_id;
627
628   if (GTK_IS_BUTTON (child))
629     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
630   else
631     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
632
633   if (signal_id)
634     {
635       GClosure *closure;
636
637       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
638                                        G_OBJECT (info_bar));
639       g_signal_connect_closure_by_id (child, signal_id, 0, closure, FALSE);
640     }
641   else
642     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkInfoBar");
643
644   gtk_box_pack_end (GTK_BOX (info_bar->priv->action_area),
645                     child, FALSE, FALSE, 0);
646   if (response_id == GTK_RESPONSE_HELP)
647     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area),
648                                         child, TRUE);
649 }
650
651 /**
652  * gtk_info_bar_get_action_area:
653  * @info_bar: a #GtkInfoBar
654  *
655  * Returns the action area of @info_bar.
656  *
657  * Returns: (transfer none): the action area
658  *
659  * Since: 2.18
660  */
661 GtkWidget*
662 gtk_info_bar_get_action_area (GtkInfoBar *info_bar)
663 {
664   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
665
666   return info_bar->priv->action_area;
667 }
668
669 /**
670  * gtk_info_bar_get_content_area:
671  * @info_bar: a #GtkInfoBar
672  *
673  * Returns the content area of @info_bar.
674  *
675  * Returns: (transfer none): the content area
676  *
677  * Since: 2.18
678  */
679 GtkWidget*
680 gtk_info_bar_get_content_area (GtkInfoBar *info_bar)
681 {
682   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
683
684   return info_bar->priv->content_area;
685 }
686
687 /**
688  * gtk_info_bar_add_button:
689  * @info_bar: a #GtkInfoBar
690  * @button_text: text of button, or stock ID
691  * @response_id: response ID for the button
692  *
693  * Adds a button with the given text (or a stock button, if button_text
694  * is a stock ID) and sets things up so that clicking the button will emit
695  * the "response" signal with the given response_id. The button is appended
696  * to the end of the info bars's action area. The button widget is
697  * returned, but usually you don't need it.
698  *
699  * Returns: (transfer none): the button widget that was added
700  *
701  * Since: 2.18
702  */
703 GtkWidget*
704 gtk_info_bar_add_button (GtkInfoBar  *info_bar,
705                          const gchar *button_text,
706                          gint         response_id)
707 {
708   GtkWidget *button;
709
710   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
711   g_return_val_if_fail (button_text != NULL, NULL);
712
713   button = gtk_button_new_from_stock (button_text);
714
715   gtk_widget_set_can_default (button, TRUE);
716
717   gtk_widget_show (button);
718
719   gtk_info_bar_add_action_widget (info_bar, button, response_id);
720
721   return button;
722 }
723
724 static void
725 add_buttons_valist (GtkInfoBar  *info_bar,
726                     const gchar *first_button_text,
727                     va_list      args)
728 {
729   const gchar* text;
730   gint response_id;
731
732   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
733
734   if (first_button_text == NULL)
735     return;
736
737   text = first_button_text;
738   response_id = va_arg (args, gint);
739
740   while (text != NULL)
741     {
742       gtk_info_bar_add_button (info_bar, text, response_id);
743
744       text = va_arg (args, gchar*);
745       if (text == NULL)
746         break;
747
748       response_id = va_arg (args, int);
749     }
750 }
751
752 /**
753  * gtk_info_bar_add_buttons:
754  * @info_bar: a #GtkInfoBar
755  * @first_button_text: button text or stock ID
756  * @...: response ID for first button, then more text-response_id pairs,
757  *     ending with %NULL
758  *
759  * Adds more buttons, same as calling gtk_info_bar_add_button()
760  * repeatedly. The variable argument list should be %NULL-terminated
761  * as with gtk_info_bar_new_with_buttons(). Each button must have both
762  * text and response ID.
763  *
764  * Since: 2.18
765  */
766 void
767 gtk_info_bar_add_buttons (GtkInfoBar  *info_bar,
768                           const gchar *first_button_text,
769                           ...)
770 {
771   va_list args;
772
773   va_start (args, first_button_text);
774   add_buttons_valist (info_bar, first_button_text, args);
775   va_end (args);
776 }
777
778 /**
779  * gtk_info_bar_new:
780  *
781  * Creates a new #GtkInfoBar object.
782  *
783  * Returns: a new #GtkInfoBar object
784  *
785  * Since: 2.18
786  */
787 GtkWidget *
788 gtk_info_bar_new (void)
789 {
790    return g_object_new (GTK_TYPE_INFO_BAR, NULL);
791 }
792
793 /**
794  * gtk_info_bar_new_with_buttons:
795  * @first_button_text: (allow-none): stock ID or text to go in first button, or %NULL
796  * @...: response ID for first button, then additional buttons, ending
797  *    with %NULL
798  *
799  * Creates a new #GtkInfoBar with buttons. Button text/response ID
800  * pairs should be listed, with a %NULL pointer ending the list.
801  * Button text can be either a stock ID such as %GTK_STOCK_OK, or
802  * some arbitrary text. A response ID can be any positive number,
803  * or one of the values in the #GtkResponseType enumeration. If the
804  * user clicks one of these dialog buttons, GtkInfoBar will emit
805  * the "response" signal with the corresponding response ID.
806  *
807  * Returns: a new #GtkInfoBar
808  */
809 GtkWidget*
810 gtk_info_bar_new_with_buttons (const gchar *first_button_text,
811                                ...)
812 {
813   GtkInfoBar *info_bar;
814   va_list args;
815
816   info_bar = GTK_INFO_BAR (gtk_info_bar_new ());
817
818   va_start (args, first_button_text);
819   add_buttons_valist (info_bar, first_button_text, args);
820   va_end (args);
821
822   return GTK_WIDGET (info_bar);
823 }
824
825 /**
826  * gtk_info_bar_set_response_sensitive:
827  * @info_bar: a #GtkInfoBar
828  * @response_id: a response ID
829  * @setting: TRUE for sensitive
830  *
831  * Calls gtk_widget_set_sensitive (widget, setting) for each
832  * widget in the info bars's action area with the given response_id.
833  * A convenient way to sensitize/desensitize dialog buttons.
834  *
835  * Since: 2.18
836  */
837 void
838 gtk_info_bar_set_response_sensitive (GtkInfoBar *info_bar,
839                                      gint        response_id,
840                                      gboolean    setting)
841 {
842   GList *children, *list;
843
844   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
845
846   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
847
848   for (list = children; list; list = list->next)
849     {
850       GtkWidget *widget = list->data;
851       ResponseData *rd = get_response_data (widget, FALSE);
852
853       if (rd && rd->response_id == response_id)
854         gtk_widget_set_sensitive (widget, setting);
855     }
856
857   g_list_free (children);
858 }
859
860 /**
861  * gtk_info_bar_set_default_response:
862  * @info_bar: a #GtkInfoBar
863  * @response_id: a response ID
864  *
865  * Sets the last widget in the info bar's action area with
866  * the given response_id as the default widget for the dialog.
867  * Pressing "Enter" normally activates the default widget.
868  *
869  * Note that this function currently requires @info_bar to
870  * be added to a widget hierarchy. 
871  *
872  * Since: 2.18
873  */
874 void
875 gtk_info_bar_set_default_response (GtkInfoBar *info_bar,
876                                    gint        response_id)
877 {
878   GList *children, *list;
879
880   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
881
882   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
883
884   for (list = children; list; list = list->next)
885     {
886       GtkWidget *widget = list->data;
887       ResponseData *rd = get_response_data (widget, FALSE);
888
889       if (rd && rd->response_id == response_id)
890         gtk_widget_grab_default (widget);
891     }
892
893   g_list_free (children);
894 }
895
896 /**
897  * gtk_info_bar_response:
898  * @info_bar: a #GtkInfoBar
899  * @response_id: a response ID
900  *
901  * Emits the 'response' signal with the given @response_id.
902  *
903  * Since: 2.18
904  */
905 void
906 gtk_info_bar_response (GtkInfoBar *info_bar,
907                        gint        response_id)
908 {
909   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
910
911   g_signal_emit (info_bar, signals[RESPONSE], 0, response_id);
912 }
913
914 typedef struct
915 {
916   gchar *widget_name;
917   gchar *response_id;
918 } ActionWidgetInfo;
919
920 typedef struct
921 {
922   GtkInfoBar *info_bar;
923   GtkBuilder *builder;
924   GSList *items;
925   gchar *response;
926 } ActionWidgetsSubParserData;
927
928 static void
929 attributes_start_element (GMarkupParseContext  *context,
930                           const gchar          *element_name,
931                           const gchar         **names,
932                           const gchar         **values,
933                           gpointer              user_data,
934                           GError              **error)
935 {
936   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
937   guint i;
938
939   if (strcmp (element_name, "action-widget") == 0)
940     {
941       for (i = 0; names[i]; i++)
942         if (strcmp (names[i], "response") == 0)
943           parser_data->response = g_strdup (values[i]);
944     }
945   else if (strcmp (element_name, "action-widgets") == 0)
946     return;
947   else
948     g_warning ("Unsupported tag for GtkInfoBar: %s\n", element_name);
949 }
950
951 static void
952 attributes_text_element (GMarkupParseContext  *context,
953                          const gchar          *text,
954                          gsize                 text_len,
955                          gpointer              user_data,
956                          GError              **error)
957 {
958   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
959   ActionWidgetInfo *item;
960
961   if (!parser_data->response)
962     return;
963
964   item = g_new (ActionWidgetInfo, 1);
965   item->widget_name = g_strndup (text, text_len);
966   item->response_id = parser_data->response;
967   parser_data->items = g_slist_prepend (parser_data->items, item);
968   parser_data->response = NULL;
969 }
970
971 static const GMarkupParser attributes_parser =
972 {
973   attributes_start_element,
974   NULL,
975   attributes_text_element,
976 };
977
978 gboolean
979 gtk_info_bar_buildable_custom_tag_start (GtkBuildable  *buildable,
980                                          GtkBuilder    *builder,
981                                          GObject       *child,
982                                          const gchar   *tagname,
983                                          GMarkupParser *parser,
984                                          gpointer      *data)
985 {
986   ActionWidgetsSubParserData *parser_data;
987
988   if (child)
989     return FALSE;
990
991   if (strcmp (tagname, "action-widgets") == 0)
992     {
993       parser_data = g_slice_new0 (ActionWidgetsSubParserData);
994       parser_data->info_bar = GTK_INFO_BAR (buildable);
995       parser_data->items = NULL;
996
997       *parser = attributes_parser;
998       *data = parser_data;
999       return TRUE;
1000     }
1001
1002   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
1003                                                    tagname, parser, data);
1004 }
1005
1006 static void
1007 gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable,
1008                                         GtkBuilder   *builder,
1009                                         GObject      *child,
1010                                         const gchar  *tagname,
1011                                         gpointer      user_data)
1012 {
1013   GSList *l;
1014   ActionWidgetsSubParserData *parser_data;
1015   GObject *object;
1016   GtkInfoBar *info_bar;
1017   ResponseData *ad;
1018   guint signal_id;
1019
1020   if (strcmp (tagname, "action-widgets"))
1021     {
1022       parent_buildable_iface->custom_finished (buildable, builder, child,
1023                                                tagname, user_data);
1024       return;
1025     }
1026
1027   info_bar = GTK_INFO_BAR (buildable);
1028   parser_data = (ActionWidgetsSubParserData*)user_data;
1029   parser_data->items = g_slist_reverse (parser_data->items);
1030
1031   for (l = parser_data->items; l; l = l->next)
1032     {
1033       ActionWidgetInfo *item = l->data;
1034
1035       object = gtk_builder_get_object (builder, item->widget_name);
1036       if (!object)
1037         {
1038           g_warning ("Unknown object %s specified in action-widgets of %s",
1039                      item->widget_name,
1040                      gtk_buildable_get_name (GTK_BUILDABLE (buildable)));
1041           continue;
1042         }
1043
1044       ad = get_response_data (GTK_WIDGET (object), TRUE);
1045       ad->response_id = atoi (item->response_id);
1046
1047       if (GTK_IS_BUTTON (object))
1048         signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
1049       else
1050         signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal;
1051
1052       if (signal_id)
1053         {
1054           GClosure *closure;
1055
1056           closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1057                                            G_OBJECT (info_bar));
1058           g_signal_connect_closure_by_id (object,
1059                                           signal_id,
1060                                           0,
1061                                           closure,
1062                                           FALSE);
1063         }
1064
1065       if (ad->response_id == GTK_RESPONSE_HELP)
1066         gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area),
1067                                             GTK_WIDGET (object), TRUE);
1068
1069       g_free (item->widget_name);
1070       g_free (item->response_id);
1071       g_free (item);
1072     }
1073   g_slist_free (parser_data->items);
1074   g_slice_free (ActionWidgetsSubParserData, parser_data);
1075 }
1076
1077 /**
1078  * gtk_info_bar_set_message_type:
1079  * @info_bar: a #GtkInfoBar
1080  * @message_type: a #GtkMessageType
1081  *
1082  * Sets the message type of the message area.
1083  * GTK+ uses this type to determine what color to use
1084  * when drawing the message area.
1085  *
1086  * Since: 2.18
1087  */
1088 void
1089 gtk_info_bar_set_message_type (GtkInfoBar     *info_bar,
1090                                GtkMessageType  message_type)
1091 {
1092   GtkInfoBarPrivate *priv;
1093   AtkObject *atk_obj;
1094
1095   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
1096
1097   priv = info_bar->priv;
1098
1099   if (priv->message_type != message_type)
1100     {
1101       priv->message_type = message_type;
1102
1103       gtk_widget_queue_draw (GTK_WIDGET (info_bar));
1104
1105       atk_obj = gtk_widget_get_accessible (GTK_WIDGET (info_bar));
1106       if (GTK_IS_ACCESSIBLE (atk_obj))
1107         {
1108           GtkStockItem item;
1109           const char *stock_id = NULL;
1110
1111           atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
1112
1113           switch (message_type)
1114             {
1115             case GTK_MESSAGE_INFO:
1116               stock_id = GTK_STOCK_DIALOG_INFO;
1117               break;
1118
1119             case GTK_MESSAGE_QUESTION:
1120               stock_id = GTK_STOCK_DIALOG_QUESTION;
1121               break;
1122
1123             case GTK_MESSAGE_WARNING:
1124               stock_id = GTK_STOCK_DIALOG_WARNING;
1125               break;
1126
1127             case GTK_MESSAGE_ERROR:
1128               stock_id = GTK_STOCK_DIALOG_ERROR;
1129               break;
1130
1131             case GTK_MESSAGE_OTHER:
1132               break;
1133
1134             default:
1135               g_warning ("Unknown GtkMessageType %u", message_type);
1136               break;
1137             }
1138
1139           if (stock_id)
1140             {
1141               gtk_stock_lookup (stock_id, &item);
1142               atk_object_set_name (atk_obj, item.label);
1143             }
1144         }
1145
1146       g_object_notify (G_OBJECT (info_bar), "message-type");
1147     }
1148 }
1149
1150 /**
1151  * gtk_info_bar_get_message_type:
1152  * @info_bar: a #GtkInfoBar
1153  *
1154  * Returns the message type of the message area.
1155  *
1156  * Returns: the message type of the message area.
1157  *
1158  * Since: 2.18
1159  */
1160 GtkMessageType
1161 gtk_info_bar_get_message_type (GtkInfoBar *info_bar)
1162 {
1163   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), GTK_MESSAGE_OTHER);
1164
1165   return info_bar->priv->message_type;
1166 }