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