Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
e-alert-bar.c:289:2 | clang-analyzer | Value stored to 'container' is never read | ||
e-alert-bar.c:289:2 | clang-analyzer | Value stored to 'container' is never read |
1 /*
2 * e-alert-bar.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 */
18
19 #include "e-alert-bar.h"
20
21 #include <config.h>
22 #include <glib/gi18n-lib.h>
23
24 #define E_ALERT_BAR_GET_PRIVATE(obj) \
25 (G_TYPE_INSTANCE_GET_PRIVATE \
26 ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
27
28 #define E_ALERT_BAR_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE \
30 ((obj), E_TYPE_ALERT_BAR, EAlertBarPrivate))
31
32 /* GTK_ICON_SIZE_DIALOG is a tad too big. */
33 #define ICON_SIZE GTK_ICON_SIZE_DND
34
35 /* Dismiss warnings automatically after 5 minutes. */
36 #define WARNING_TIMEOUT_SECONDS (5 * 60)
37
38 struct _EAlertBarPrivate {
39 GQueue alerts;
40 GtkWidget *image; /* not referenced */
41 GtkWidget *primary_label; /* not referenced */
42 GtkWidget *secondary_label; /* not referenced */
43 };
44
45 G_DEFINE_TYPE (
46 EAlertBar,
47 e_alert_bar,
48 GTK_TYPE_INFO_BAR)
49
50 static void
51 alert_bar_response_close (EAlert *alert)
52 {
53 e_alert_response (alert, GTK_RESPONSE_CLOSE);
54 }
55
56 static void
57 alert_bar_show_alert (EAlertBar *alert_bar)
58 {
59 GtkImage *image;
60 GtkInfoBar *info_bar;
61 GtkWidget *action_area;
62 GtkWidget *widget;
63 EAlert *alert;
64 GList *actions;
65 GList *children;
66 GtkMessageType message_type;
67 const gchar *primary_text;
68 const gchar *secondary_text;
69 const gchar *stock_id;
70 gboolean have_primary_text;
71 gboolean have_secondary_text;
72 gboolean visible;
73 gint response_id;
74 gchar *markup;
75
76 info_bar = GTK_INFO_BAR (alert_bar);
77 action_area = gtk_info_bar_get_action_area (info_bar);
78
79 alert = g_queue_peek_head (&alert_bar->priv->alerts);
80 g_return_if_fail (E_IS_ALERT (alert));
81
82 /* Remove all buttons from the previous alert. */
83 children = gtk_container_get_children (GTK_CONTAINER (action_area));
84 while (children != NULL) {
85 GtkWidget *child = GTK_WIDGET (children->data);
86 gtk_container_remove (GTK_CONTAINER (action_area), child);
87 children = g_list_delete_link (children, children);
88 }
89
90 /* Add alert-specific buttons. */
91 actions = e_alert_peek_actions (alert);
92 while (actions != NULL) {
93 /* These actions are already wired to trigger an
94 * EAlert::response signal when activated, which
95 * will in turn call gtk_info_bar_response(), so
96 * we can add buttons directly to the action
97 * area without knowning their response IDs. */
98
99 widget = gtk_button_new ();
100
101 gtk_activatable_set_related_action (
102 GTK_ACTIVATABLE (widget),
103 GTK_ACTION (actions->data));
104
105 gtk_box_pack_end (
106 GTK_BOX (action_area), widget, FALSE, FALSE, 0);
107
108 actions = g_list_next (actions);
109 }
110
111 /* Add a dismiss button. */
112 widget = gtk_button_new ();
113 gtk_button_set_image (
114 GTK_BUTTON (widget),
115 gtk_image_new_from_stock (
116 GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
117 gtk_button_set_relief (
118 GTK_BUTTON (widget), GTK_RELIEF_NONE);
119 gtk_widget_set_tooltip_text (
120 widget, _("Close this message"));
121 gtk_box_pack_end (
122 GTK_BOX (action_area), widget, FALSE, FALSE, 0);
123 gtk_button_box_set_child_non_homogeneous (
124 GTK_BUTTON_BOX (action_area), widget, TRUE);
125 gtk_widget_show (widget);
126
127 g_signal_connect_swapped (
128 widget, "clicked",
129 G_CALLBACK (alert_bar_response_close), alert);
130
131 primary_text = e_alert_get_primary_text (alert);
132 secondary_text = e_alert_get_secondary_text (alert);
133
134 if (primary_text == NULL)
135 primary_text = "";
136
137 if (secondary_text == NULL)
138 secondary_text = "";
139
140 have_primary_text = (*primary_text != '\0');
141 have_secondary_text = (*secondary_text != '\0');
142
143 response_id = e_alert_get_default_response (alert);
144 gtk_info_bar_set_default_response (info_bar, response_id);
145
146 message_type = e_alert_get_message_type (alert);
147 gtk_info_bar_set_message_type (info_bar, message_type);
148
149 widget = alert_bar->priv->primary_label;
150 if (have_primary_text && have_secondary_text)
151 markup = g_markup_printf_escaped (
152 "<b>%s</b>", primary_text);
153 else
154 markup = g_markup_escape_text (primary_text, -1);
155 gtk_label_set_markup (GTK_LABEL (widget), markup);
156 gtk_widget_set_visible (widget, have_primary_text);
157 g_free (markup);
158
159 widget = alert_bar->priv->secondary_label;
160 if (have_primary_text && have_secondary_text)
161 markup = g_markup_printf_escaped (
162 "<small>%s</small>", secondary_text);
163 else
164 markup = g_markup_escape_text (secondary_text, -1);
165 gtk_label_set_markup (GTK_LABEL (widget), markup);
166 gtk_widget_set_visible (widget, have_secondary_text);
167 g_free (markup);
168
169 stock_id = e_alert_get_stock_id (alert);
170 image = GTK_IMAGE (alert_bar->priv->image);
171 gtk_image_set_from_stock (image, stock_id, ICON_SIZE);
172
173 /* Avoid showing an image for one-line alerts,
174 * which are usually questions or informational. */
175 visible = have_primary_text && have_secondary_text;
176 gtk_widget_set_visible (alert_bar->priv->image, visible);
177
178 gtk_widget_show (GTK_WIDGET (alert_bar));
179
180 /* Warnings are generally meant for transient errors.
181 * No need to leave them up indefinitely. Close them
182 * automatically if the user hasn't responded after a
183 * reasonable period of time has elapsed. */
184 if (message_type == GTK_MESSAGE_WARNING)
185 e_alert_start_timer (alert, WARNING_TIMEOUT_SECONDS);
186 }
187
188 static void
189 alert_bar_response_cb (EAlert *alert,
190 gint response_id,
191 EAlertBar *alert_bar)
192 {
193 GQueue *queue;
194 EAlert *head;
195 gboolean was_head;
196
197 queue = &alert_bar->priv->alerts;
198 head = g_queue_peek_head (queue);
199 was_head = (alert == head);
200
201 g_signal_handlers_disconnect_by_func (
202 alert, alert_bar_response_cb, alert_bar);
203
204 if (g_queue_remove (queue, alert))
205 g_object_unref (alert);
206
207 if (g_queue_is_empty (queue))
208 gtk_widget_hide (GTK_WIDGET (alert_bar));
209 else if (was_head) {
210 GtkInfoBar *info_bar = GTK_INFO_BAR (alert_bar);
211 gtk_info_bar_response (info_bar, response_id);
212 alert_bar_show_alert (alert_bar);
213 }
214 }
215
216 static void
217 alert_bar_dispose (GObject *object)
218 {
219 EAlertBarPrivate *priv;
220
221 priv = E_ALERT_BAR_GET_PRIVATE (object);
222
223 while (!g_queue_is_empty (&priv->alerts)) {
224 EAlert *alert = g_queue_pop_head (&priv->alerts);
225 g_signal_handlers_disconnect_by_func (
226 alert, alert_bar_response_cb, object);
227 g_object_unref (alert);
228 }
229
230 /* Chain up to parent's dispose() method. */
231 G_OBJECT_CLASS (e_alert_bar_parent_class)->dispose (object);
232 }
233
234 static void
235 alert_bar_constructed (GObject *object)
236 {
237 EAlertBarPrivate *priv;
238 GtkInfoBar *info_bar;
239 GtkWidget *action_area;
240 GtkWidget *content_area;
241 GtkWidget *container;
242 GtkWidget *widget;
243
244 priv = E_ALERT_BAR_GET_PRIVATE (object);
245
246 /* Chain up to parent's constructed() method. */
247 G_OBJECT_CLASS (e_alert_bar_parent_class)->constructed (object);
248
249 g_queue_init (&priv->alerts);
250
251 info_bar = GTK_INFO_BAR (object);
252 action_area = gtk_info_bar_get_action_area (info_bar);
253 content_area = gtk_info_bar_get_content_area (info_bar);
254
255 gtk_orientable_set_orientation (
256 GTK_ORIENTABLE (action_area), GTK_ORIENTATION_HORIZONTAL);
257 gtk_widget_set_valign (action_area, GTK_ALIGN_START);
258
259 container = content_area;
260
261 widget = gtk_image_new ();
262 gtk_misc_set_alignment (GTK_MISC (widget), 0.5, 0.0);
263 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
264 priv->image = widget;
265 gtk_widget_show (widget);
266
267 widget = gtk_vbox_new (FALSE, 12);
268 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
269 gtk_widget_show (widget);
270
271 container = widget;
272
273 widget = gtk_label_new (NULL);
274 gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
275 gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
276 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
277 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
278 priv->primary_label = widget;
279 gtk_widget_show (widget);
280
281 widget = gtk_label_new (NULL);
282 gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
283 gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
284 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
285 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
286 priv->secondary_label = widget;
287 gtk_widget_show (widget);
288
289 container = action_area;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
290 }
291
292 static GtkSizeRequestMode
293 alert_bar_get_request_mode (GtkWidget *widget)
294 {
295 /* GtkHBox does width-for-height by default. But we
296 * want the alert bar to be as short as possible. */
297 return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
298 }
299
300 static void
301 e_alert_bar_class_init (EAlertBarClass *class)
302 {
303 GObjectClass *object_class;
304 GtkWidgetClass *widget_class;
305
306 g_type_class_add_private (class, sizeof (EAlertBarPrivate));
307
308 object_class = G_OBJECT_CLASS (class);
309 object_class->dispose = alert_bar_dispose;
310 object_class->constructed = alert_bar_constructed;
311
312 widget_class = GTK_WIDGET_CLASS (class);
313 widget_class->get_request_mode = alert_bar_get_request_mode;
314 }
315
316 static void
317 e_alert_bar_init (EAlertBar *alert_bar)
318 {
319 alert_bar->priv = E_ALERT_BAR_GET_PRIVATE (alert_bar);
320 }
321
322 GtkWidget *
323 e_alert_bar_new (void)
324 {
325 return g_object_new (E_TYPE_ALERT_BAR, NULL);
326 }
327
328 void
329 e_alert_bar_clear (EAlertBar *alert_bar)
330 {
331 GQueue *queue;
332 EAlert *alert;
333
334 g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
335
336 queue = &alert_bar->priv->alerts;
337
338 while ((alert = g_queue_pop_head (queue)) != NULL)
339 alert_bar_response_close (alert);
340 }
341
342 typedef struct {
343 gboolean found;
344 EAlert *looking_for;
345 } DuplicateData;
346
347 static void
348 alert_bar_find_duplicate_cb (EAlert *alert,
349 DuplicateData *dd)
350 {
351 g_return_if_fail (dd->looking_for != NULL);
352
353 dd->found |= (
354 e_alert_get_message_type (alert) ==
355 e_alert_get_message_type (dd->looking_for) &&
356 g_strcmp0 (
357 e_alert_get_primary_text (alert),
358 e_alert_get_primary_text (dd->looking_for)) == 0 &&
359 g_strcmp0 (
360 e_alert_get_secondary_text (alert),
361 e_alert_get_secondary_text (dd->looking_for)) == 0);
362 }
363
364 void
365 e_alert_bar_add_alert (EAlertBar *alert_bar,
366 EAlert *alert)
367 {
368 DuplicateData dd;
369
370 g_return_if_fail (E_IS_ALERT_BAR (alert_bar));
371 g_return_if_fail (E_IS_ALERT (alert));
372
373 dd.found = FALSE;
374 dd.looking_for = alert;
375
376 g_queue_foreach (
377 &alert_bar->priv->alerts,
378 (GFunc) alert_bar_find_duplicate_cb, &dd);
379
380 if (dd.found)
381 return;
382
383 g_signal_connect (
384 alert, "response",
385 G_CALLBACK (alert_bar_response_cb), alert_bar);
386
387 g_queue_push_head (&alert_bar->priv->alerts, g_object_ref (alert));
388
389 alert_bar_show_alert (alert_bar);
390 }