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