1 /*
2 * e-alert-bar.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include <glib/gi18n-lib.h>
21
22 #include "e-dialog-widgets.h"
23 #include "e-alert-dialog.h"
24 #include "e-alert-bar.h"
25
26 /* The GtkScrolledWindow has some minimum height of 86 pixels or so, but some
27 messages can be smaller, thus subclass it here and override the minimum value. */
28
29 typedef struct _EScrolledWindow {
30 GtkScrolledWindow parent;
31 } EScrolledWindow;
32
33 typedef struct _EScrolledWindowClass {
34 GtkScrolledWindowClass parent_class;
35 } EScrolledWindowClass;
36
37 GType e_scrolled_window_get_type (void) G_GNUC_CONST;
38
G_DEFINE_TYPE(EScrolledWindow,e_scrolled_window,GTK_TYPE_SCROLLED_WINDOW)39 G_DEFINE_TYPE (EScrolledWindow, e_scrolled_window, GTK_TYPE_SCROLLED_WINDOW)
40
41 static void
42 e_scrolled_window_get_preferred_height_for_width (GtkWidget *widget,
43 gint width,
44 gint *minimum_size,
45 gint *natural_size)
46 {
47 GtkScrolledWindow *scrolled_window = GTK_SCROLLED_WINDOW (widget);
48 GtkWidget *child;
49 gint min_height, max_height;
50
51 /* Chain up to parent's method. */
52 GTK_WIDGET_CLASS (e_scrolled_window_parent_class)->get_preferred_height_for_width (widget, width, minimum_size, natural_size);
53
54 min_height = gtk_scrolled_window_get_min_content_height (scrolled_window);
55 max_height = gtk_scrolled_window_get_max_content_height (scrolled_window);
56
57 if (min_height > 0 && min_height < *minimum_size)
58 *minimum_size = min_height + 2;
59
60 if (max_height > 0 && max_height < *natural_size)
61 *natural_size = max_height + 2;
62
63 child = gtk_bin_get_child (GTK_BIN (widget));
64
65 if (child && width > 1) {
66 gint child_min_height = -1, child_natural_height = -1;
67
68 gtk_widget_get_preferred_height_for_width (child, width, &child_min_height, &child_natural_height);
69
70 if (*minimum_size > child_min_height && child_min_height > 0)
71 *minimum_size = child_min_height + 2;
72
73 if (*natural_size > child_natural_height && child_natural_height > 0)
74 *natural_size = child_natural_height + 2;
75 }
76 }
77
78 static GtkSizeRequestMode
e_scrolled_window_get_request_mode(GtkWidget * widget)79 e_scrolled_window_get_request_mode (GtkWidget *widget)
80 {
81 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
82 }
83
84 static void
e_scrolled_window_class_init(EScrolledWindowClass * class)85 e_scrolled_window_class_init (EScrolledWindowClass *class)
86 {
87 GtkWidgetClass *widget_class;
88
89 widget_class = GTK_WIDGET_CLASS (class);
90 widget_class->get_preferred_height_for_width = e_scrolled_window_get_preferred_height_for_width;
91 widget_class->get_request_mode = e_scrolled_window_get_request_mode;
92 }
93
94 static void
e_scrolled_window_init(EScrolledWindow * scrolled_window)95 e_scrolled_window_init (EScrolledWindow *scrolled_window)
96 {
97 }
98
99 static GtkWidget *
e_scrolled_window_new(void)100 e_scrolled_window_new (void)
101 {
102 return g_object_new (e_scrolled_window_get_type (),
103 "hadjustment", NULL,
104 "vadjustment", NULL,
105 NULL);
106 }
107
108 #define E_ALERT_BAR_GET_PRIVATE(obj) \
109 (G_TYPE_INSTANCE_GET_PRIVATE \
110 ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
111
112 /* GTK_ICON_SIZE_DIALOG is a tad too big. */
113 #define ICON_SIZE GTK_ICON_SIZE_DND
114
115 /* Dismiss warnings automatically after 5 minutes. */
116 #define WARNING_TIMEOUT_SECONDS (5 * 60)
117
118 /* Maximum height of the text message; after that value the message is scrolled. */
119 #define MAX_HEIGHT 200
120
121 struct _EAlertBarPrivate {
122 GQueue alerts;
123 GtkWidget *image; /* not referenced */
124 GtkWidget *scrolled_window; /* not referenced */
125 GtkWidget *message_label; /* not referenced */
126 gint max_content_height;
127 };
128
G_DEFINE_TYPE(EAlertBar,e_alert_bar,GTK_TYPE_INFO_BAR)129 G_DEFINE_TYPE (EAlertBar, e_alert_bar, GTK_TYPE_INFO_BAR)
130
131 static void
132 alert_bar_response_close (EAlert *alert)
133 {
134 e_alert_response (alert, GTK_RESPONSE_CLOSE);
135 }
136
137 static void
alert_bar_show_alert(EAlertBar * alert_bar)138 alert_bar_show_alert (EAlertBar *alert_bar)
139 {
140 GtkImage *image;
141 GtkInfoBar *info_bar;
142 GtkWidget *action_area;
143 GtkWidget *widget;
144 EAlert *alert;
145 GList *link;
146 GList *children;
147 GtkMessageType message_type;
148 const gchar *primary_text;
149 const gchar *secondary_text;
150 const gchar *icon_name;
151 gboolean have_primary_text;
152 gboolean have_secondary_text;
153 gboolean visible;
154 gint response_id;
155 gchar *markup;
156
157 info_bar = GTK_INFO_BAR (alert_bar);
158 action_area = gtk_info_bar_get_action_area (info_bar);
159
160 alert = g_queue_peek_head (&alert_bar->priv->alerts);
161 g_return_if_fail (E_IS_ALERT (alert));
162
163 /* Remove all buttons from the previous alert. */
164 children = gtk_container_get_children (GTK_CONTAINER (action_area));
165 while (children != NULL) {
166 GtkWidget *child = GTK_WIDGET (children->data);
167 gtk_container_remove (GTK_CONTAINER (action_area), child);
168 children = g_list_delete_link (children, children);
169 }
170
171 /* Add alert-specific buttons. */
172 link = e_alert_peek_actions (alert);
173 while (link != NULL) {
174 GtkAction *action = GTK_ACTION (link->data);
175
176 /* These actions are already wired to trigger an
177 * EAlert::response signal when activated, which
178 * will in turn call gtk_info_bar_response(), so
179 * we can add buttons directly to the action
180 * area without knowning their response IDs. */
181
182 widget = gtk_button_new ();
183
184 gtk_activatable_set_related_action (GTK_ACTIVATABLE (widget), action);
185 gtk_box_pack_end (GTK_BOX (action_area), widget, FALSE, FALSE, 0);
186
187 e_alert_update_destructive_action_style (action, widget);
188
189 link = g_list_next (link);
190 }
191
192 link = e_alert_peek_widgets (alert);
193 while (link != NULL) {
194 widget = link->data;
195
196 gtk_box_pack_end (GTK_BOX (action_area), widget, FALSE, FALSE, 0);
197 link = g_list_next (link);
198 }
199
200 /* Add a dismiss button. */
201 widget = e_dialog_button_new_with_icon ("window-close", NULL);
202 gtk_button_set_relief (
203 GTK_BUTTON (widget), GTK_RELIEF_NONE);
204 gtk_widget_set_tooltip_text (
205 /* Translators: Escape is a keyboard binding. */
206 widget, _("Close this message (Escape)"));
207 gtk_box_pack_end (
208 GTK_BOX (action_area), widget, FALSE, FALSE, 0);
209 gtk_button_box_set_child_non_homogeneous (
210 GTK_BUTTON_BOX (action_area), widget, TRUE);
211 gtk_widget_show (widget);
212
213 g_signal_connect_swapped (
214 widget, "clicked",
215 G_CALLBACK (alert_bar_response_close), alert);
216
217 widget = gtk_widget_get_toplevel (GTK_WIDGET (alert_bar));
218
219 gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW (alert_bar->priv->scrolled_window), -1);
220
221 if (widget) {
222 gint max_height;
223
224 /* Allow up to 20% of the window height being used by the alert, or at least MAX_HEIGHT pixels */
225 max_height = MAX (gtk_widget_get_allocated_height (widget) / 5, MAX_HEIGHT);
226
227 alert_bar->priv->max_content_height = max_height;
228
229 gtk_scrolled_window_set_max_content_height (GTK_SCROLLED_WINDOW (alert_bar->priv->scrolled_window), max_height);
230 }
231
232 primary_text = e_alert_get_primary_text (alert);
233 secondary_text = e_alert_get_secondary_text (alert);
234
235 if (primary_text == NULL)
236 primary_text = "";
237
238 if (secondary_text == NULL)
239 secondary_text = "";
240
241 have_primary_text = (*primary_text != '\0');
242 have_secondary_text = (*secondary_text != '\0');
243
244 response_id = e_alert_get_default_response (alert);
245 gtk_info_bar_set_default_response (info_bar, response_id);
246
247 message_type = e_alert_get_message_type (alert);
248 gtk_info_bar_set_message_type (info_bar, message_type);
249
250 if (have_primary_text && have_secondary_text)
251 markup = g_markup_printf_escaped ("<b>%s</b>\n\n<small>%s</small>", primary_text, secondary_text);
252 else if (have_primary_text)
253 markup = g_markup_escape_text (primary_text, -1);
254 else
255 markup = g_markup_escape_text (secondary_text, -1);
256 gtk_label_set_markup (GTK_LABEL (alert_bar->priv->message_label), markup);
257 g_free (markup);
258
259 icon_name = e_alert_get_icon_name (alert);
260 image = GTK_IMAGE (alert_bar->priv->image);
261 gtk_image_set_from_icon_name (image, icon_name, ICON_SIZE);
262
263 /* Avoid showing an image for empty alerts. */
264 visible = have_primary_text || have_secondary_text;
265 gtk_widget_set_visible (alert_bar->priv->image, visible);
266
267 gtk_widget_show (GTK_WIDGET (alert_bar));
268
269 /* Warnings are generally meant for transient errors.
270 * No need to leave them up indefinitely. Close them
271 * automatically if the user hasn't responded after a
272 * reasonable period of time has elapsed. */
273 if (message_type == GTK_MESSAGE_WARNING)
274 e_alert_start_timer (alert, WARNING_TIMEOUT_SECONDS);
275 }
276
277 static void
alert_bar_response_cb(EAlert * alert,gint response_id,EAlertBar * alert_bar)278 alert_bar_response_cb (EAlert *alert,
279 gint response_id,
280 EAlertBar *alert_bar)
281 {
282 GQueue *queue;
283 EAlert *head;
284 gboolean was_head;
285
286 queue = &alert_bar->priv->alerts;
287 head = g_queue_peek_head (queue);
288 was_head = (alert == head);
289
290 g_signal_handlers_disconnect_by_func (
291 alert, alert_bar_response_cb, alert_bar);
292
293 if (g_queue_remove (queue, alert))
294 g_object_unref (alert);
295
296 if (g_queue_is_empty (queue)) {
297 GtkWidget *action_area;
298 GList *children;
299
300 gtk_widget_hide (GTK_WIDGET (alert_bar));
301
302 action_area = gtk_info_bar_get_action_area (GTK_INFO_BAR (alert_bar));
303
304 /* Remove all buttons from the previous alert. */
305 children = gtk_container_get_children (GTK_CONTAINER (action_area));
306 while (children != NULL) {
307 GtkWidget *child = GTK_WIDGET (children->data);
308 gtk_container_remove (GTK_CONTAINER (action_area), child);
309 children = g_list_delete_link (children, children);
310 }
311 } else if (was_head) {
312 GtkInfoBar *info_bar = GTK_INFO_BAR (alert_bar);
313 gtk_info_bar_response (info_bar, response_id);
314 alert_bar_show_alert (alert_bar);
315 }
316 }
317
318 static void
alert_bar_message_label_size_allocate_cb(GtkWidget * message_label,GdkRectangle * allocation,gpointer user_data)319 alert_bar_message_label_size_allocate_cb (GtkWidget *message_label,
320 GdkRectangle *allocation,
321 gpointer user_data)
322 {
323 GtkScrolledWindow *scrolled_window;
324 EAlertBar *alert_bar = user_data;
325 gint max_height, use_height;
326
327 g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
328 g_return_if_fail (allocation != NULL);
329
330 scrolled_window = GTK_SCROLLED_WINDOW (alert_bar->priv->scrolled_window);
331
332 max_height = alert_bar->priv->max_content_height;
333
334 if (allocation->height > 0 && allocation->height <= max_height)
335 use_height = allocation->height;
336 else if (allocation->height <= 0)
337 use_height = -1;
338 else
339 use_height = max_height;
340
341 /* To avoid runtime warnings about min being larger than the new max */
342 gtk_scrolled_window_set_min_content_height (scrolled_window, -1);
343
344 if (use_height > 0 && use_height < max_height)
345 gtk_scrolled_window_set_max_content_height (scrolled_window, use_height);
346 else
347 gtk_scrolled_window_set_max_content_height (scrolled_window, max_height);
348
349 gtk_scrolled_window_set_min_content_height (scrolled_window, use_height);
350
351 gtk_widget_queue_resize (alert_bar->priv->scrolled_window);
352 }
353
354 static void
alert_bar_dispose(GObject * object)355 alert_bar_dispose (GObject *object)
356 {
357 EAlertBarPrivate *priv;
358
359 priv = E_ALERT_BAR_GET_PRIVATE (object);
360
361 if (priv->message_label) {
362 g_signal_handlers_disconnect_by_func (priv->message_label,
363 G_CALLBACK (alert_bar_message_label_size_allocate_cb), object);
364 priv->message_label = NULL;
365 }
366
367 while (!g_queue_is_empty (&priv->alerts)) {
368 EAlert *alert = g_queue_pop_head (&priv->alerts);
369 g_signal_handlers_disconnect_by_func (
370 alert, alert_bar_response_cb, object);
371 g_object_unref (alert);
372 }
373
374 /* Chain up to parent's dispose() method. */
375 G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object);
376 }
377
378 static void
alert_bar_add_css_style(GtkWidget * widget,const gchar * css)379 alert_bar_add_css_style (GtkWidget *widget,
380 const gchar *css)
381 {
382 GtkCssProvider *provider;
383 GError *error = NULL;
384
385 provider = gtk_css_provider_new ();
386
387 if (gtk_css_provider_load_from_data (provider, css, -1, &error)) {
388 GtkStyleContext *style_context;
389
390 style_context = gtk_widget_get_style_context (widget);
391
392 gtk_style_context_add_provider (style_context, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
393 } else {
394 g_warning ("%s: Failed to parse CSS for %s: %s", G_STRFUNC, G_OBJECT_TYPE_NAME (widget), error ? error->message : "Unknown error");
395 }
396
397 g_clear_object (&provider);
398 g_clear_error (&error);
399 }
400
401 static void
alert_bar_constructed(GObject * object)402 alert_bar_constructed (GObject *object)
403 {
404 EAlertBarPrivate *priv;
405 GtkInfoBar *info_bar;
406 GtkWidget *action_area;
407 GtkWidget *content_area;
408 GtkWidget *container;
409 GtkWidget *widget;
410 GObject *revealer;
411
412 priv = E_ALERT_BAR_GET_PRIVATE (object);
413
414 /* Chain up to parent's constructed() method. */
415 G_OBJECT_CLASS (e_alert_bar_parent_class)->constructed (object);
416
417 g_queue_init (&priv->alerts);
418
419 info_bar = GTK_INFO_BAR (object);
420 action_area = gtk_info_bar_get_action_area (info_bar);
421 content_area = gtk_info_bar_get_content_area (info_bar);
422
423 gtk_orientable_set_orientation (
424 GTK_ORIENTABLE (action_area), GTK_ORIENTATION_HORIZONTAL);
425 gtk_widget_set_valign (action_area, GTK_ALIGN_START);
426
427 container = content_area;
428
429 widget = gtk_image_new ();
430 gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.0);
431 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
432 priv->image = widget;
433 gtk_widget_show (widget);
434
435 widget = e_scrolled_window_new ();
436 g_object_set (G_OBJECT (widget),
437 "valign", GTK_ALIGN_CENTER,
438 "halign", GTK_ALIGN_FILL,
439 "hexpand", TRUE,
440 "hscrollbar-policy", GTK_POLICY_NEVER,
441 "vscrollbar-policy", GTK_POLICY_AUTOMATIC,
442 NULL);
443 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
444 priv->scrolled_window = widget;
445 gtk_widget_show (widget);
446
447 container = widget;
448
449 widget = gtk_label_new (NULL);
450 gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
451 gtk_label_set_line_wrap_mode (GTK_LABEL (widget), PANGO_WRAP_WORD_CHAR);
452 gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
453 gtk_label_set_width_chars (GTK_LABEL (widget), 20);
454 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
455 gtk_widget_set_valign (widget, GTK_ALIGN_CENTER);
456 gtk_container_add (GTK_CONTAINER (container), widget);
457 priv->message_label = widget;
458 gtk_widget_show (widget);
459
460 g_signal_connect (priv->message_label, "size-allocate",
461 G_CALLBACK (alert_bar_message_label_size_allocate_cb), object);
462
463 widget = gtk_bin_get_child (GTK_BIN (container));
464
465 if (GTK_IS_VIEWPORT (widget)) {
466 gtk_viewport_set_shadow_type (GTK_VIEWPORT (widget), GTK_SHADOW_NONE);
467
468 alert_bar_add_css_style (widget, "viewport { background: none; border: none; }");
469 }
470
471 alert_bar_add_css_style (container, "scrolledwindow { background: none; border: none; }");
472
473 /* Disable animation of the revealer, until GtkInfoBar's bug #710888 is fixed */
474 revealer = gtk_widget_get_template_child (GTK_WIDGET (object), GTK_TYPE_INFO_BAR, "revealer");
475 if (revealer) {
476 gtk_revealer_set_transition_type (GTK_REVEALER (revealer), GTK_REVEALER_TRANSITION_TYPE_NONE);
477 gtk_revealer_set_transition_duration (GTK_REVEALER (revealer), 0);
478 }
479 }
480
481 static GtkSizeRequestMode
alert_bar_get_request_mode(GtkWidget * widget)482 alert_bar_get_request_mode (GtkWidget *widget)
483 {
484 /* GtkBox does width-for-height by default. But we
485 * want the alert bar to be as short as possible. */
486 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
487 }
488
489 static void
alert_bar_close(GtkInfoBar * info_bar)490 alert_bar_close (GtkInfoBar *info_bar)
491 {
492 /* GtkInfoBar's close() method looks for a button with a response
493 * code of GTK_RESPONSE_CANCEL. But that does not apply here, so
494 * we have to override the method. */
495 e_alert_bar_close_alert (E_ALERT_BAR (info_bar));
496 }
497
498 static void
e_alert_bar_class_init(EAlertBarClass * class)499 e_alert_bar_class_init (EAlertBarClass *class)
500 {
501 GObjectClass *object_class;
502 GtkWidgetClass *widget_class;
503 GtkInfoBarClass *info_bar_class;
504
505 g_type_class_add_private (class, sizeof (EAlertBarPrivate));
506
507 object_class = G_OBJECT_CLASS (class);
508 object_class->dispose = alert_bar_dispose;
509 object_class->constructed = alert_bar_constructed;
510
511 widget_class = GTK_WIDGET_CLASS (class);
512 widget_class->get_request_mode = alert_bar_get_request_mode;
513
514 info_bar_class = GTK_INFO_BAR_CLASS (class);
515 info_bar_class->close = alert_bar_close;
516 }
517
518 static void
e_alert_bar_init(EAlertBar * alert_bar)519 e_alert_bar_init (EAlertBar *alert_bar)
520 {
521 alert_bar->priv = E_ALERT_BAR_GET_PRIVATE (alert_bar);
522 alert_bar->priv->max_content_height = MAX_HEIGHT;
523 }
524
525 GtkWidget *
e_alert_bar_new(void)526 e_alert_bar_new (void)
527 {
528 return g_object_new (E_TYPE_ALERT_BAR, NULL);
529 }
530
531 void
e_alert_bar_clear(EAlertBar * alert_bar)532 e_alert_bar_clear (EAlertBar *alert_bar)
533 {
534 GQueue *queue;
535 EAlert *alert;
536
537 g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
538
539 queue = &alert_bar->priv->alerts;
540
541 while ((alert = g_queue_pop_head (queue)) != NULL)
542 alert_bar_response_close (alert);
543 }
544
545 typedef struct {
546 gboolean found;
547 EAlert *looking_for;
548 } DuplicateData;
549
550 static void
alert_bar_find_duplicate_cb(EAlert * alert,DuplicateData * dd)551 alert_bar_find_duplicate_cb (EAlert *alert,
552 DuplicateData *dd)
553 {
554 g_return_if_fail (dd->looking_for != NULL);
555
556 dd->found |= (
557 e_alert_get_message_type (alert) ==
558 e_alert_get_message_type (dd->looking_for) &&
559 g_strcmp0 (
560 e_alert_get_primary_text (alert),
561 e_alert_get_primary_text (dd->looking_for)) == 0 &&
562 g_strcmp0 (
563 e_alert_get_secondary_text (alert),
564 e_alert_get_secondary_text (dd->looking_for)) == 0);
565 }
566
567 void
e_alert_bar_add_alert(EAlertBar * alert_bar,EAlert * alert)568 e_alert_bar_add_alert (EAlertBar *alert_bar,
569 EAlert *alert)
570 {
571 DuplicateData dd;
572
573 g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
574 g_return_if_fail (E_IS_ALERT (alert));
575
576 dd.found = FALSE;
577 dd.looking_for = alert;
578
579 g_queue_foreach (
580 &alert_bar->priv->alerts,
581 (GFunc) alert_bar_find_duplicate_cb, &dd);
582
583 if (dd.found)
584 return;
585
586 g_signal_connect (
587 alert, "response",
588 G_CALLBACK (alert_bar_response_cb), alert_bar);
589
590 g_queue_push_head (&alert_bar->priv->alerts, g_object_ref (alert));
591
592 alert_bar_show_alert (alert_bar);
593 }
594
595 /**
596 * e_alert_bar_close_alert:
597 * @alert_bar: an #EAlertBar
598 *
599 * Closes the active #EAlert and returns %TRUE, or else returns %FALSE if
600 * there is no active #EAlert.
601 *
602 * Returns: whether an #EAlert was closed
603 **/
604 gboolean
e_alert_bar_close_alert(EAlertBar * alert_bar)605 e_alert_bar_close_alert (EAlertBar *alert_bar)
606 {
607 EAlert *alert;
608 gboolean alert_closed = FALSE;
609
610 g_return_val_if_fail (E_IS_ALERT_BAR (alert_bar), FALSE);
611
612 alert = g_queue_peek_head (&alert_bar->priv->alerts);
613
614 if (alert != NULL) {
615 alert_bar_response_close (alert);
616 alert_closed = TRUE;
617 }
618
619 return alert_closed;
620 }
621
622 /**
623 * e_alert_bar_submit_alert:
624 * @alert_bar: an #EAlertBar
625 * @alert: an #EAlert
626 *
627 * Depending on the @alert type either shows a dialog or adds
628 * the alert into the @alert_bar. This is meant to be used
629 * by #EAlertSink implementations which use the #EAlertBar.
630 *
631 * Since: 3.26
632 **/
633 void
e_alert_bar_submit_alert(EAlertBar * alert_bar,EAlert * alert)634 e_alert_bar_submit_alert (EAlertBar *alert_bar,
635 EAlert *alert)
636 {
637 GtkWidget *toplevel;
638 GtkWidget *widget;
639 GtkWindow *parent;
640
641 g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
642 g_return_if_fail (E_IS_ALERT (alert));
643
644 switch (e_alert_get_message_type (alert)) {
645 case GTK_MESSAGE_INFO:
646 case GTK_MESSAGE_WARNING:
647 case GTK_MESSAGE_QUESTION:
648 case GTK_MESSAGE_ERROR:
649 e_alert_bar_add_alert (alert_bar, alert);
650 break;
651
652 default:
653 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (alert_bar));
654 if (GTK_IS_WINDOW (toplevel))
655 parent = GTK_WINDOW (toplevel);
656 else
657 parent = NULL;
658 widget = e_alert_dialog_new (parent, alert);
659 gtk_dialog_run (GTK_DIALOG (widget));
660 gtk_widget_destroy (widget);
661 }
662 }
663