]> Pileus Git - ~andy/gtk/blob - gtk/gtkinfobar.c
State that _add_button functions return a "#GtkButton widget"
[~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_style_get (widget,
499                         "button-spacing", &button_spacing,
500                         "action-area-border", &action_area_border,
501                         "content-area-spacing", &content_area_spacing,
502                         "content-area-border", &content_area_border,
503                         NULL);
504
505   gtk_box_set_spacing (GTK_BOX (info_bar->priv->action_area), button_spacing);
506   gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->action_area),
507                                   action_area_border);
508   gtk_box_set_spacing (GTK_BOX (info_bar->priv->content_area), content_area_spacing);
509   gtk_container_set_border_width (GTK_CONTAINER (info_bar->priv->content_area),
510                                   content_area_border);
511 }
512
513 static void
514 gtk_info_bar_init (GtkInfoBar *info_bar)
515 {
516   GtkWidget *content_area;
517   GtkWidget *action_area;
518
519   gtk_widget_push_composite_child ();
520
521   info_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (info_bar,
522                                                 GTK_TYPE_INFO_BAR,
523                                                 GtkInfoBarPrivate);
524
525   content_area = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
526   gtk_widget_show (content_area);
527   gtk_box_pack_start (GTK_BOX (info_bar), content_area, TRUE, TRUE, 0);
528
529   action_area = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
530   gtk_widget_show (action_area);
531   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_END);
532   gtk_box_pack_start (GTK_BOX (info_bar), action_area, FALSE, TRUE, 0);
533
534   gtk_widget_set_app_paintable (GTK_WIDGET (info_bar), TRUE);
535   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (info_bar), TRUE);
536
537   info_bar->priv->content_area = content_area;
538   info_bar->priv->action_area = action_area;
539
540   gtk_widget_pop_composite_child ();
541 }
542
543 static GtkBuildableIface *parent_buildable_iface;
544
545 static void
546 gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface)
547 {
548   parent_buildable_iface = g_type_interface_peek_parent (iface);
549   iface->get_internal_child = gtk_info_bar_buildable_get_internal_child;
550   iface->custom_tag_start = gtk_info_bar_buildable_custom_tag_start;
551   iface->custom_finished = gtk_info_bar_buildable_custom_finished;
552 }
553
554 static GObject *
555 gtk_info_bar_buildable_get_internal_child (GtkBuildable *buildable,
556                                            GtkBuilder   *builder,
557                                            const gchar  *childname)
558 {
559   if (strcmp (childname, "content_area") == 0)
560     return G_OBJECT (GTK_INFO_BAR (buildable)->priv->content_area);
561   else if (strcmp (childname, "action_area") == 0)
562     return G_OBJECT (GTK_INFO_BAR (buildable)->priv->action_area);
563
564   return parent_buildable_iface->get_internal_child (buildable,
565                                                      builder,
566                                                      childname);
567 }
568
569 static gint
570 get_response_for_widget (GtkInfoBar *info_bar,
571                          GtkWidget  *widget)
572 {
573   ResponseData *rd;
574
575   rd = get_response_data (widget, FALSE);
576   if (!rd)
577     return GTK_RESPONSE_NONE;
578   else
579     return rd->response_id;
580 }
581
582 static void
583 action_widget_activated (GtkWidget  *widget,
584                          GtkInfoBar *info_bar)
585 {
586   gint response_id;
587
588   response_id = get_response_for_widget (info_bar, widget);
589   gtk_info_bar_response (info_bar, response_id);
590 }
591
592 /**
593  * gtk_info_bar_add_action_widget:
594  * @info_bar: a #GtkInfoBar
595  * @child: an activatable widget
596  * @response_id: response ID for @child
597  *
598  * Add an activatable widget to the action area of a #GtkInfoBar,
599  * connecting a signal handler that will emit the #GtkInfoBar::response
600  * signal on the message area when the widget is activated. The widget
601  * is appended to the end of the message areas action area.
602  *
603  * Since: 2.18
604  */
605 void
606 gtk_info_bar_add_action_widget (GtkInfoBar *info_bar,
607                                 GtkWidget  *child,
608                                 gint        response_id)
609 {
610   ResponseData *ad;
611   guint signal_id;
612
613   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
614   g_return_if_fail (GTK_IS_WIDGET (child));
615
616   ad = get_response_data (child, TRUE);
617
618   ad->response_id = response_id;
619
620   if (GTK_IS_BUTTON (child))
621     signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
622   else
623     signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;
624
625   if (signal_id)
626     {
627       GClosure *closure;
628
629       closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
630                                        G_OBJECT (info_bar));
631       g_signal_connect_closure_by_id (child, signal_id, 0, closure, FALSE);
632     }
633   else
634     g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkInfoBar");
635
636   gtk_box_pack_end (GTK_BOX (info_bar->priv->action_area),
637                     child, FALSE, FALSE, 0);
638   if (response_id == GTK_RESPONSE_HELP)
639     gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area),
640                                         child, TRUE);
641 }
642
643 /**
644  * gtk_info_bar_get_action_area:
645  * @info_bar: a #GtkInfoBar
646  *
647  * Returns the action area of @info_bar.
648  *
649  * Returns: (transfer none): the action area
650  *
651  * Since: 2.18
652  */
653 GtkWidget*
654 gtk_info_bar_get_action_area (GtkInfoBar *info_bar)
655 {
656   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
657
658   return info_bar->priv->action_area;
659 }
660
661 /**
662  * gtk_info_bar_get_content_area:
663  * @info_bar: a #GtkInfoBar
664  *
665  * Returns the content area of @info_bar.
666  *
667  * Returns: (transfer none): the content area
668  *
669  * Since: 2.18
670  */
671 GtkWidget*
672 gtk_info_bar_get_content_area (GtkInfoBar *info_bar)
673 {
674   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
675
676   return info_bar->priv->content_area;
677 }
678
679 /**
680  * gtk_info_bar_add_button:
681  * @info_bar: a #GtkInfoBar
682  * @button_text: text of button, or stock ID
683  * @response_id: response ID for the button
684  *
685  * Adds a button with the given text (or a stock button, if button_text
686  * is a stock ID) and sets things up so that clicking the button will emit
687  * the "response" signal with the given response_id. The button is appended
688  * to the end of the info bars's action area. The button widget is
689  * returned, but usually you don't need it.
690  *
691  * Returns: (transfer none): the #GtkButton widget that was added
692  *
693  * Since: 2.18
694  */
695 GtkWidget*
696 gtk_info_bar_add_button (GtkInfoBar  *info_bar,
697                          const gchar *button_text,
698                          gint         response_id)
699 {
700   GtkWidget *button;
701
702   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL);
703   g_return_val_if_fail (button_text != NULL, NULL);
704
705   button = gtk_button_new_from_stock (button_text);
706
707   gtk_widget_set_can_default (button, TRUE);
708
709   gtk_widget_show (button);
710
711   gtk_info_bar_add_action_widget (info_bar, button, response_id);
712
713   return button;
714 }
715
716 static void
717 add_buttons_valist (GtkInfoBar  *info_bar,
718                     const gchar *first_button_text,
719                     va_list      args)
720 {
721   const gchar* text;
722   gint response_id;
723
724   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
725
726   if (first_button_text == NULL)
727     return;
728
729   text = first_button_text;
730   response_id = va_arg (args, gint);
731
732   while (text != NULL)
733     {
734       gtk_info_bar_add_button (info_bar, text, response_id);
735
736       text = va_arg (args, gchar*);
737       if (text == NULL)
738         break;
739
740       response_id = va_arg (args, int);
741     }
742 }
743
744 /**
745  * gtk_info_bar_add_buttons:
746  * @info_bar: a #GtkInfoBar
747  * @first_button_text: button text or stock ID
748  * @...: response ID for first button, then more text-response_id pairs,
749  *     ending with %NULL
750  *
751  * Adds more buttons, same as calling gtk_info_bar_add_button()
752  * repeatedly. The variable argument list should be %NULL-terminated
753  * as with gtk_info_bar_new_with_buttons(). Each button must have both
754  * text and response ID.
755  *
756  * Since: 2.18
757  */
758 void
759 gtk_info_bar_add_buttons (GtkInfoBar  *info_bar,
760                           const gchar *first_button_text,
761                           ...)
762 {
763   va_list args;
764
765   va_start (args, first_button_text);
766   add_buttons_valist (info_bar, first_button_text, args);
767   va_end (args);
768 }
769
770 /**
771  * gtk_info_bar_new:
772  *
773  * Creates a new #GtkInfoBar object.
774  *
775  * Returns: a new #GtkInfoBar object
776  *
777  * Since: 2.18
778  */
779 GtkWidget *
780 gtk_info_bar_new (void)
781 {
782    return g_object_new (GTK_TYPE_INFO_BAR, NULL);
783 }
784
785 /**
786  * gtk_info_bar_new_with_buttons:
787  * @first_button_text: (allow-none): stock ID or text to go in first button, or %NULL
788  * @...: response ID for first button, then additional buttons, ending
789  *    with %NULL
790  *
791  * Creates a new #GtkInfoBar with buttons. Button text/response ID
792  * pairs should be listed, with a %NULL pointer ending the list.
793  * Button text can be either a stock ID such as %GTK_STOCK_OK, or
794  * some arbitrary text. A response ID can be any positive number,
795  * or one of the values in the #GtkResponseType enumeration. If the
796  * user clicks one of these dialog buttons, GtkInfoBar will emit
797  * the "response" signal with the corresponding response ID.
798  *
799  * Returns: a new #GtkInfoBar
800  */
801 GtkWidget*
802 gtk_info_bar_new_with_buttons (const gchar *first_button_text,
803                                ...)
804 {
805   GtkInfoBar *info_bar;
806   va_list args;
807
808   info_bar = GTK_INFO_BAR (gtk_info_bar_new ());
809
810   va_start (args, first_button_text);
811   add_buttons_valist (info_bar, first_button_text, args);
812   va_end (args);
813
814   return GTK_WIDGET (info_bar);
815 }
816
817 /**
818  * gtk_info_bar_set_response_sensitive:
819  * @info_bar: a #GtkInfoBar
820  * @response_id: a response ID
821  * @setting: TRUE for sensitive
822  *
823  * Calls gtk_widget_set_sensitive (widget, setting) for each
824  * widget in the info bars's action area with the given response_id.
825  * A convenient way to sensitize/desensitize dialog buttons.
826  *
827  * Since: 2.18
828  */
829 void
830 gtk_info_bar_set_response_sensitive (GtkInfoBar *info_bar,
831                                      gint        response_id,
832                                      gboolean    setting)
833 {
834   GList *children, *list;
835
836   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
837
838   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
839
840   for (list = children; list; list = list->next)
841     {
842       GtkWidget *widget = list->data;
843       ResponseData *rd = get_response_data (widget, FALSE);
844
845       if (rd && rd->response_id == response_id)
846         gtk_widget_set_sensitive (widget, setting);
847     }
848
849   g_list_free (children);
850 }
851
852 /**
853  * gtk_info_bar_set_default_response:
854  * @info_bar: a #GtkInfoBar
855  * @response_id: a response ID
856  *
857  * Sets the last widget in the info bar's action area with
858  * the given response_id as the default widget for the dialog.
859  * Pressing "Enter" normally activates the default widget.
860  *
861  * Note that this function currently requires @info_bar to
862  * be added to a widget hierarchy. 
863  *
864  * Since: 2.18
865  */
866 void
867 gtk_info_bar_set_default_response (GtkInfoBar *info_bar,
868                                    gint        response_id)
869 {
870   GList *children, *list;
871
872   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
873
874   children = gtk_container_get_children (GTK_CONTAINER (info_bar->priv->action_area));
875
876   for (list = children; list; list = list->next)
877     {
878       GtkWidget *widget = list->data;
879       ResponseData *rd = get_response_data (widget, FALSE);
880
881       if (rd && rd->response_id == response_id)
882         gtk_widget_grab_default (widget);
883     }
884
885   g_list_free (children);
886 }
887
888 /**
889  * gtk_info_bar_response:
890  * @info_bar: a #GtkInfoBar
891  * @response_id: a response ID
892  *
893  * Emits the 'response' signal with the given @response_id.
894  *
895  * Since: 2.18
896  */
897 void
898 gtk_info_bar_response (GtkInfoBar *info_bar,
899                        gint        response_id)
900 {
901   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
902
903   g_signal_emit (info_bar, signals[RESPONSE], 0, response_id);
904 }
905
906 typedef struct
907 {
908   gchar *widget_name;
909   gchar *response_id;
910 } ActionWidgetInfo;
911
912 typedef struct
913 {
914   GtkInfoBar *info_bar;
915   GtkBuilder *builder;
916   GSList *items;
917   gchar *response;
918 } ActionWidgetsSubParserData;
919
920 static void
921 attributes_start_element (GMarkupParseContext  *context,
922                           const gchar          *element_name,
923                           const gchar         **names,
924                           const gchar         **values,
925                           gpointer              user_data,
926                           GError              **error)
927 {
928   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
929   guint i;
930
931   if (strcmp (element_name, "action-widget") == 0)
932     {
933       for (i = 0; names[i]; i++)
934         if (strcmp (names[i], "response") == 0)
935           parser_data->response = g_strdup (values[i]);
936     }
937   else if (strcmp (element_name, "action-widgets") == 0)
938     return;
939   else
940     g_warning ("Unsupported tag for GtkInfoBar: %s\n", element_name);
941 }
942
943 static void
944 attributes_text_element (GMarkupParseContext  *context,
945                          const gchar          *text,
946                          gsize                 text_len,
947                          gpointer              user_data,
948                          GError              **error)
949 {
950   ActionWidgetsSubParserData *parser_data = (ActionWidgetsSubParserData*)user_data;
951   ActionWidgetInfo *item;
952
953   if (!parser_data->response)
954     return;
955
956   item = g_new (ActionWidgetInfo, 1);
957   item->widget_name = g_strndup (text, text_len);
958   item->response_id = parser_data->response;
959   parser_data->items = g_slist_prepend (parser_data->items, item);
960   parser_data->response = NULL;
961 }
962
963 static const GMarkupParser attributes_parser =
964 {
965   attributes_start_element,
966   NULL,
967   attributes_text_element,
968 };
969
970 gboolean
971 gtk_info_bar_buildable_custom_tag_start (GtkBuildable  *buildable,
972                                          GtkBuilder    *builder,
973                                          GObject       *child,
974                                          const gchar   *tagname,
975                                          GMarkupParser *parser,
976                                          gpointer      *data)
977 {
978   ActionWidgetsSubParserData *parser_data;
979
980   if (child)
981     return FALSE;
982
983   if (strcmp (tagname, "action-widgets") == 0)
984     {
985       parser_data = g_slice_new0 (ActionWidgetsSubParserData);
986       parser_data->info_bar = GTK_INFO_BAR (buildable);
987       parser_data->items = NULL;
988
989       *parser = attributes_parser;
990       *data = parser_data;
991       return TRUE;
992     }
993
994   return parent_buildable_iface->custom_tag_start (buildable, builder, child,
995                                                    tagname, parser, data);
996 }
997
998 static void
999 gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable,
1000                                         GtkBuilder   *builder,
1001                                         GObject      *child,
1002                                         const gchar  *tagname,
1003                                         gpointer      user_data)
1004 {
1005   GSList *l;
1006   ActionWidgetsSubParserData *parser_data;
1007   GObject *object;
1008   GtkInfoBar *info_bar;
1009   ResponseData *ad;
1010   guint signal_id;
1011
1012   if (strcmp (tagname, "action-widgets"))
1013     {
1014       parent_buildable_iface->custom_finished (buildable, builder, child,
1015                                                tagname, user_data);
1016       return;
1017     }
1018
1019   info_bar = GTK_INFO_BAR (buildable);
1020   parser_data = (ActionWidgetsSubParserData*)user_data;
1021   parser_data->items = g_slist_reverse (parser_data->items);
1022
1023   for (l = parser_data->items; l; l = l->next)
1024     {
1025       ActionWidgetInfo *item = l->data;
1026
1027       object = gtk_builder_get_object (builder, item->widget_name);
1028       if (!object)
1029         {
1030           g_warning ("Unknown object %s specified in action-widgets of %s",
1031                      item->widget_name,
1032                      gtk_buildable_get_name (GTK_BUILDABLE (buildable)));
1033           continue;
1034         }
1035
1036       ad = get_response_data (GTK_WIDGET (object), TRUE);
1037       ad->response_id = atoi (item->response_id);
1038
1039       if (GTK_IS_BUTTON (object))
1040         signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
1041       else
1042         signal_id = GTK_WIDGET_GET_CLASS (object)->activate_signal;
1043
1044       if (signal_id)
1045         {
1046           GClosure *closure;
1047
1048           closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
1049                                            G_OBJECT (info_bar));
1050           g_signal_connect_closure_by_id (object,
1051                                           signal_id,
1052                                           0,
1053                                           closure,
1054                                           FALSE);
1055         }
1056
1057       if (ad->response_id == GTK_RESPONSE_HELP)
1058         gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (info_bar->priv->action_area),
1059                                             GTK_WIDGET (object), TRUE);
1060
1061       g_free (item->widget_name);
1062       g_free (item->response_id);
1063       g_free (item);
1064     }
1065   g_slist_free (parser_data->items);
1066   g_slice_free (ActionWidgetsSubParserData, parser_data);
1067 }
1068
1069 /**
1070  * gtk_info_bar_set_message_type:
1071  * @info_bar: a #GtkInfoBar
1072  * @message_type: a #GtkMessageType
1073  *
1074  * Sets the message type of the message area.
1075  * GTK+ uses this type to determine what color to use
1076  * when drawing the message area.
1077  *
1078  * Since: 2.18
1079  */
1080 void
1081 gtk_info_bar_set_message_type (GtkInfoBar     *info_bar,
1082                                GtkMessageType  message_type)
1083 {
1084   GtkInfoBarPrivate *priv;
1085   AtkObject *atk_obj;
1086
1087   g_return_if_fail (GTK_IS_INFO_BAR (info_bar));
1088
1089   priv = info_bar->priv;
1090
1091   if (priv->message_type != message_type)
1092     {
1093       priv->message_type = message_type;
1094
1095       gtk_widget_queue_draw (GTK_WIDGET (info_bar));
1096
1097       atk_obj = gtk_widget_get_accessible (GTK_WIDGET (info_bar));
1098       if (GTK_IS_ACCESSIBLE (atk_obj))
1099         {
1100           GtkStockItem item;
1101           const char *stock_id = NULL;
1102
1103           atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
1104
1105           switch (message_type)
1106             {
1107             case GTK_MESSAGE_INFO:
1108               stock_id = GTK_STOCK_DIALOG_INFO;
1109               break;
1110
1111             case GTK_MESSAGE_QUESTION:
1112               stock_id = GTK_STOCK_DIALOG_QUESTION;
1113               break;
1114
1115             case GTK_MESSAGE_WARNING:
1116               stock_id = GTK_STOCK_DIALOG_WARNING;
1117               break;
1118
1119             case GTK_MESSAGE_ERROR:
1120               stock_id = GTK_STOCK_DIALOG_ERROR;
1121               break;
1122
1123             case GTK_MESSAGE_OTHER:
1124               break;
1125
1126             default:
1127               g_warning ("Unknown GtkMessageType %u", message_type);
1128               break;
1129             }
1130
1131           if (stock_id)
1132             {
1133               gtk_stock_lookup (stock_id, &item);
1134               atk_object_set_name (atk_obj, item.label);
1135             }
1136         }
1137
1138       g_object_notify (G_OBJECT (info_bar), "message-type");
1139     }
1140 }
1141
1142 /**
1143  * gtk_info_bar_get_message_type:
1144  * @info_bar: a #GtkInfoBar
1145  *
1146  * Returns the message type of the message area.
1147  *
1148  * Returns: the message type of the message area.
1149  *
1150  * Since: 2.18
1151  */
1152 GtkMessageType
1153 gtk_info_bar_get_message_type (GtkInfoBar *info_bar)
1154 {
1155   g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), GTK_MESSAGE_OTHER);
1156
1157   return info_bar->priv->message_type;
1158 }