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