No issues found
1 /*
2 * e-activity-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 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include "e-activity-bar.h"
24
25 #define E_ACTIVITY_BAR_GET_PRIVATE(obj) \
26 (G_TYPE_INSTANCE_GET_PRIVATE \
27 ((obj), E_TYPE_ACTIVITY_BAR, EActivityBarPrivate))
28
29 #define FEEDBACK_PERIOD 1 /* seconds */
30 #define COMPLETED_ICON_NAME "emblem-default"
31
32 struct _EActivityBarPrivate {
33 EActivity *activity; /* weak reference */
34 GtkWidget *image; /* not referenced */
35 GtkWidget *label; /* not referenced */
36 GtkWidget *cancel; /* not referenced */
37 GtkWidget *spinner; /* not referenced */
38
39 /* If the user clicks the Cancel button, keep the cancelled
40 * EActivity object alive for a short duration so the user
41 * gets some visual feedback that cancellation worked. */
42 guint timeout_id;
43 };
44
45 enum {
46 PROP_0,
47 PROP_ACTIVITY
48 };
49
50 G_DEFINE_TYPE (
51 EActivityBar,
52 e_activity_bar,
53 GTK_TYPE_INFO_BAR)
54
55 static void
56 activity_bar_feedback (EActivityBar *bar)
57 {
58 EActivity *activity;
59 EActivityState state;
60
61 activity = e_activity_bar_get_activity (bar);
62 g_return_if_fail (E_IS_ACTIVITY (activity));
63
64 state = e_activity_get_state (activity);
65 if (state != E_ACTIVITY_CANCELLED && state != E_ACTIVITY_COMPLETED)
66 return;
67
68 if (bar->priv->timeout_id > 0)
69 g_source_remove (bar->priv->timeout_id);
70
71 /* Hold a reference on the EActivity for a short
72 * period so the activity bar stays visible. */
73 bar->priv->timeout_id = g_timeout_add_seconds_full (
74 G_PRIORITY_LOW, FEEDBACK_PERIOD, (GSourceFunc) gtk_false,
75 g_object_ref (activity), (GDestroyNotify) g_object_unref);
76 }
77
78 static void
79 activity_bar_update (EActivityBar *bar)
80 {
81 EActivity *activity;
82 EActivityState state;
83 GCancellable *cancellable;
84 const gchar *icon_name;
85 gboolean sensitive;
86 gboolean visible;
87 gchar *description;
88
89 activity = e_activity_bar_get_activity (bar);
90
91 if (activity == NULL) {
92 gtk_widget_hide (GTK_WIDGET (bar));
93 return;
94 }
95
96 cancellable = e_activity_get_cancellable (activity);
97 icon_name = e_activity_get_icon_name (activity);
98 state = e_activity_get_state (activity);
99
100 description = e_activity_describe (activity);
101 gtk_label_set_text (GTK_LABEL (bar->priv->label), description);
102
103 if (state == E_ACTIVITY_CANCELLED) {
104 PangoAttribute *attr;
105 PangoAttrList *attr_list;
106
107 attr_list = pango_attr_list_new ();
108
109 attr = pango_attr_strikethrough_new (TRUE);
110 pango_attr_list_insert (attr_list, attr);
111
112 gtk_label_set_attributes (
113 GTK_LABEL (bar->priv->label), attr_list);
114
115 pango_attr_list_unref (attr_list);
116 } else
117 gtk_label_set_attributes (
118 GTK_LABEL (bar->priv->label), NULL);
119
120 if (state == E_ACTIVITY_COMPLETED)
121 icon_name = COMPLETED_ICON_NAME;
122
123 if (state == E_ACTIVITY_CANCELLED) {
124 gtk_image_set_from_stock (
125 GTK_IMAGE (bar->priv->image),
126 GTK_STOCK_CANCEL, GTK_ICON_SIZE_BUTTON);
127 gtk_widget_show (bar->priv->image);
128 } else if (icon_name != NULL) {
129 gtk_image_set_from_icon_name (
130 GTK_IMAGE (bar->priv->image),
131 icon_name, GTK_ICON_SIZE_BUTTON);
132 gtk_widget_show (bar->priv->image);
133 } else {
134 gtk_widget_hide (bar->priv->image);
135 }
136
137 visible = (cancellable != NULL);
138 gtk_widget_set_visible (bar->priv->cancel, visible);
139
140 sensitive = (state == E_ACTIVITY_RUNNING);
141 gtk_widget_set_sensitive (bar->priv->cancel, sensitive);
142
143 visible = (description != NULL && *description != '\0');
144 gtk_widget_set_visible (GTK_WIDGET (bar), visible);
145
146 g_free (description);
147 }
148
149 static void
150 activity_bar_cancel (EActivityBar *bar)
151 {
152 EActivity *activity;
153 GCancellable *cancellable;
154
155 activity = e_activity_bar_get_activity (bar);
156 g_return_if_fail (E_IS_ACTIVITY (activity));
157
158 cancellable = e_activity_get_cancellable (activity);
159 g_cancellable_cancel (cancellable);
160
161 activity_bar_update (bar);
162 }
163
164 static void
165 activity_bar_weak_notify_cb (EActivityBar *bar,
166 GObject *where_the_object_was)
167 {
168 g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
169
170 bar->priv->activity = NULL;
171 e_activity_bar_set_activity (bar, NULL);
172 }
173
174 static void
175 activity_bar_set_property (GObject *object,
176 guint property_id,
177 const GValue *value,
178 GParamSpec *pspec)
179 {
180 switch (property_id) {
181 case PROP_ACTIVITY:
182 e_activity_bar_set_activity (
183 E_ACTIVITY_BAR (object),
184 g_value_get_object (value));
185 return;
186 }
187
188 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
189 }
190
191 static void
192 activity_bar_get_property (GObject *object,
193 guint property_id,
194 GValue *value,
195 GParamSpec *pspec)
196 {
197 switch (property_id) {
198 case PROP_ACTIVITY:
199 g_value_set_object (
200 value, e_activity_bar_get_activity (
201 E_ACTIVITY_BAR (object)));
202 return;
203 }
204
205 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
206 }
207
208 static void
209 activity_bar_dispose (GObject *object)
210 {
211 EActivityBarPrivate *priv;
212
213 priv = E_ACTIVITY_BAR_GET_PRIVATE (object);
214
215 if (priv->timeout_id > 0) {
216 g_source_remove (priv->timeout_id);
217 priv->timeout_id = 0;
218 }
219
220 if (priv->activity != NULL) {
221 g_signal_handlers_disconnect_matched (
222 priv->activity, G_SIGNAL_MATCH_DATA,
223 0, 0, NULL, NULL, object);
224 g_object_weak_unref (
225 G_OBJECT (priv->activity), (GWeakNotify)
226 activity_bar_weak_notify_cb, object);
227 priv->activity = NULL;
228 }
229
230 /* Chain up to parent's dispose() method. */
231 G_OBJECT_CLASS (e_activity_bar_parent_class)->dispose (object);
232 }
233
234 static void
235 e_activity_bar_class_init (EActivityBarClass *class)
236 {
237 GObjectClass *object_class;
238
239 g_type_class_add_private (class, sizeof (EActivityBarPrivate));
240
241 object_class = G_OBJECT_CLASS (class);
242 object_class->set_property = activity_bar_set_property;
243 object_class->get_property = activity_bar_get_property;
244 object_class->dispose = activity_bar_dispose;
245
246 g_object_class_install_property (
247 object_class,
248 PROP_ACTIVITY,
249 g_param_spec_object (
250 "activity",
251 NULL,
252 NULL,
253 E_TYPE_ACTIVITY,
254 G_PARAM_READWRITE |
255 G_PARAM_CONSTRUCT));
256 }
257
258 static void
259 e_activity_bar_init (EActivityBar *bar)
260 {
261 GtkWidget *container;
262 GtkWidget *widget;
263
264 bar->priv = E_ACTIVITY_BAR_GET_PRIVATE (bar);
265
266 container = gtk_info_bar_get_content_area (GTK_INFO_BAR (bar));
267
268 widget = gtk_hbox_new (FALSE, 12);
269 gtk_container_add (GTK_CONTAINER (container), widget);
270 gtk_widget_show (widget);
271
272 container = widget;
273
274 widget = gtk_image_new ();
275 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
276 bar->priv->image = widget;
277
278 widget = gtk_spinner_new ();
279 gtk_spinner_start (GTK_SPINNER (widget));
280 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
281 bar->priv->spinner = widget;
282
283 /* The spinner is only visible when the image is not. */
284 g_object_bind_property (
285 bar->priv->image, "visible",
286 bar->priv->spinner, "visible",
287 G_BINDING_BIDIRECTIONAL |
288 G_BINDING_SYNC_CREATE |
289 G_BINDING_INVERT_BOOLEAN);
290
291 widget = gtk_label_new (NULL);
292 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
293 gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
294 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
295 bar->priv->label = widget;
296 gtk_widget_show (widget);
297
298 /* This is only shown if the EActivity has a GCancellable. */
299 widget = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
300 gtk_info_bar_add_action_widget (
301 GTK_INFO_BAR (bar), widget, GTK_RESPONSE_CANCEL);
302 bar->priv->cancel = widget;
303 gtk_widget_hide (widget);
304
305 g_signal_connect_swapped (
306 widget, "clicked",
307 G_CALLBACK (activity_bar_cancel), bar);
308 }
309
310 GtkWidget *
311 e_activity_bar_new (void)
312 {
313 return g_object_new (E_TYPE_ACTIVITY_BAR, NULL);
314 }
315
316 EActivity *
317 e_activity_bar_get_activity (EActivityBar *bar)
318 {
319 g_return_val_if_fail (E_IS_ACTIVITY_BAR (bar), NULL);
320
321 return bar->priv->activity;
322 }
323
324 void
325 e_activity_bar_set_activity (EActivityBar *bar,
326 EActivity *activity)
327 {
328 g_return_if_fail (E_IS_ACTIVITY_BAR (bar));
329
330 if (activity != NULL)
331 g_return_if_fail (E_IS_ACTIVITY (activity));
332
333 if (bar->priv->timeout_id > 0) {
334 g_source_remove (bar->priv->timeout_id);
335 bar->priv->timeout_id = 0;
336 }
337
338 if (bar->priv->activity != NULL) {
339 g_signal_handlers_disconnect_matched (
340 bar->priv->activity, G_SIGNAL_MATCH_DATA,
341 0, 0, NULL, NULL, bar);
342 g_object_weak_unref (
343 G_OBJECT (bar->priv->activity),
344 (GWeakNotify) activity_bar_weak_notify_cb, bar);
345 }
346
347 bar->priv->activity = activity;
348
349 if (activity != NULL) {
350 g_object_weak_ref (
351 G_OBJECT (activity), (GWeakNotify)
352 activity_bar_weak_notify_cb, bar);
353
354 g_signal_connect_swapped (
355 activity, "notify::state",
356 G_CALLBACK (activity_bar_feedback), bar);
357
358 g_signal_connect_swapped (
359 activity, "notify",
360 G_CALLBACK (activity_bar_update), bar);
361 }
362
363 activity_bar_update (bar);
364
365 g_object_notify (G_OBJECT (bar), "activity");
366 }