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