]> Pileus Git - ~andy/gtk/blob - gtk/gtkinfobar.c
Merge branch 'master' into broadway
[~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 = GTK_INFO_BAR (object);
191
192   switch (prop_id)
193     {
194     case PROP_MESSAGE_TYPE:
195       gtk_info_bar_set_message_type (info_bar, g_value_get_enum (value));
196       break;
197     default:
198       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
199       break;
200     }
201 }
202
203 static void
204 gtk_info_bar_get_property (GObject    *object,
205                            guint       prop_id,
206                            GValue     *value,
207                            GParamSpec *pspec)
208 {
209   GtkInfoBar *info_bar = GTK_INFO_BAR (object);
210
211   switch (prop_id)
212     {
213     case PROP_MESSAGE_TYPE:
214       g_value_set_enum (value, gtk_info_bar_get_message_type (info_bar));
215       break;
216     default:
217       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
218       break;
219     }
220 }
221
222 static void
223 gtk_info_bar_finalize (GObject *object)
224 {
225   G_OBJECT_CLASS (gtk_info_bar_parent_class)->finalize (object);
226 }
227
228 static void
229 response_data_free (gpointer data)
230 {
231   g_slice_free (ResponseData, data);
232 }
233
234 static ResponseData *
235 get_response_data (GtkWidget *widget,
236                    gboolean   create)
237 {
238   ResponseData *ad = g_object_get_data (G_OBJECT (widget),
239                                         "gtk-info-bar-response-data");
240
241   if (ad == NULL && create)
242     {
243       ad = g_slice_new (ResponseData);
244
245       g_object_set_data_full (G_OBJECT (widget),
246                               I_("gtk-info-bar-response-data"),
247                               ad,
248                               response_data_free);
249     }
250
251   return ad;
252 }
253
254 static GtkWidget *
255 find_button (GtkInfoBar *info_bar,
256              gint        response_id)
257 {
258   GList *children, *list;
259   GtkWidget *child = NULL;
260
261   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
262
263   for (list = children; list; list = list->next)
264     {
265       ResponseData *rd = get_response_data (list->data, FALSE);
266
267       if (rd && rd->response_id == response_id)
268         {
269           child = list->data;
270           break;
271         }
272     }
273
274   g_list_free (children);
275
276   return child;
277 }
278
279 static void
280 gtk_info_bar_close (GtkInfoBar *info_bar)
281 {
282   if (!find_button (info_bar, GTK_RESPONSE_CANCEL))
283     return;
284
285   gtk_info_bar_response (GTK_INFO_BAR (info_bar),
286                          GTK_RESPONSE_CANCEL);
287 }
288
289 static gboolean
290 gtk_info_bar_draw (GtkWidget      *widget,
291                    cairo_t        *cr)
292 {
293   GtkInfoBarPrivate *priv = GTK_INFO_BAR (widget)->priv;
294   const char* type_class[] = {
295     GTK_STYLE_CLASS_INFO,
296     GTK_STYLE_CLASS_WARNING,
297     GTK_STYLE_CLASS_QUESTION,
298     GTK_STYLE_CLASS_ERROR,
299     NULL
300   };
301
302   if (priv->message_type != GTK_MESSAGE_OTHER)
303     {
304       GtkStyleContext *context;
305
306       context = gtk_widget_get_style_context (widget);
307
308       gtk_style_context_save (context);
309
310       if (type_class[priv->message_type])
311         gtk_style_context_add_class (context,
312                                      type_class[priv->message_type]);
313
314       gtk_render_background (context, cr, 0, 0,
315                              gtk_widget_get_allocated_width (widget),
316                              gtk_widget_get_allocated_height (widget));
317       gtk_render_frame (context, cr, 0, 0,
318                         gtk_widget_get_allocated_width (widget),
319                         gtk_widget_get_allocated_height (widget));
320
321       gtk_style_context_restore (context);
322     }
323
324   if (GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->draw)
325     GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->draw (widget, cr);
326
327   return FALSE;
328 }
329
330 static void
331 gtk_info_bar_class_init (GtkInfoBarClass *klass)
332 {
333   GtkWidgetClass *widget_class;
334   GObjectClass *object_class;
335   GtkBindingSet *binding_set;
336
337   widget_class = GTK_WIDGET_CLASS (klass);
338   object_class = G_OBJECT_CLASS (klass);
339
340   object_class->get_property = gtk_info_bar_get_property;
341   object_class->set_property = gtk_info_bar_set_property;
342   object_class->finalize = gtk_info_bar_finalize;
343
344   widget_class->style_updated = gtk_info_bar_style_updated;
345   widget_class->draw = gtk_info_bar_draw;
346
347   klass->close = gtk_info_bar_close;
348
349   /**
350    * GtkInfoBar:message-type:
351    *
352    * The type of the message.
353    *
354    * The type is used to determine the colors to use in the info bar.
355    * The following symbolic color names can by used to customize
356    * these colors:
357    * "info_fg_color", "info_bg_color",
358    * "warning_fg_color", "warning_bg_color",
359    * "question_fg_color", "question_bg_color",
360    * "error_fg_color", "error_bg_color".
361    * "other_fg_color", "other_bg_color".
362    *
363    * If the type is #GTK_MESSAGE_OTHER, no info bar is painted but the
364    * colors are still set.
365    *
366    * Since: 2.18
367    */
368   g_object_class_install_property (object_class,
369                                    PROP_MESSAGE_TYPE,
370                                    g_param_spec_enum ("message-type",
371                                                       P_("Message Type"),
372                                                       P_("The type of message"),
373                                                       GTK_TYPE_MESSAGE_TYPE,
374                                                       GTK_MESSAGE_INFO,
375                                                       GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
376   /**
377    * GtkInfoBar::response:
378    * @info_bar: the object on which the signal is emitted
379    * @response_id: the response ID
380    *
381    * Emitted when an action widget is clicked or the application programmer
382    * calls gtk_dialog_response(). The @response_id depends on which action
383    * widget was clicked.
384    *
385    * Since: 2.18
386    */
387   signals[RESPONSE] = g_signal_new (I_("response"),
388                                     G_OBJECT_CLASS_TYPE (klass),
389                                     G_SIGNAL_RUN_LAST,
390                                     G_STRUCT_OFFSET (GtkInfoBarClass, response),
391                                     NULL, NULL,
392                                     g_cclosure_marshal_VOID__INT,
393                                     G_TYPE_NONE, 1,
394                                     G_TYPE_INT);
395
396   /**
397    * GtkInfoBar::close:
398    *
399    * The ::close signal is a
400    * <link linkend="keybinding-signals">keybinding signal</link>
401    * which gets emitted when the user uses a keybinding to dismiss
402    * the info bar.
403    *
404    * The default binding for this signal is the Escape key.
405    *
406    * Since: 2.18
407    */
408   signals[CLOSE] =  g_signal_new (I_("close"),
409                                   G_OBJECT_CLASS_TYPE (klass),
410                                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
411                                   G_STRUCT_OFFSET (GtkInfoBarClass, close),
412                                   NULL, NULL,
413                                   g_cclosure_marshal_VOID__VOID,
414                                   G_TYPE_NONE, 0);
415
416   /**
417    * GtkInfoBar:content-area-border:
418    *
419    * The width of the border around the content
420    * content area of the info bar.
421    *
422    * Since: 2.18
423    */
424   gtk_widget_class_install_style_property (widget_class,
425                                            g_param_spec_int ("content-area-border",
426                                                              P_("Content area border"),
427                                                              P_("Width of border around the content area"),
428                                                              0,
429                                                              G_MAXINT,
430                                                              8,
431                                                              GTK_PARAM_READABLE));
432
433   /**
434    * GtkInfoBar:content-area-spacing:
435    *
436    * The default spacing used between elements of the
437    * content area of the info bar.
438    *
439    * Since: 2.18
440    */
441   gtk_widget_class_install_style_property (widget_class,
442                                            g_param_spec_int ("content-area-spacing",
443                                                              P_("Content area spacing"),
444                                                              P_("Spacing between elements of the area"),
445                                                              0,
446                                                              G_MAXINT,
447                                                              16,
448                                                              GTK_PARAM_READABLE));
449
450   /**
451    * GtkInfoBar:button-spacing:
452    *
453    * Spacing between buttons in the action area of the info bar.
454    *
455    * Since: 2.18
456    */
457   gtk_widget_class_install_style_property (widget_class,
458                                            g_param_spec_int ("button-spacing",
459                                                              P_("Button spacing"),
460                                                              P_("Spacing between buttons"),
461                                                              0,
462                                                              G_MAXINT,
463                                                              6,
464                                                              GTK_PARAM_READABLE));
465
466   /**
467    * GtkInfoBar:action-area-border:
468    *
469    * Width of the border around the action area of the info bar.
470    *
471    * Since: 2.18
472    */
473   gtk_widget_class_install_style_property (widget_class,
474                                            g_param_spec_int ("action-area-border",
475                                                              P_("Action area border"),
476                                                              P_("Width of border around the action area"),
477                                                              0,
478                                                              G_MAXINT,
479                                                              5,
480                                                              GTK_PARAM_READABLE));
481
482   binding_set = gtk_binding_set_by_class (klass);
483
484   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
485
486   g_type_class_add_private (object_class, sizeof (GtkInfoBarPrivate));
487 }
488
489 static void
490 gtk_info_bar_style_updated (GtkWidget *widget)
491 {
492   GtkInfoBar *info_bar = GTK_INFO_BAR (widget);
493   gint button_spacing;
494   gint action_area_border;
495   gint content_area_spacing;
496   gint content_area_border;
497
498   GTK_WIDGET_CLASS (gtk_info_bar_parent_class)->style_updated (widget);
499
500   gtk_widget_style_get (widget,
501                         "button-spacing", &button_spacing,
502                         "action-area-border", &action_area_border,
503                         "content-area-spacing", &content_area_spacing,
504                         "content-area-border", &content_area_border,
505                         NULL);
506
507   gtk_box_set_spacing (GTK_BOX (info_bar->priv->action_area), button_spacing);
508   gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->action_area),
509                                   action_area_border);
510   gtk_box_set_spacing (GTK_BOX (info_bar->priv->content_area), content_area_spacing);
511   gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->content_area),
512                                   content_area_border);
513 }
514
515 static void
516 gtk_info_bar_init (GtkInfoBar *info_bar)
517 {
518   GtkWidget *content_area;
519   GtkWidget *action_area;
520
521   gtk_widget_push_composite_child ();
522
523   info_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (info_bar,
524                                                 GTK_TYPE_INFO_BAR,
525                                                 GtkInfoBarPrivate);
526
527   content_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
528   gtk_widget_show (content_area);
529   gtk_box_pack_start (GTK_BOX (info_bar), content_area, TRUE, TRUE, 0);
530
531   action_area = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
532   gtk_widget_show (action_area);
533   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
534   gtk_box_pack_start (GTK_BOX (info_bar), action_area, FALSE, TRUE, 0);
535
536   gtk_widget_set_app_paintable (GTK_WIDGET (info_bar), TRUE);
537   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (info_bar), TRUE);
538
539   info_bar->priv->content_area = content_area;
540   info_bar->priv->action_area = action_area;
541
542   gtk_widget_pop_composite_child ();
543 }
544
545 static GtkBuildableIface *parent_buildable_iface;
546
547 static void
548 gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface)
549 {
550   parent_buildable_iface = g_type_interface_peek_parent (iface);
551   iface->get_internal_child = gtk_info_bar_buildable_get_internal_child;
552   iface->custom_tag_start = gtk_info_bar_buildable_custom_tag_start;
553   iface->custom_finished = gtk_info_bar_buildable_custom_finished;
554 }
555
556 static GObject *
557 gtk_info_bar_buildable_get_internal_child (GtkBuildable *buildable,
558                                            GtkBuilder   *builder,
559                                            const gchar  *childname)
560 {
561   if (strcmp (childname, "content_area") == 0)
562     return G_OBJECT (GTK_INFO_BAR (buildable)->priv->content_area);
563   else if (strcmp (childname, "action_area") == 0)
564     return G_OBJECT (GTK_INFO_BAR (buildable)->priv->action_area);
565
566   return parent_buildable_iface->get_internal_child (buildable,
567                                                      builder,
568                                                      childname);
569 }
570
571 static gint
572 get_response_for_widget (GtkInfoBar *info_bar,
573                          GtkWidget  *widget)
574 {
575   ResponseData *rd;
576
577   rd = get_response_data (widget, FALSE);
578   if (!rd)
579     return GTK_RESPONSE_NONE;
580   else
581     return rd->response_id;
582 }
583
584 static void
585 action_widget_activated (GtkWidget  *widget,
586                          GtkInfoBar *info_bar)
587 {
588   gint response_id;
589
590   response_id = get_response_for_widget (info_bar, widget);
591   gtk_info_bar_response (info_bar, response_id);
592 }
593
594 /**
595  * gtk_info_bar_add_action_widget:
596  * @info_bar: a #GtkInfoBar
597  * @child: an activatable widget
598  * @response_id: response ID for @child
599  *
600  * Add an activatable widget to the action area of a #GtkInfoBar,
601  * connecting a signal handler that will emit the #GtkInfoBar::response
602  * signal on the message area when the widget is activated. The widget
603  * is appended to the end of the message areas action area.
604  *
605  * Since: 2.18
606  */
607 void
608 gtk_info_bar_add_action_widget (GtkInfoBar *info_bar,
609                                 GtkWidget  *child,
610                                 gint        response_id)
611 {
612   ResponseData *ad;
613   guint signal_id;
614
615   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
616   g_return_if_fail (GTK_IS_WIDGET (child));
617
618   ad = get_response_data (child, TRUE);
619
620   ad->response_id = response_id;
621
622   if (GTK_IS_BUTTON (child))
623     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
624   else
625     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
626
627   if (signal_id)
628     {
629       GClosure *closure;
630
631       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
632                                        G_OBJECT (info_bar));
633       g_signal_connect_closure_by_id (child, signal_id, 0, closure, FALSE);
634     }
635   else
636     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkInfoBar");
637
638   gtk_box_pack_end (GTK_BOX (info_bar->priv->action_area),
639                     child, FALSE, FALSE, 0);
640   if (response_id == GTK_RESPONSE_HELP)
641     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area),
642                                         child, TRUE);
643 }
644
645 /**
646  * gtk_info_bar_get_action_area:
647  * @info_bar: a #GtkInfoBar
648  *
649  * Returns the action area of @info_bar.
650  *
651  * Returns: (transfer none): the action area
652  *
653  * Since: 2.18
654  */
655 GtkWidget*
656 gtk_info_bar_get_action_area (GtkInfoBar *info_bar)
657 {
658   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
659
660   return info_bar->priv->action_area;
661 }
662
663 /**
664  * gtk_info_bar_get_content_area:
665  * @info_bar: a #GtkInfoBar
666  *
667  * Returns the content area of @info_bar.
668  *
669  * Returns: (transfer none): the content area
670  *
671  * Since: 2.18
672  */
673 GtkWidget*
674 gtk_info_bar_get_content_area (GtkInfoBar *info_bar)
675 {
676   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
677
678   return info_bar->priv->content_area;
679 }
680
681 /**
682  * gtk_info_bar_add_button:
683  * @info_bar: a #GtkInfoBar
684  * @button_text: text of button, or stock ID
685  * @response_id: response ID for the button
686  *
687  * Adds a button with the given text (or a stock button, if button_text
688  * is a stock ID) and sets things up so that clicking the button will emit
689  * the "response" signal with the given response_id. The button is appended
690  * to the end of the info bars's action area. The button widget is
691  * returned, but usually you don't need it.
692  *
693  * Returns: (transfer none): the #GtkButton widget that was added
694  *
695  * Since: 2.18
696  */
697 GtkWidget*
698 gtk_info_bar_add_button (GtkInfoBar  *info_bar,
699                          const gchar *button_text,
700                          gint         response_id)
701 {
702   GtkWidget *button;
703
704   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
705   g_return_val_if_fail (button_text != NULL, NULL);
706
707   button = gtk_button_new_from_stock (button_text);
708
709   gtk_widget_set_can_default (button, TRUE);
710
711   gtk_widget_show (button);
712
713   gtk_info_bar_add_action_widget (info_bar, button, response_id);
714
715   return button;
716 }
717
718 static void
719 add_buttons_valist (GtkInfoBar  *info_bar,
720                     const gchar *first_button_text,
721                     va_list      args)
722 {
723   const gchar* text;
724   gint response_id;
725
726   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
727
728   if (first_button_text == NULL)
729     return;
730
731   text = first_button_text;
732   response_id = va_arg (args, gint);
733
734   while (text != NULL)
735     {
736       gtk_info_bar_add_button (info_bar, text, response_id);
737
738       text = va_arg (args, gchar*);
739       if (text == NULL)
740         break;
741
742       response_id = va_arg (args, int);
743     }
744 }
745
746 /**
747  * gtk_info_bar_add_buttons:
748  * @info_bar: a #GtkInfoBar
749  * @first_button_text: button text or stock ID
750  * @...: response ID for first button, then more text-response_id pairs,
751  *     ending with %NULL
752  *
753  * Adds more buttons, same as calling gtk_info_bar_add_button()
754  * repeatedly. The variable argument list should be %NULL-terminated
755  * as with gtk_info_bar_new_with_buttons(). Each button must have both
756  * text and response ID.
757  *
758  * Since: 2.18
759  */
760 void
761 gtk_info_bar_add_buttons (GtkInfoBar  *info_bar,
762                           const gchar *first_button_text,
763                           ...)
764 {
765   va_list args;
766
767   va_start (args, first_button_text);
768   add_buttons_valist (info_bar, first_button_text, args);
769   va_end (args);
770 }
771
772 /**
773  * gtk_info_bar_new:
774  *
775  * Creates a new #GtkInfoBar object.
776  *
777  * Returns: a new #GtkInfoBar object
778  *
779  * Since: 2.18
780  */
781 GtkWidget *
782 gtk_info_bar_new (void)
783 {
784    return g_object_new (GTK_TYPE_INFO_BAR, NULL);
785 }
786
787 /**
788  * gtk_info_bar_new_with_buttons:
789  * @first_button_text: (allow-none): stock ID or text to go in first button, or %NULL
790  * @...: response ID for first button, then additional buttons, ending
791  *    with %NULL
792  *
793  * Creates a new #GtkInfoBar with buttons. Button text/response ID
794  * pairs should be listed, with a %NULL pointer ending the list.
795  * Button text can be either a stock ID such as %GTK_STOCK_OK, or
796  * some arbitrary text. A response ID can be any positive number,
797  * or one of the values in the #GtkResponseType enumeration. If the
798  * user clicks one of these dialog buttons, GtkInfoBar will emit
799  * the "response" signal with the corresponding response ID.
800  *
801  * Returns: a new #GtkInfoBar
802  */
803 GtkWidget*
804 gtk_info_bar_new_with_buttons (const gchar *first_button_text,
805                                ...)
806 {
807   GtkInfoBar *info_bar;
808   va_list args;
809
810   info_bar = GTK_INFO_BAR (gtk_info_bar_new ());
811
812   va_start (args, first_button_text);
813   add_buttons_valist (info_bar, first_button_text, args);
814   va_end (args);
815
816   return GTK_WIDGET (info_bar);
817 }
818
819 /**
820  * gtk_info_bar_set_response_sensitive:
821  * @info_bar: a #GtkInfoBar
822  * @response_id: a response ID
823  * @setting: TRUE for sensitive
824  *
825  * Calls gtk_widget_set_sensitive (widget, setting) for each
826  * widget in the info bars's action area with the given response_id.
827  * A convenient way to sensitize/desensitize dialog buttons.
828  *
829  * Since: 2.18
830  */
831 void
832 gtk_info_bar_set_response_sensitive (GtkInfoBar *info_bar,
833                                      gint        response_id,
834                                      gboolean    setting)
835 {
836   GList *children, *list;
837
838   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
839
840   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
841
842   for (list = children; list; list = list->next)
843     {
844       GtkWidget *widget = list->data;
845       ResponseData *rd = get_response_data (widget, FALSE);
846
847       if (rd && rd->response_id == response_id)
848         gtk_widget_set_sensitive (widget, setting);
849     }
850
851   g_list_free (children);
852 }
853
854 /**
855  * gtk_info_bar_set_default_response:
856  * @info_bar: a #GtkInfoBar
857  * @response_id: a response ID
858  *
859  * Sets the last widget in the info bar's action area with
860  * the given response_id as the default widget for the dialog.
861  * Pressing "Enter" normally activates the default widget.
862  *
863  * Note that this function currently requires @info_bar to
864  * be added to a widget hierarchy. 
865  *
866  * Since: 2.18
867  */
868 void
869 gtk_info_bar_set_default_response (GtkInfoBar *info_bar,
870                                    gint        response_id)
871 {
872   GList *children, *list;
873
874   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
875
876   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
877
878   for (list = children; list; list = list->next)
879     {
880       GtkWidget *widget = list->data;
881       ResponseData *rd = get_response_data (widget, FALSE);
882
883       if (rd && rd->response_id == response_id)
884         gtk_widget_grab_default (widget);
885     }
886
887   g_list_free (children);
888 }
889
890 /**
891  * gtk_info_bar_response:
892  * @info_bar: a #GtkInfoBar
893  * @response_id: a response ID
894  *
895  * Emits the 'response' signal with the given @response_id.
896  *
897  * Since: 2.18
898  */
899 void
900 gtk_info_bar_response (GtkInfoBar *info_bar,
901                        gint        response_id)
902 {
903   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
904
905   g_signal_emit (info_bar, signals[RESPONSE], 0, response_id);
906 }
907
908 typedef struct
909 {
910   gchar *widget_name;
911   gchar *response_id;
912 } ActionWidgetInfo;
913
914 typedef struct
915 {
916   GtkInfoBar *info_bar;
917   GtkBuilder *builder;
918   GSList *items;
919   gchar *response;
920 } ActionWidgetsSubParserData;
921
922 static void
923 attributes_start_element (GMarkupParseContext  *context,
924                           const gchar          *element_name,
925                           const gchar         **names,
926                           const gchar         **values,
927                           gpointer              user_data,
928                           GError              **error)
929 {
930   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
931   guint i;
932
933   if (strcmp (element_name, "action-widget") == 0)
934     {
935       for (i = 0; names[i]; i++)
936         if (strcmp (names[i], "response") == 0)
937           parser_data->response = g_strdup (values[i]);
938     }
939   else if (strcmp (element_name, "action-widgets") == 0)
940     return;
941   else
942     g_warning ("Unsupported tag for GtkInfoBar: %s\n", element_name);
943 }
944
945 static void
946 attributes_text_element (GMarkupParseContext  *context,
947                          const gchar          *text,
948                          gsize                 text_len,
949                          gpointer              user_data,
950                          GError              **error)
951 {
952   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
953   ActionWidgetInfo *item;
954
955   if (!parser_data->response)
956     return;
957
958   item = g_new (ActionWidgetInfo, 1);
959   item->widget_name = g_strndup (text, text_len);
960   item->response_id = parser_data->response;
961   parser_data->items = g_slist_prepend (parser_data->items, item);
962   parser_data->response = NULL;
963 }
964
965 static const GMarkupParser attributes_parser =
966 {
967   attributes_start_element,
968   NULL,
969   attributes_text_element,
970 };
971
972 gboolean
973 gtk_info_bar_buildable_custom_tag_start (GtkBuildable  *buildable,
974                                          GtkBuilder    *builder,
975                                          GObject       *child,
976                                          const gchar   *tagname,
977                                          GMarkupParser *parser,
978                                          gpointer      *data)
979 {
980   ActionWidgetsSubParserData *parser_data;
981
982   if (child)
983     return FALSE;
984
985   if (strcmp (tagname, "action-widgets") == 0)
986     {
987       parser_data = g_slice_new0 (ActionWidgetsSubParserData);
988       parser_data->info_bar = GTK_INFO_BAR (buildable);
989       parser_data->items = NULL;
990
991       *parser = attributes_parser;
992       *data = parser_data;
993       return TRUE;
994     }
995
996   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
997                                                    tagname, parser, data);
998 }
999
1000 static void
1001 gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable,
1002                                         GtkBuilder   *builder,
1003                                         GObject      *child,
1004                                         const gchar  *tagname,
1005                                         gpointer      user_data)
1006 {
1007   GSList *l;
1008   ActionWidgetsSubParserData *parser_data;
1009   GObject *object;
1010   GtkInfoBar *info_bar;
1011   ResponseData *ad;
1012   guint signal_id;
1013
1014   if (strcmp (tagname, "action-widgets"))
1015     {
1016       parent_buildable_iface->custom_finished (buildable, builder, child,
1017                                                tagname, user_data);
1018       return;
1019     }
1020
1021   info_bar = GTK_INFO_BAR (buildable);
1022   parser_data = (ActionWidgetsSubParserData*)user_data;
1023   parser_data->items = g_slist_reverse (parser_data->items);
1024
1025   for (l = parser_data->items; l; l = l->next)
1026     {
1027       ActionWidgetInfo *item = l->data;
1028
1029       object = gtk_builder_get_object (builder, item->widget_name);
1030       if (!object)
1031         {
1032           g_warning ("Unknown object %s specified in action-widgets of %s",
1033                      item->widget_name,
1034                      gtk_buildable_get_name (GTK_BUILDABLE (buildable)));
1035           continue;
1036         }
1037
1038       ad = get_response_data (GTK_WIDGET (object), TRUE);
1039       ad->response_id = atoi (item->response_id);
1040
1041       if (GTK_IS_BUTTON (object))
1042         signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
1043       else
1044         signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal;
1045
1046       if (signal_id)
1047         {
1048           GClosure *closure;
1049
1050           closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1051                                            G_OBJECT (info_bar));
1052           g_signal_connect_closure_by_id (object,
1053                                           signal_id,
1054                                           0,
1055                                           closure,
1056                                           FALSE);
1057         }
1058
1059       if (ad->response_id == GTK_RESPONSE_HELP)
1060         gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area),
1061                                             GTK_WIDGET (object), TRUE);
1062
1063       g_free (item->widget_name);
1064       g_free (item->response_id);
1065       g_free (item);
1066     }
1067   g_slist_free (parser_data->items);
1068   g_slice_free (ActionWidgetsSubParserData, parser_data);
1069 }
1070
1071 /**
1072  * gtk_info_bar_set_message_type:
1073  * @info_bar: a #GtkInfoBar
1074  * @message_type: a #GtkMessageType
1075  *
1076  * Sets the message type of the message area.
1077  * GTK+ uses this type to determine what color to use
1078  * when drawing the message area.
1079  *
1080  * Since: 2.18
1081  */
1082 void
1083 gtk_info_bar_set_message_type (GtkInfoBar     *info_bar,
1084                                GtkMessageType  message_type)
1085 {
1086   GtkInfoBarPrivate *priv;
1087   AtkObject *atk_obj;
1088
1089   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
1090
1091   priv = info_bar->priv;
1092
1093   if (priv->message_type != message_type)
1094     {
1095       priv->message_type = message_type;
1096
1097       gtk_widget_queue_draw (GTK_WIDGET (info_bar));
1098
1099       atk_obj = gtk_widget_get_accessible (GTK_WIDGET (info_bar));
1100       if (GTK_IS_ACCESSIBLE (atk_obj))
1101         {
1102           GtkStockItem item;
1103           const char *stock_id = NULL;
1104
1105           atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
1106
1107           switch (message_type)
1108             {
1109             case GTK_MESSAGE_INFO:
1110               stock_id = GTK_STOCK_DIALOG_INFO;
1111               break;
1112
1113             case GTK_MESSAGE_QUESTION:
1114               stock_id = GTK_STOCK_DIALOG_QUESTION;
1115               break;
1116
1117             case GTK_MESSAGE_WARNING:
1118               stock_id = GTK_STOCK_DIALOG_WARNING;
1119               break;
1120
1121             case GTK_MESSAGE_ERROR:
1122               stock_id = GTK_STOCK_DIALOG_ERROR;
1123               break;
1124
1125             case GTK_MESSAGE_OTHER:
1126               break;
1127
1128             default:
1129               g_warning ("Unknown GtkMessageType %u", message_type);
1130               break;
1131             }
1132
1133           if (stock_id)
1134             {
1135               gtk_stock_lookup (stock_id, &item);
1136               atk_object_set_name (atk_obj, item.label);
1137             }
1138         }
1139
1140       g_object_notify (G_OBJECT (info_bar), "message-type");
1141     }
1142 }
1143
1144 /**
1145  * gtk_info_bar_get_message_type:
1146  * @info_bar: a #GtkInfoBar
1147  *
1148  * Returns the message type of the message area.
1149  *
1150  * Returns: the message type of the message area.
1151  *
1152  * Since: 2.18
1153  */
1154 GtkMessageType
1155 gtk_info_bar_get_message_type (GtkInfoBar *info_bar)
1156 {
1157   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), GTK_MESSAGE_OTHER);
1158
1159   return info_bar->priv->message_type;
1160 }