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