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