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, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | /* |
22 | * Modified by the gedit Team, 2005. See the AUTHORS file for a |
23 | * list of people on the gtk Team. |
24 | * See the gedit ChangeLog files for a list of changes. |
25 | * |
26 | * Modified by the GTK+ team, 2008-2009. |
27 | */ |
28 | |
29 | |
30 | #include "config.h" |
31 | |
32 | #include <stdlib.h> |
33 | |
34 | #include "gtkinfobar.h" |
35 | #include "gtkbuildable.h" |
36 | #include "gtkbuilderprivate.h" |
37 | #include "gtkbox.h" |
38 | #include "gtklabel.h" |
39 | #include "gtkbutton.h" |
40 | #include "gtkenums.h" |
41 | #include "gtkdialog.h" |
42 | #include "gtkrevealer.h" |
43 | #include "gtkintl.h" |
44 | #include "gtkprivate.h" |
45 | #include "gtktypebuiltins.h" |
46 | #include "gtkwidgetprivate.h" |
47 | #include "gtkbinlayout.h" |
48 | #include "gtkgestureclick.h" |
49 | |
50 | /** |
51 | * GtkInfoBar: |
52 | * |
53 | * `GtkInfoBar` can be show messages to the user without a dialog. |
54 | * |
55 | * ![An example GtkInfoBar](info-bar.png) |
56 | * |
57 | * It is often temporarily shown at the top or bottom of a document. |
58 | * In contrast to [class@Gtk.Dialog], which has an action area at the |
59 | * bottom, `GtkInfoBar` has an action area at the side. |
60 | * |
61 | * The API of `GtkInfoBar` is very similar to `GtkDialog`, allowing you |
62 | * to add buttons to the action area with [method@Gtk.InfoBar.add_button] |
63 | * or [ctor@Gtk.InfoBar.new_with_buttons]. The sensitivity of action widgets |
64 | * can be controlled with [method@Gtk.InfoBar.set_response_sensitive]. |
65 | * |
66 | * To add widgets to the main content area of a `GtkInfoBar`, use |
67 | * [method@Gtk.InfoBar.add_child]. |
68 | * |
69 | * Similar to [class@Gtk.MessageDialog], the contents of a `GtkInfoBar` |
70 | * can by classified as error message, warning, informational message, etc, |
71 | * by using [method@Gtk.InfoBar.set_message_type]. GTK may use the message |
72 | * type to determine how the message is displayed. |
73 | * |
74 | * A simple example for using a `GtkInfoBar`: |
75 | * ```c |
76 | * GtkWidget *message_label; |
77 | * GtkWidget *widget; |
78 | * GtkWidget *grid; |
79 | * GtkInfoBar *bar; |
80 | * |
81 | * // set up info bar |
82 | * widget = gtk_info_bar_new (); |
83 | * bar = GTK_INFO_BAR (widget); |
84 | * grid = gtk_grid_new (); |
85 | * |
86 | * message_label = gtk_label_new (""); |
87 | * gtk_info_bar_add_child (bar, message_label); |
88 | * gtk_info_bar_add_button (bar, |
89 | * _("_OK"), |
90 | * GTK_RESPONSE_OK); |
91 | * g_signal_connect (bar, |
92 | * "response", |
93 | * G_CALLBACK (gtk_widget_hide), |
94 | * NULL); |
95 | * gtk_grid_attach (GTK_GRID (grid), |
96 | * widget, |
97 | * 0, 2, 1, 1); |
98 | * |
99 | * // ... |
100 | * |
101 | * // show an error message |
102 | * gtk_label_set_text (GTK_LABEL (message_label), "An error occurred!"); |
103 | * gtk_info_bar_set_message_type (bar, GTK_MESSAGE_ERROR); |
104 | * gtk_widget_show (bar); |
105 | * ``` |
106 | * |
107 | * # GtkInfoBar as GtkBuildable |
108 | * |
109 | * `GtkInfoBar` supports a custom `<action-widgets>` element, which can contain |
110 | * multiple `<action-widget>` elements. The “response” attribute specifies a |
111 | * numeric response, and the content of the element is the id of widget |
112 | * (which should be a child of the dialogs @action_area). |
113 | * |
114 | * `GtkInfoBar` supports adding action widgets by specifying “action” as |
115 | * the “type” attribute of a `<child>` element. The widget will be added |
116 | * either to the action area. The response id has to be associated |
117 | * with the action widget using the `<action-widgets>` element. |
118 | * |
119 | * # CSS nodes |
120 | * |
121 | * `GtkInfoBar` has a single CSS node with name infobar. The node may get |
122 | * one of the style classes .info, .warning, .error or .question, depending |
123 | * on the message type. |
124 | * If the info bar shows a close button, that button will have the .close |
125 | * style class applied. |
126 | */ |
127 | |
128 | enum |
129 | { |
130 | PROP_0, |
131 | PROP_MESSAGE_TYPE, |
132 | PROP_SHOW_CLOSE_BUTTON, |
133 | PROP_REVEALED, |
134 | LAST_PROP |
135 | }; |
136 | |
137 | typedef struct _GtkInfoBarClass GtkInfoBarClass; |
138 | |
139 | struct _GtkInfoBar |
140 | { |
141 | GtkWidget parent_instance; |
142 | |
143 | GtkWidget *content_area; |
144 | GtkWidget *action_area; |
145 | GtkWidget *close_button; |
146 | GtkWidget *revealer; |
147 | |
148 | GtkMessageType message_type; |
149 | int default_response; |
150 | gboolean default_response_sensitive; |
151 | }; |
152 | |
153 | struct _GtkInfoBarClass |
154 | { |
155 | GtkWidgetClass parent_class; |
156 | |
157 | void (* response) (GtkInfoBar *info_bar, int response_id); |
158 | void (* close) (GtkInfoBar *info_bar); |
159 | }; |
160 | |
161 | typedef struct _ResponseData ResponseData; |
162 | |
163 | struct _ResponseData |
164 | { |
165 | int response_id; |
166 | gulong handler_id; |
167 | }; |
168 | |
169 | enum |
170 | { |
171 | RESPONSE, |
172 | CLOSE, |
173 | LAST_SIGNAL |
174 | }; |
175 | |
176 | static GParamSpec *props[LAST_PROP] = { NULL, }; |
177 | static guint signals[LAST_SIGNAL]; |
178 | |
179 | static void gtk_info_bar_set_property (GObject *object, |
180 | guint prop_id, |
181 | const GValue *value, |
182 | GParamSpec *pspec); |
183 | static void gtk_info_bar_get_property (GObject *object, |
184 | guint prop_id, |
185 | GValue *value, |
186 | GParamSpec *pspec); |
187 | static void gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface); |
188 | static gboolean gtk_info_bar_buildable_custom_tag_start (GtkBuildable *buildable, |
189 | GtkBuilder *builder, |
190 | GObject *child, |
191 | const char *tagname, |
192 | GtkBuildableParser *parser, |
193 | gpointer *data); |
194 | static void gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable, |
195 | GtkBuilder *builder, |
196 | GObject *child, |
197 | const char *tagname, |
198 | gpointer user_data); |
199 | static void gtk_info_bar_buildable_add_child (GtkBuildable *buildable, |
200 | GtkBuilder *builder, |
201 | GObject *child, |
202 | const char *type); |
203 | |
204 | |
205 | |
206 | G_DEFINE_TYPE_WITH_CODE (GtkInfoBar, gtk_info_bar, GTK_TYPE_WIDGET, |
207 | G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, |
208 | gtk_info_bar_buildable_interface_init)) |
209 | |
210 | static void |
211 | gtk_info_bar_set_property (GObject *object, |
212 | guint prop_id, |
213 | const GValue *value, |
214 | GParamSpec *pspec) |
215 | { |
216 | GtkInfoBar *info_bar = GTK_INFO_BAR (object); |
217 | |
218 | switch (prop_id) |
219 | { |
220 | case PROP_MESSAGE_TYPE: |
221 | gtk_info_bar_set_message_type (info_bar, message_type: g_value_get_enum (value)); |
222 | break; |
223 | case PROP_SHOW_CLOSE_BUTTON: |
224 | gtk_info_bar_set_show_close_button (info_bar, setting: g_value_get_boolean (value)); |
225 | break; |
226 | case PROP_REVEALED: |
227 | gtk_info_bar_set_revealed (info_bar, revealed: g_value_get_boolean (value)); |
228 | break; |
229 | default: |
230 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
231 | break; |
232 | } |
233 | } |
234 | |
235 | static void |
236 | gtk_info_bar_get_property (GObject *object, |
237 | guint prop_id, |
238 | GValue *value, |
239 | GParamSpec *pspec) |
240 | { |
241 | GtkInfoBar *info_bar = GTK_INFO_BAR (object); |
242 | |
243 | switch (prop_id) |
244 | { |
245 | case PROP_MESSAGE_TYPE: |
246 | g_value_set_enum (value, v_enum: gtk_info_bar_get_message_type (info_bar)); |
247 | break; |
248 | case PROP_SHOW_CLOSE_BUTTON: |
249 | g_value_set_boolean (value, v_boolean: gtk_info_bar_get_show_close_button (info_bar)); |
250 | break; |
251 | case PROP_REVEALED: |
252 | g_value_set_boolean (value, v_boolean: gtk_info_bar_get_revealed (info_bar)); |
253 | break; |
254 | default: |
255 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
256 | break; |
257 | } |
258 | } |
259 | |
260 | static void |
261 | response_data_free (gpointer data) |
262 | { |
263 | g_slice_free (ResponseData, data); |
264 | } |
265 | |
266 | static ResponseData * |
267 | get_response_data (GtkWidget *widget, |
268 | gboolean create) |
269 | { |
270 | ResponseData *ad = g_object_get_data (G_OBJECT (widget), |
271 | key: "gtk-info-bar-response-data" ); |
272 | |
273 | if (ad == NULL && create) |
274 | { |
275 | ad = g_slice_new (ResponseData); |
276 | |
277 | g_object_set_data_full (G_OBJECT (widget), |
278 | I_("gtk-info-bar-response-data" ), |
279 | data: ad, |
280 | destroy: response_data_free); |
281 | } |
282 | |
283 | return ad; |
284 | } |
285 | |
286 | static void |
287 | clear_response_data (GtkWidget *widget) |
288 | { |
289 | ResponseData *data; |
290 | |
291 | data = get_response_data (widget, FALSE); |
292 | g_signal_handler_disconnect (instance: widget, handler_id: data->handler_id); |
293 | g_object_set_data (G_OBJECT (widget), key: "gtk-info-bar-response-data" , NULL); |
294 | } |
295 | |
296 | static GtkWidget * |
297 | find_button (GtkInfoBar *info_bar, |
298 | int response_id) |
299 | { |
300 | GtkWidget *child; |
301 | |
302 | for (child = gtk_widget_get_first_child (widget: info_bar->action_area); |
303 | child != NULL; |
304 | child = gtk_widget_get_next_sibling (widget: child)) |
305 | { |
306 | ResponseData *rd = get_response_data (widget: child, FALSE); |
307 | |
308 | if (rd && rd->response_id == response_id) |
309 | return child; |
310 | } |
311 | |
312 | return NULL; |
313 | } |
314 | |
315 | static void |
316 | gtk_info_bar_close (GtkInfoBar *info_bar) |
317 | { |
318 | if (!gtk_widget_get_visible (widget: info_bar->close_button) && |
319 | !find_button (info_bar, response_id: GTK_RESPONSE_CANCEL)) |
320 | return; |
321 | |
322 | gtk_info_bar_response (GTK_INFO_BAR (info_bar), |
323 | response_id: GTK_RESPONSE_CANCEL); |
324 | } |
325 | |
326 | static void |
327 | gtk_info_bar_dispose (GObject *object) |
328 | { |
329 | GtkInfoBar *self = GTK_INFO_BAR (object); |
330 | |
331 | g_clear_pointer (&self->revealer, gtk_widget_unparent); |
332 | |
333 | G_OBJECT_CLASS (gtk_info_bar_parent_class)->dispose (object); |
334 | } |
335 | |
336 | static void |
337 | gtk_info_bar_class_init (GtkInfoBarClass *klass) |
338 | { |
339 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
340 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
341 | |
342 | object_class->get_property = gtk_info_bar_get_property; |
343 | object_class->set_property = gtk_info_bar_set_property; |
344 | object_class->dispose = gtk_info_bar_dispose; |
345 | |
346 | klass->close = gtk_info_bar_close; |
347 | |
348 | /** |
349 | * GtkInfoBar:message-type: (attributes org.gtk.Property.get=gtk_info_bar_get_message_type org.gtk.Property.set=gtk_info_bar_set_message_type) |
350 | * |
351 | * The type of the message. |
352 | * |
353 | * The type may be used to determine the appearance of the info bar. |
354 | */ |
355 | props[PROP_MESSAGE_TYPE] = |
356 | g_param_spec_enum (name: "message-type" , |
357 | P_("Message Type" ), |
358 | P_("The type of message" ), |
359 | enum_type: GTK_TYPE_MESSAGE_TYPE, |
360 | default_value: GTK_MESSAGE_INFO, |
361 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY); |
362 | |
363 | /** |
364 | * GtkInfoBar:show-close-button: (attributes org.gtk.Property.get=gtk_info_bar_get_show_close_button org.gtk.Property.set=gtk_info_bar_set_show_close_button) |
365 | * |
366 | * Whether to include a standard close button. |
367 | */ |
368 | props[PROP_SHOW_CLOSE_BUTTON] = |
369 | g_param_spec_boolean (name: "show-close-button" , |
370 | P_("Show Close Button" ), |
371 | P_("Whether to include a standard close button" ), |
372 | FALSE, |
373 | GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY); |
374 | |
375 | /** |
376 | * GtkInfoBar:revealed: (attributes org.gtk.Property.get=gtk_info_bar_get_revealed org.gtk.Property.set=gtk_info_bar_set_revealed) |
377 | * |
378 | * Whether the info bar shows its contents. |
379 | */ |
380 | props[PROP_REVEALED] = |
381 | g_param_spec_boolean (name: "revealed" , |
382 | P_("Reveal" ), |
383 | P_("Controls whether the info bar shows its contents or not" ), |
384 | TRUE, |
385 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY); |
386 | |
387 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props); |
388 | |
389 | /** |
390 | * GtkInfoBar::response: |
391 | * @info_bar: the object on which the signal is emitted |
392 | * @response_id: the response ID |
393 | * |
394 | * Emitted when an action widget is clicked. |
395 | * |
396 | * The signal is also emitted when the application programmer |
397 | * calls [method@Gtk.InfoBar.response]. The @response_id depends |
398 | * on which action widget was clicked. |
399 | */ |
400 | signals[RESPONSE] = g_signal_new (I_("response" ), |
401 | G_OBJECT_CLASS_TYPE (klass), |
402 | signal_flags: G_SIGNAL_RUN_LAST, |
403 | G_STRUCT_OFFSET (GtkInfoBarClass, response), |
404 | NULL, NULL, |
405 | NULL, |
406 | G_TYPE_NONE, n_params: 1, |
407 | G_TYPE_INT); |
408 | |
409 | /** |
410 | * GtkInfoBar::close: |
411 | * |
412 | * Gets emitted when the user uses a keybinding to dismiss the info bar. |
413 | * |
414 | * The ::close signal is a [keybinding signal](class.SignalAction.html). |
415 | * |
416 | * The default binding for this signal is the Escape key. |
417 | */ |
418 | signals[CLOSE] = g_signal_new (I_("close" ), |
419 | G_OBJECT_CLASS_TYPE (klass), |
420 | signal_flags: G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, |
421 | G_STRUCT_OFFSET (GtkInfoBarClass, close), |
422 | NULL, NULL, |
423 | NULL, |
424 | G_TYPE_NONE, n_params: 0); |
425 | |
426 | gtk_widget_class_add_binding_signal (widget_class, |
427 | GDK_KEY_Escape, mods: 0, |
428 | signal: "close" , |
429 | NULL); |
430 | |
431 | gtk_widget_class_set_css_name (widget_class, I_("infobar" )); |
432 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); |
433 | } |
434 | |
435 | static void |
436 | close_button_clicked_cb (GtkWidget *button, |
437 | GtkInfoBar *info_bar) |
438 | { |
439 | gtk_info_bar_response (GTK_INFO_BAR (info_bar), response_id: GTK_RESPONSE_CLOSE); |
440 | } |
441 | |
442 | static void |
443 | click_released_cb (GtkGestureClick *gesture, |
444 | guint n_press, |
445 | double x, |
446 | double y, |
447 | GtkInfoBar *info_bar) |
448 | { |
449 | if (info_bar->default_response && info_bar->default_response_sensitive) |
450 | gtk_info_bar_response (info_bar, response_id: info_bar->default_response); |
451 | } |
452 | |
453 | static void |
454 | gtk_info_bar_init (GtkInfoBar *info_bar) |
455 | { |
456 | GtkWidget *widget = GTK_WIDGET (info_bar); |
457 | GtkWidget *main_box; |
458 | GtkGesture *gesture; |
459 | |
460 | /* message-type is a CONSTRUCT property, so we init to a value |
461 | * different from its default to trigger its property setter |
462 | * during construction */ |
463 | info_bar->message_type = GTK_MESSAGE_OTHER; |
464 | |
465 | info_bar->revealer = gtk_revealer_new (); |
466 | gtk_revealer_set_reveal_child (GTK_REVEALER (info_bar->revealer), TRUE); |
467 | gtk_widget_set_parent (widget: info_bar->revealer, parent: widget); |
468 | |
469 | main_box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
470 | gtk_revealer_set_child (GTK_REVEALER (info_bar->revealer), child: main_box); |
471 | |
472 | info_bar->content_area = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
473 | gtk_widget_set_hexpand (widget: info_bar->content_area, TRUE); |
474 | gtk_box_append (GTK_BOX (main_box), child: info_bar->content_area); |
475 | |
476 | info_bar->action_area = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0); |
477 | gtk_widget_set_halign (widget: info_bar->action_area, align: GTK_ALIGN_END); |
478 | gtk_widget_set_valign (widget: info_bar->action_area, align: GTK_ALIGN_CENTER); |
479 | gtk_box_append (GTK_BOX (main_box), child: info_bar->action_area); |
480 | |
481 | info_bar->close_button = gtk_button_new_from_icon_name (icon_name: "window-close-symbolic" ); |
482 | gtk_widget_hide (widget: info_bar->close_button); |
483 | gtk_widget_set_valign (widget: info_bar->close_button, align: GTK_ALIGN_CENTER); |
484 | gtk_widget_add_css_class (widget: info_bar->close_button, css_class: "close" ); |
485 | gtk_box_append (GTK_BOX (main_box), child: info_bar->close_button); |
486 | g_signal_connect (info_bar->close_button, "clicked" , |
487 | G_CALLBACK (close_button_clicked_cb), info_bar); |
488 | |
489 | gesture = gtk_gesture_click_new (); |
490 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_PRIMARY); |
491 | g_signal_connect (gesture, "released" , G_CALLBACK (click_released_cb), widget); |
492 | gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture)); |
493 | } |
494 | |
495 | static GtkBuildableIface *parent_buildable_iface; |
496 | |
497 | static void |
498 | gtk_info_bar_buildable_interface_init (GtkBuildableIface *iface) |
499 | { |
500 | parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface); |
501 | |
502 | iface->add_child = gtk_info_bar_buildable_add_child; |
503 | iface->custom_tag_start = gtk_info_bar_buildable_custom_tag_start; |
504 | iface->custom_finished = gtk_info_bar_buildable_custom_finished; |
505 | } |
506 | |
507 | static int |
508 | get_response_for_widget (GtkInfoBar *info_bar, |
509 | GtkWidget *widget) |
510 | { |
511 | ResponseData *rd; |
512 | |
513 | rd = get_response_data (widget, FALSE); |
514 | if (!rd) |
515 | return GTK_RESPONSE_NONE; |
516 | else |
517 | return rd->response_id; |
518 | } |
519 | |
520 | static void |
521 | action_widget_activated (GtkWidget *widget, |
522 | GtkInfoBar *info_bar) |
523 | { |
524 | int response_id; |
525 | |
526 | response_id = get_response_for_widget (info_bar, widget); |
527 | gtk_info_bar_response (info_bar, response_id); |
528 | } |
529 | |
530 | /** |
531 | * gtk_info_bar_add_action_widget: |
532 | * @info_bar: a `GtkInfoBar` |
533 | * @child: an activatable widget |
534 | * @response_id: response ID for @child |
535 | * |
536 | * Add an activatable widget to the action area of a `GtkInfoBar`. |
537 | * |
538 | * This also connects a signal handler that will emit the |
539 | * [signal@Gtk.InfoBar::response] signal on the message area |
540 | * when the widget is activated. The widget is appended to the |
541 | * end of the message areas action area. |
542 | */ |
543 | void |
544 | gtk_info_bar_add_action_widget (GtkInfoBar *info_bar, |
545 | GtkWidget *child, |
546 | int response_id) |
547 | { |
548 | ResponseData *ad; |
549 | guint signal_id; |
550 | |
551 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
552 | g_return_if_fail (GTK_IS_WIDGET (child)); |
553 | |
554 | ad = get_response_data (widget: child, TRUE); |
555 | |
556 | G_DEBUG_HERE(); |
557 | ad->response_id = response_id; |
558 | |
559 | if (GTK_IS_BUTTON (child)) |
560 | signal_id = g_signal_lookup (name: "clicked" , GTK_TYPE_BUTTON); |
561 | else |
562 | signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (child)); |
563 | |
564 | if (signal_id) |
565 | { |
566 | GClosure *closure; |
567 | |
568 | closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), |
569 | G_OBJECT (info_bar)); |
570 | ad->handler_id = g_signal_connect_closure_by_id (instance: child, signal_id, detail: 0, closure, FALSE); |
571 | } |
572 | else |
573 | g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkInfoBar" ); |
574 | |
575 | gtk_box_append (GTK_BOX (info_bar->action_area), child); |
576 | } |
577 | |
578 | /** |
579 | * gtk_info_bar_remove_action_widget: |
580 | * @info_bar: a `GtkInfoBar` |
581 | * @widget: an action widget to remove |
582 | * |
583 | * Removes a widget from the action area of @info_bar. |
584 | * |
585 | * The widget must have been put there by a call to |
586 | * [method@Gtk.InfoBar.add_action_widget] or [method@Gtk.InfoBar.add_button]. |
587 | */ |
588 | void |
589 | gtk_info_bar_remove_action_widget (GtkInfoBar *info_bar, |
590 | GtkWidget *widget) |
591 | { |
592 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
593 | g_return_if_fail (GTK_IS_WIDGET (widget)); |
594 | g_return_if_fail (gtk_widget_get_parent (widget) == info_bar->action_area); |
595 | |
596 | clear_response_data (widget); |
597 | |
598 | gtk_box_remove (GTK_BOX (info_bar->action_area), child: widget); |
599 | } |
600 | |
601 | /** |
602 | * gtk_info_bar_add_button: |
603 | * @info_bar: a `GtkInfoBar` |
604 | * @button_text: text of button |
605 | * @response_id: response ID for the button |
606 | * |
607 | * Adds a button with the given text. |
608 | * |
609 | * Clicking the button will emit the [signal@Gtk.InfoBar::response] |
610 | * signal with the given response_id. The button is appended to the |
611 | * end of the info bars's action area. The button widget is returned, |
612 | * but usually you don't need it. |
613 | * |
614 | * Returns: (transfer none) (type Gtk.Button): the `GtkButton` widget |
615 | * that was added |
616 | */ |
617 | GtkWidget* |
618 | gtk_info_bar_add_button (GtkInfoBar *info_bar, |
619 | const char *button_text, |
620 | int response_id) |
621 | { |
622 | GtkWidget *button; |
623 | |
624 | g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), NULL); |
625 | g_return_val_if_fail (button_text != NULL, NULL); |
626 | |
627 | button = gtk_button_new_with_label (label: button_text); |
628 | gtk_button_set_use_underline (GTK_BUTTON (button), TRUE); |
629 | |
630 | gtk_widget_show (widget: button); |
631 | |
632 | gtk_info_bar_add_action_widget (info_bar, child: button, response_id); |
633 | |
634 | return button; |
635 | } |
636 | |
637 | static void |
638 | add_buttons_valist (GtkInfoBar *info_bar, |
639 | const char *first_button_text, |
640 | va_list args) |
641 | { |
642 | const char * text; |
643 | int response_id; |
644 | |
645 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
646 | |
647 | if (first_button_text == NULL) |
648 | return; |
649 | |
650 | text = first_button_text; |
651 | response_id = va_arg (args, int); |
652 | |
653 | while (text != NULL) |
654 | { |
655 | gtk_info_bar_add_button (info_bar, button_text: text, response_id); |
656 | |
657 | text = va_arg (args, char *); |
658 | if (text == NULL) |
659 | break; |
660 | |
661 | response_id = va_arg (args, int); |
662 | } |
663 | } |
664 | |
665 | /** |
666 | * gtk_info_bar_add_buttons: |
667 | * @info_bar: a `GtkInfoBar` |
668 | * @first_button_text: button text |
669 | * @...: response ID for first button, then more text-response_id pairs, |
670 | * ending with %NULL |
671 | * |
672 | * Adds multiple buttons. |
673 | * |
674 | * This is the same as calling [method@Gtk.InfoBar.add_button] |
675 | * repeatedly. The variable argument list should be %NULL-terminated |
676 | * as with [ctor@Gtk.InfoBar.new_with_buttons]. Each button must have both |
677 | * text and response ID. |
678 | */ |
679 | void |
680 | gtk_info_bar_add_buttons (GtkInfoBar *info_bar, |
681 | const char *first_button_text, |
682 | ...) |
683 | { |
684 | va_list args; |
685 | |
686 | va_start (args, first_button_text); |
687 | add_buttons_valist (info_bar, first_button_text, args); |
688 | va_end (args); |
689 | } |
690 | |
691 | /** |
692 | * gtk_info_bar_new: |
693 | * |
694 | * Creates a new `GtkInfoBar` object. |
695 | * |
696 | * Returns: a new `GtkInfoBar` object |
697 | */ |
698 | GtkWidget * |
699 | gtk_info_bar_new (void) |
700 | { |
701 | return g_object_new (GTK_TYPE_INFO_BAR, NULL); |
702 | } |
703 | |
704 | /** |
705 | * gtk_info_bar_new_with_buttons: |
706 | * @first_button_text: (nullable): ext to go in first button |
707 | * @...: response ID for first button, then additional buttons, ending |
708 | * with %NULL |
709 | * |
710 | * Creates a new `GtkInfoBar` with buttons. |
711 | * |
712 | * Button text/response ID pairs should be listed, with a %NULL pointer |
713 | * ending the list. A response ID can be any positive number, |
714 | * or one of the values in the `GtkResponseType` enumeration. If the |
715 | * user clicks one of these dialog buttons, GtkInfoBar will emit |
716 | * the [signal@Gtk.InfoBar::response] signal with the corresponding |
717 | * response ID. |
718 | * |
719 | * Returns: a new `GtkInfoBar` |
720 | */ |
721 | GtkWidget* |
722 | gtk_info_bar_new_with_buttons (const char *first_button_text, |
723 | ...) |
724 | { |
725 | GtkInfoBar *info_bar; |
726 | va_list args; |
727 | |
728 | info_bar = GTK_INFO_BAR (gtk_info_bar_new ()); |
729 | |
730 | va_start (args, first_button_text); |
731 | add_buttons_valist (info_bar, first_button_text, args); |
732 | va_end (args); |
733 | |
734 | return GTK_WIDGET (info_bar); |
735 | } |
736 | |
737 | static void |
738 | update_default_response (GtkInfoBar *info_bar, |
739 | int response_id, |
740 | gboolean sensitive) |
741 | { |
742 | info_bar->default_response = response_id; |
743 | info_bar->default_response_sensitive = sensitive; |
744 | |
745 | if (response_id && sensitive) |
746 | gtk_widget_add_css_class (GTK_WIDGET (info_bar), css_class: "action" ); |
747 | else |
748 | gtk_widget_remove_css_class (GTK_WIDGET (info_bar), css_class: "action" ); |
749 | } |
750 | |
751 | /** |
752 | * gtk_info_bar_set_response_sensitive: |
753 | * @info_bar: a `GtkInfoBar` |
754 | * @response_id: a response ID |
755 | * @setting: TRUE for sensitive |
756 | * |
757 | * Sets the sensitivity of action widgets for @response_id. |
758 | * |
759 | * Calls `gtk_widget_set_sensitive (widget, setting)` for each |
760 | * widget in the info bars’s action area with the given @response_id. |
761 | * A convenient way to sensitize/desensitize buttons. |
762 | */ |
763 | void |
764 | gtk_info_bar_set_response_sensitive (GtkInfoBar *info_bar, |
765 | int response_id, |
766 | gboolean setting) |
767 | { |
768 | GtkWidget *child; |
769 | |
770 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
771 | |
772 | for (child = gtk_widget_get_first_child (widget: info_bar->action_area); |
773 | child != NULL; |
774 | child = gtk_widget_get_next_sibling (widget: child)) |
775 | { |
776 | ResponseData *rd = get_response_data (widget: child, FALSE); |
777 | |
778 | if (rd && rd->response_id == response_id) |
779 | gtk_widget_set_sensitive (widget: child, sensitive: setting); |
780 | } |
781 | |
782 | if (response_id == info_bar->default_response) |
783 | update_default_response (info_bar, response_id, sensitive: setting); |
784 | } |
785 | |
786 | /** |
787 | * gtk_info_bar_set_default_response: |
788 | * @info_bar: a `GtkInfoBar` |
789 | * @response_id: a response ID |
790 | * |
791 | * Sets the last widget in the info bar’s action area with |
792 | * the given response_id as the default widget for the dialog. |
793 | * |
794 | * Pressing “Enter” normally activates the default widget. |
795 | * |
796 | * Note that this function currently requires @info_bar to |
797 | * be added to a widget hierarchy. |
798 | */ |
799 | void |
800 | gtk_info_bar_set_default_response (GtkInfoBar *info_bar, |
801 | int response_id) |
802 | { |
803 | GtkWidget *child; |
804 | GtkWidget *window; |
805 | gboolean sensitive = TRUE; |
806 | |
807 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
808 | |
809 | window = gtk_widget_get_ancestor (GTK_WIDGET (info_bar), GTK_TYPE_WINDOW); |
810 | |
811 | for (child = gtk_widget_get_first_child (widget: info_bar->action_area); |
812 | child != NULL; |
813 | child = gtk_widget_get_next_sibling (widget: child)) |
814 | { |
815 | ResponseData *rd = get_response_data (widget: child, FALSE); |
816 | |
817 | if (rd && rd->response_id == response_id) |
818 | { |
819 | gtk_window_set_default_widget (GTK_WINDOW (window), default_widget: child); |
820 | sensitive = gtk_widget_get_sensitive (widget: child); |
821 | break; |
822 | } |
823 | } |
824 | |
825 | update_default_response (info_bar, response_id, sensitive); |
826 | } |
827 | |
828 | /** |
829 | * gtk_info_bar_response: |
830 | * @info_bar: a `GtkInfoBar` |
831 | * @response_id: a response ID |
832 | * |
833 | * Emits the “response” signal with the given @response_id. |
834 | */ |
835 | void |
836 | gtk_info_bar_response (GtkInfoBar *info_bar, |
837 | int response_id) |
838 | { |
839 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
840 | |
841 | g_signal_emit (instance: info_bar, signal_id: signals[RESPONSE], detail: 0, response_id); |
842 | } |
843 | |
844 | typedef struct |
845 | { |
846 | char *name; |
847 | int response_id; |
848 | int line; |
849 | int col; |
850 | } ActionWidgetInfo; |
851 | |
852 | typedef struct |
853 | { |
854 | GtkInfoBar *info_bar; |
855 | GtkBuilder *builder; |
856 | GSList *items; |
857 | int response_id; |
858 | gboolean is_text; |
859 | GString *string; |
860 | int line; |
861 | int col; |
862 | } SubParserData; |
863 | |
864 | static void |
865 | action_widget_info_free (gpointer data) |
866 | { |
867 | ActionWidgetInfo *item = data; |
868 | |
869 | g_free (mem: item->name); |
870 | g_free (mem: item); |
871 | } |
872 | |
873 | static void |
874 | parser_start_element (GtkBuildableParseContext *context, |
875 | const char *element_name, |
876 | const char **names, |
877 | const char **values, |
878 | gpointer user_data, |
879 | GError **error) |
880 | { |
881 | SubParserData *data = (SubParserData*)user_data; |
882 | |
883 | if (strcmp (s1: element_name, s2: "action-widget" ) == 0) |
884 | { |
885 | const char *response; |
886 | GValue gvalue = G_VALUE_INIT; |
887 | |
888 | if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "action-widgets" , error)) |
889 | return; |
890 | |
891 | if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error, |
892 | first_type: G_MARKUP_COLLECT_STRING, first_attr: "response" , &response, |
893 | G_MARKUP_COLLECT_INVALID)) |
894 | { |
895 | _gtk_builder_prefix_error (builder: data->builder, context, error); |
896 | return; |
897 | } |
898 | |
899 | if (!gtk_builder_value_from_string_type (builder: data->builder, type: GTK_TYPE_RESPONSE_TYPE, string: response, value: &gvalue, error)) |
900 | { |
901 | _gtk_builder_prefix_error (builder: data->builder, context, error); |
902 | return; |
903 | } |
904 | |
905 | data->response_id = g_value_get_enum (value: &gvalue); |
906 | data->is_text = TRUE; |
907 | g_string_set_size (string: data->string, len: 0); |
908 | gtk_buildable_parse_context_get_position (context, line_number: &data->line, char_number: &data->col); |
909 | } |
910 | else if (strcmp (s1: element_name, s2: "action-widgets" ) == 0) |
911 | { |
912 | if (!_gtk_builder_check_parent (builder: data->builder, context, parent_name: "object" , error)) |
913 | return; |
914 | |
915 | if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error, |
916 | first_type: G_MARKUP_COLLECT_INVALID, NULL, NULL, |
917 | G_MARKUP_COLLECT_INVALID)) |
918 | _gtk_builder_prefix_error (builder: data->builder, context, error); |
919 | } |
920 | else |
921 | { |
922 | _gtk_builder_error_unhandled_tag (builder: data->builder, context, |
923 | object: "GtkInfoBar" , element_name, |
924 | error); |
925 | } |
926 | } |
927 | |
928 | static void |
929 | parser_text_element (GtkBuildableParseContext *context, |
930 | const char *text, |
931 | gsize text_len, |
932 | gpointer user_data, |
933 | GError **error) |
934 | { |
935 | SubParserData *data = (SubParserData*)user_data; |
936 | |
937 | if (data->is_text) |
938 | g_string_append_len (string: data->string, val: text, len: text_len); |
939 | } |
940 | |
941 | static void |
942 | parser_end_element (GtkBuildableParseContext *context, |
943 | const char *element_name, |
944 | gpointer user_data, |
945 | GError **error) |
946 | { |
947 | SubParserData *data = (SubParserData*)user_data; |
948 | |
949 | if (data->is_text) |
950 | { |
951 | ActionWidgetInfo *item; |
952 | |
953 | item = g_new (ActionWidgetInfo, 1); |
954 | item->name = g_strdup (str: data->string->str); |
955 | item->response_id = data->response_id; |
956 | item->line = data->line; |
957 | item->col = data->col; |
958 | |
959 | data->items = g_slist_prepend (list: data->items, data: item); |
960 | data->is_text = FALSE; |
961 | } |
962 | } |
963 | |
964 | static const GtkBuildableParser sub_parser = |
965 | { |
966 | parser_start_element, |
967 | parser_end_element, |
968 | parser_text_element, |
969 | }; |
970 | |
971 | gboolean |
972 | gtk_info_bar_buildable_custom_tag_start (GtkBuildable *buildable, |
973 | GtkBuilder *builder, |
974 | GObject *child, |
975 | const char *tagname, |
976 | GtkBuildableParser *parser, |
977 | gpointer *parser_data) |
978 | { |
979 | SubParserData *data; |
980 | |
981 | if (parent_buildable_iface->custom_tag_start (buildable, builder, child, |
982 | tagname, parser, parser_data)) |
983 | return TRUE; |
984 | |
985 | if (!child && strcmp (s1: tagname, s2: "action-widgets" ) == 0) |
986 | { |
987 | data = g_slice_new0 (SubParserData); |
988 | data->info_bar = GTK_INFO_BAR (buildable); |
989 | data->builder = builder; |
990 | data->string = g_string_new (init: "" ); |
991 | data->items = NULL; |
992 | |
993 | *parser = sub_parser; |
994 | *parser_data = data; |
995 | return TRUE; |
996 | } |
997 | |
998 | return FALSE; |
999 | } |
1000 | |
1001 | static void |
1002 | gtk_info_bar_buildable_custom_finished (GtkBuildable *buildable, |
1003 | GtkBuilder *builder, |
1004 | GObject *child, |
1005 | const char *tagname, |
1006 | gpointer user_data) |
1007 | { |
1008 | GtkInfoBar *info_bar = GTK_INFO_BAR (buildable); |
1009 | GSList *l; |
1010 | SubParserData *data; |
1011 | GObject *object; |
1012 | ResponseData *ad; |
1013 | guint signal_id; |
1014 | |
1015 | if (strcmp (s1: tagname, s2: "action-widgets" )) |
1016 | { |
1017 | parent_buildable_iface->custom_finished (buildable, builder, child, |
1018 | tagname, user_data); |
1019 | return; |
1020 | } |
1021 | |
1022 | data = (SubParserData*)user_data; |
1023 | data->items = g_slist_reverse (list: data->items); |
1024 | |
1025 | for (l = data->items; l; l = l->next) |
1026 | { |
1027 | ActionWidgetInfo *item = l->data; |
1028 | |
1029 | object = _gtk_builder_lookup_object (builder, name: item->name, line: item->line, col: item->col); |
1030 | if (!object) |
1031 | continue; |
1032 | |
1033 | ad = get_response_data (GTK_WIDGET (object), TRUE); |
1034 | ad->response_id = item->response_id; |
1035 | |
1036 | if (GTK_IS_BUTTON (object)) |
1037 | signal_id = g_signal_lookup (name: "clicked" , GTK_TYPE_BUTTON); |
1038 | else |
1039 | signal_id = gtk_widget_class_get_activate_signal (GTK_WIDGET_GET_CLASS (object)); |
1040 | |
1041 | if (signal_id) |
1042 | { |
1043 | GClosure *closure; |
1044 | |
1045 | closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated), |
1046 | G_OBJECT (info_bar)); |
1047 | g_signal_connect_closure_by_id (instance: object, signal_id, detail: 0, closure, FALSE); |
1048 | } |
1049 | } |
1050 | |
1051 | g_slist_free_full (list: data->items, free_func: action_widget_info_free); |
1052 | g_string_free (string: data->string, TRUE); |
1053 | g_slice_free (SubParserData, data); |
1054 | } |
1055 | |
1056 | static void |
1057 | gtk_info_bar_buildable_add_child (GtkBuildable *buildable, |
1058 | GtkBuilder *builder, |
1059 | GObject *child, |
1060 | const char *type) |
1061 | { |
1062 | GtkInfoBar *info_bar = GTK_INFO_BAR (buildable); |
1063 | |
1064 | if (!type && GTK_IS_WIDGET (child)) |
1065 | gtk_info_bar_add_child (GTK_INFO_BAR (info_bar), GTK_WIDGET (child)); |
1066 | else if (g_strcmp0 (str1: type, str2: "action" ) == 0) |
1067 | gtk_box_append (GTK_BOX (info_bar->action_area), GTK_WIDGET (child)); |
1068 | else |
1069 | parent_buildable_iface->add_child (buildable, builder, child, type); |
1070 | } |
1071 | |
1072 | /** |
1073 | * gtk_info_bar_set_message_type: (attributes org.gtk.Method.set_property=message-type) |
1074 | * @info_bar: a `GtkInfoBar` |
1075 | * @message_type: a `GtkMessageType` |
1076 | * |
1077 | * Sets the message type of the message area. |
1078 | * |
1079 | * GTK uses this type to determine how the message is displayed. |
1080 | */ |
1081 | void |
1082 | gtk_info_bar_set_message_type (GtkInfoBar *info_bar, |
1083 | GtkMessageType message_type) |
1084 | { |
1085 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
1086 | |
1087 | if (info_bar->message_type != message_type) |
1088 | { |
1089 | const char *type_class[] = { |
1090 | "info" , |
1091 | "warning" , |
1092 | "question" , |
1093 | "error" , |
1094 | NULL |
1095 | }; |
1096 | |
1097 | if (type_class[info_bar->message_type]) |
1098 | gtk_widget_remove_css_class (GTK_WIDGET (info_bar), css_class: type_class[info_bar->message_type]); |
1099 | |
1100 | info_bar->message_type = message_type; |
1101 | |
1102 | gtk_widget_queue_draw (GTK_WIDGET (info_bar)); |
1103 | |
1104 | if (type_class[info_bar->message_type]) |
1105 | gtk_widget_add_css_class (GTK_WIDGET (info_bar), css_class: type_class[info_bar->message_type]); |
1106 | |
1107 | g_object_notify_by_pspec (G_OBJECT (info_bar), pspec: props[PROP_MESSAGE_TYPE]); |
1108 | } |
1109 | } |
1110 | |
1111 | /** |
1112 | * gtk_info_bar_get_message_type: (attributes org.gtk.Method.get_property=message-type) |
1113 | * @info_bar: a `GtkInfoBar` |
1114 | * |
1115 | * Returns the message type of the message area. |
1116 | * |
1117 | * Returns: the message type of the message area. |
1118 | */ |
1119 | GtkMessageType |
1120 | gtk_info_bar_get_message_type (GtkInfoBar *info_bar) |
1121 | { |
1122 | g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), GTK_MESSAGE_OTHER); |
1123 | |
1124 | return info_bar->message_type; |
1125 | } |
1126 | |
1127 | |
1128 | /** |
1129 | * gtk_info_bar_set_show_close_button: (attributes org.gtk.Method.set_property=show-close-button) |
1130 | * @info_bar: a `GtkInfoBar` |
1131 | * @setting: %TRUE to include a close button |
1132 | * |
1133 | * If true, a standard close button is shown. |
1134 | * |
1135 | * When clicked it emits the response %GTK_RESPONSE_CLOSE. |
1136 | */ |
1137 | void |
1138 | gtk_info_bar_set_show_close_button (GtkInfoBar *info_bar, |
1139 | gboolean setting) |
1140 | { |
1141 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
1142 | |
1143 | if (setting == gtk_info_bar_get_show_close_button (info_bar)) |
1144 | return; |
1145 | |
1146 | gtk_widget_set_visible (widget: info_bar->close_button, visible: setting); |
1147 | g_object_notify_by_pspec (G_OBJECT (info_bar), pspec: props[PROP_SHOW_CLOSE_BUTTON]); |
1148 | } |
1149 | |
1150 | /** |
1151 | * gtk_info_bar_get_show_close_button: (attributes org.gtk.Method.get_property=show-close-button) |
1152 | * @info_bar: a `GtkInfoBar` |
1153 | * |
1154 | * Returns whether the widget will display a standard close button. |
1155 | * |
1156 | * Returns: %TRUE if the widget displays standard close button |
1157 | */ |
1158 | gboolean |
1159 | gtk_info_bar_get_show_close_button (GtkInfoBar *info_bar) |
1160 | { |
1161 | g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), FALSE); |
1162 | |
1163 | return gtk_widget_get_visible (widget: info_bar->close_button); |
1164 | } |
1165 | |
1166 | /** |
1167 | * gtk_info_bar_set_revealed: (attributes org.gtk.Method.set_property=revealed) |
1168 | * @info_bar: a `GtkInfoBar` |
1169 | * @revealed: The new value of the property |
1170 | * |
1171 | * Sets whether the `GtkInfoBar` is revealed. |
1172 | * |
1173 | * Changing this will make @info_bar reveal or conceal |
1174 | * itself via a sliding transition. |
1175 | * |
1176 | * Note: this does not show or hide @info_bar in the |
1177 | * [property@Gtk.Widget:visible] sense, so revealing has no effect |
1178 | * if [property@Gtk.Widget:visible] is %FALSE. |
1179 | */ |
1180 | void |
1181 | gtk_info_bar_set_revealed (GtkInfoBar *info_bar, |
1182 | gboolean revealed) |
1183 | { |
1184 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
1185 | |
1186 | if (revealed == gtk_revealer_get_reveal_child (GTK_REVEALER (info_bar->revealer))) |
1187 | return; |
1188 | |
1189 | gtk_revealer_set_reveal_child (GTK_REVEALER (info_bar->revealer), reveal_child: revealed); |
1190 | g_object_notify_by_pspec (G_OBJECT (info_bar), pspec: props[PROP_REVEALED]); |
1191 | } |
1192 | |
1193 | /** |
1194 | * gtk_info_bar_get_revealed: (attributes org.gtk.Method.get_property=revealed) |
1195 | * @info_bar: a `GtkInfoBar` |
1196 | * |
1197 | * Returns whether the info bar is currently revealed. |
1198 | * |
1199 | * Returns: the current value of the [property@Gtk.InfoBar:revealed] property |
1200 | */ |
1201 | gboolean |
1202 | gtk_info_bar_get_revealed (GtkInfoBar *info_bar) |
1203 | { |
1204 | g_return_val_if_fail (GTK_IS_INFO_BAR (info_bar), FALSE); |
1205 | |
1206 | return gtk_revealer_get_reveal_child (GTK_REVEALER (info_bar->revealer)); |
1207 | } |
1208 | |
1209 | /** |
1210 | * gtk_info_bar_add_child: |
1211 | * @info_bar: a `GtkInfoBar` |
1212 | * @widget: the child to be added |
1213 | * |
1214 | * Adds a widget to the content area of the info bar. |
1215 | */ |
1216 | void |
1217 | gtk_info_bar_add_child (GtkInfoBar *info_bar, |
1218 | GtkWidget *widget) |
1219 | { |
1220 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
1221 | g_return_if_fail (GTK_IS_WIDGET (widget)); |
1222 | |
1223 | gtk_box_append (GTK_BOX (info_bar->content_area), child: widget); |
1224 | } |
1225 | |
1226 | /** |
1227 | * gtk_info_bar_remove_child: |
1228 | * @info_bar: a `GtkInfoBar` |
1229 | * @widget: a child that has been added to the content area |
1230 | * |
1231 | * Removes a widget from the content area of the info bar. |
1232 | */ |
1233 | void |
1234 | gtk_info_bar_remove_child (GtkInfoBar *info_bar, |
1235 | GtkWidget *widget) |
1236 | { |
1237 | g_return_if_fail (GTK_IS_INFO_BAR (info_bar)); |
1238 | g_return_if_fail (GTK_IS_WIDGET (widget)); |
1239 | |
1240 | gtk_box_remove (GTK_BOX (info_bar->content_area), child: widget); |
1241 | } |
1242 | |