1 /*
2 * Seahorse
3 *
4 * Copyright (C) 2004 Stefan Walter
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "seahorse-progress.h"
20 #include "seahorse-widget.h"
21
22 /* -----------------------------------------------------------------------------
23 * GENERIC PROGRESS BAR HANDLING
24 */
25
26 /**
27 * SECTION:seahorse-progress
28 * @short_description: Progress bar handling
29 *
30 **/
31
32 static void disconnect_progress (SeahorseWidget *widget, SeahorseOperation *op);
33
34 /**
35 * progress: The progress bar to pulse
36 *
37 * Called to pulse the progress bar when we're in pulse mode
38 *
39 * Returns TRUE if the bar pulsed
40 **/
41 static gboolean
pulse_timer(GtkProgressBar * progress)42 pulse_timer (GtkProgressBar *progress)
43 {
44 g_assert (GTK_IS_PROGRESS_BAR (progress));
45
46 if (gtk_progress_bar_get_pulse_step (progress) != 0) {
47 gtk_progress_bar_pulse (progress);
48 return TRUE;
49 }
50
51 return FALSE;
52 }
53
54 /**
55 * progress: The progress bar to init
56 *
57 * Inits the progress bar and sets a timer function.
58 *
59 **/
60 static void
start_pulse(GtkProgressBar * progress)61 start_pulse (GtkProgressBar *progress)
62 {
63 guint stag;
64
65 gtk_progress_bar_set_pulse_step (progress, 0.05);
66 gtk_progress_bar_pulse (progress);
67
68 stag = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (progress), "pulse-timer"));
69 if (stag == 0) {
70 /* Start a pulse timer */
71 stag = g_timeout_add (100, (GSourceFunc)pulse_timer, progress);
72 g_object_set_data_full (G_OBJECT (progress), "pulse-timer",
73 GINT_TO_POINTER (stag), (GDestroyNotify)g_source_remove);
74 }
75 }
76
77 /**
78 * progress: The progress bar
79 *
80 * Stops progress bar pulsing
81 *
82 **/
83 static void
stop_pulse(GtkProgressBar * progress)84 stop_pulse (GtkProgressBar *progress)
85 {
86 gtk_progress_bar_set_pulse_step (progress, 0.0);
87
88 /* This causes the destroy callback to be called */
89 g_object_set_data (G_OBJECT (progress), "pulse-timer", NULL);
90 }
91
92
93 /**
94 * operation: The operation to process
95 * message: The message that will be pushed to the status bar
96 * fract: The fraction of the progress bar to fill
97 * swidget: The SeahorseWidget to extract the gtk widgets from
98 *
99 * This gets called whenever an operation updates it's progress status thingy.
100 * We update the appbar as appropriate. If operation != NULL then we only
101 * display the latest operation in our special list
102 *
103 **/
104 static void
operation_progress(SeahorseOperation * operation,const gchar * message,gdouble fract,SeahorseWidget * swidget)105 operation_progress (SeahorseOperation *operation, const gchar *message,
106 gdouble fract, SeahorseWidget *swidget)
107 {
108 GtkProgressBar *progress;
109 GtkStatusbar *status;
110 guint id;
111
112 progress = GTK_PROGRESS_BAR (seahorse_widget_get_widget (swidget, "progress"));
113 status = GTK_STATUSBAR (seahorse_widget_get_widget (swidget, "status"));
114
115 if (!seahorse_operation_is_running (operation))
116 fract = 0.0;
117
118 if (message != NULL && status) {
119 g_return_if_fail (GTK_IS_STATUSBAR (status));
120 id = gtk_statusbar_get_context_id (status, "operation-progress");
121 gtk_statusbar_pop (status, id);
122 if (message[0])
123 gtk_statusbar_push (status, id, message);
124 }
125
126 if(progress) {
127 g_return_if_fail (GTK_IS_PROGRESS_BAR (progress));
128 if (fract >= 0.0) {
129 stop_pulse (progress);
130 gtk_progress_bar_set_fraction (progress, fract);
131 } else {
132 start_pulse (progress);
133 }
134 }
135 }
136
137 /**
138 * operation: The finished operation
139 * swidget: The SeahorseWidget to get the progress bar from
140 *
141 * Handles the progress bar display of finished operations.
142 * If there is an error in the operation, it will be displayed.
143 *
144 **/
145 static void
operation_done(SeahorseOperation * operation,SeahorseWidget * swidget)146 operation_done (SeahorseOperation *operation, SeahorseWidget *swidget)
147 {
148 GError *err = NULL;
149
150 if (!seahorse_operation_is_successful (operation)) {
151 seahorse_operation_copy_error (operation, &err);
152 if (err) {
153 operation_progress (operation, err->message, 0.0, swidget);
154 g_error_free (err);
155 }
156 } else {
157 operation_progress (operation, "", 0.0, swidget);
158 }
159
160 g_signal_handlers_disconnect_by_func (swidget, disconnect_progress, operation);
161 g_object_set_data (G_OBJECT (swidget), "operation", NULL);
162 }
163
164 /**
165 * widget: SeahorseWidget that is used for display
166 * op: SeahorseOperation to disconnect
167 *
168 * Disconnect the operation_progress and the operation_done functions from the
169 * operation
170 *
171 **/
172 static void
disconnect_progress(SeahorseWidget * widget,SeahorseOperation * op)173 disconnect_progress (SeahorseWidget *widget, SeahorseOperation *op)
174 {
175 g_signal_handlers_disconnect_by_func (op, operation_progress, widget);
176 g_signal_handlers_disconnect_by_func (op, operation_done, widget);
177 }
178
179 /**
180 * seahorse_progress_status_set_operation:
181 * @swidget: The SeahorseWidget to add the operation to
182 * @operation: The operation to add
183 *
184 * Adds the operation to the widget.
185 *
186 */
187 void
seahorse_progress_status_set_operation(SeahorseWidget * swidget,SeahorseOperation * operation)188 seahorse_progress_status_set_operation (SeahorseWidget *swidget,
189 SeahorseOperation *operation)
190 {
191 SeahorseOperation *prev;
192
193 /*
194 * Note that this is not one off, the operation is monitored until it is
195 * replaced, so if the operation starts up again the progress will be
196 * displayed
197 */
198
199 g_return_if_fail (SEAHORSE_IS_WIDGET (swidget));
200 g_return_if_fail (SEAHORSE_IS_OPERATION (operation));
201
202 prev = SEAHORSE_OPERATION (g_object_get_data (G_OBJECT (swidget), "operation"));
203 if (prev) {
204
205 /* If it's the same operation, just ignore */
206 if (prev == operation)
207 return;
208
209 /* If the previous one was a multi operation, just piggy back this one in */
210 if (SEAHORSE_IS_MULTI_OPERATION (prev)) {
211 g_object_ref (operation);
212 seahorse_multi_operation_take (SEAHORSE_MULTI_OPERATION (prev), operation);
213 return;
214 }
215
216 /* Otherwise disconnect old progress, replace with new */
217 disconnect_progress (swidget, prev);
218 }
219
220 g_object_ref (operation);
221 g_object_set_data_full (G_OBJECT (swidget), "operation", operation,
222 (GDestroyNotify)g_object_unref);
223 g_signal_connect (swidget, "destroy",
224 G_CALLBACK (disconnect_progress), operation);
225
226 if (!seahorse_operation_is_running (operation))
227 operation_done (operation, swidget);
228
229 operation_progress (operation, seahorse_operation_get_message (operation),
230 seahorse_operation_get_progress (operation), swidget);
231
232 g_signal_connect (operation, "done", G_CALLBACK (operation_done), swidget);
233 g_signal_connect (operation, "progress", G_CALLBACK (operation_progress), swidget);
234 }
235
236 /**
237 * seahorse_progress_status_get_operation:
238 * @swidget: The SeahorseWidget to extract the operation from
239 *
240 *
241 *
242 * Returns: The operation stored in the widget
243 */
244 SeahorseOperation*
seahorse_progress_status_get_operation(SeahorseWidget * swidget)245 seahorse_progress_status_get_operation (SeahorseWidget *swidget)
246 {
247 return SEAHORSE_OPERATION (g_object_get_data (G_OBJECT (swidget), "operation"));
248 }
249
250 /* -----------------------------------------------------------------------------
251 * PROGRESS WINDOWS
252 */
253
254 /**
255 * operation: The SeahorseOperation to use
256 * message: An optional message to display
257 * fract: The fraction finished
258 * swidget: the SeahorseWidget to get the widgets from
259 *
260 * Progress window update. Similar to operation_progress
261 *
262 **/
263 static void
progress_operation_update(SeahorseOperation * operation,const gchar * message,gdouble fract,SeahorseWidget * swidget)264 progress_operation_update (SeahorseOperation *operation, const gchar *message,
265 gdouble fract, SeahorseWidget *swidget)
266 {
267 GtkProgressBar *progress;
268 GtkWidget *w;
269 const gchar *t;
270
271 w = GTK_WIDGET (seahorse_widget_get_widget (swidget, "operation-details"));
272 g_return_if_fail (w != NULL);
273
274 t = seahorse_operation_get_message (operation);
275 gtk_label_set_text (GTK_LABEL (w), t ? t : "");
276
277 progress = GTK_PROGRESS_BAR (seahorse_widget_get_widget (swidget, "operation-bar"));
278 g_return_if_fail (w != NULL);
279
280 if (fract >= 0.0) {
281 stop_pulse (progress);
282 gtk_progress_bar_set_fraction (progress, fract);
283 } else {
284 start_pulse (progress);
285 }
286 }
287
288 /**
289 * on_progress_operation_cancel:
290 * @button: ignored
291 * @operation: The operation to cancel
292 *
293 * Cancels an operation
294 *
295 */
296 G_MODULE_EXPORT void
on_progress_operation_cancel(GtkButton * button,SeahorseOperation * operation)297 on_progress_operation_cancel (GtkButton *button, SeahorseOperation *operation)
298 {
299 if (seahorse_operation_is_running (operation))
300 seahorse_operation_cancel (operation);
301 }
302
303 /**
304 * operation: ignored
305 * swidget: The SeahorseWidget to destroy
306 *
307 * Cleans up after the operation is done
308 *
309 **/
310 static void
progress_operation_done(SeahorseOperation * operation,SeahorseWidget * swidget)311 progress_operation_done (SeahorseOperation *operation, SeahorseWidget *swidget)
312 {
313 seahorse_widget_destroy (swidget);
314 }
315
316 /**
317 * widget:
318 * event: ignored
319 * operation: The operation
320 *
321 * Closing the windows cancels the operation
322 *
323 * Returns TRUE
324 **/
325 static int
progress_delete_event(GtkWidget * widget,GdkEvent * event,SeahorseOperation * operation)326 progress_delete_event (GtkWidget *widget, GdkEvent *event,
327 SeahorseOperation *operation)
328 {
329 /* When window close we simulate a cancel */
330 on_progress_operation_cancel (NULL, operation);
331
332 /* Allow window to close regardless of outcome */
333 return TRUE;
334 }
335
336 /**
337 * swidget: The SeahorseWidget relevant
338 * operation: The operation to disconnect handlers from
339 *
340 * Disconnects the handlers from the functions
341 *
342 **/
343 static void
progress_destroy(SeahorseWidget * swidget,SeahorseOperation * operation)344 progress_destroy (SeahorseWidget *swidget, SeahorseOperation *operation)
345 {
346 /*
347 * Since we allow the window to close (user forces it) even when the
348 * operation is not complete, we have to take care to cleanup these
349 * signal handlers.
350 */
351 g_signal_handlers_disconnect_by_func (operation, operation_done, swidget);
352 g_signal_handlers_disconnect_by_func (operation, operation_progress, swidget);
353 }
354
355 /**
356 * operation: The operation to create a new progress window for
357 *
358 * Creates a new progress window and adds the operation to it.
359 *
360 * Returns FALSE
361 **/
362 static gboolean
progress_show(SeahorseOperation * operation)363 progress_show (SeahorseOperation *operation)
364 {
365 SeahorseWidget *swidget;
366 GtkWidget *w;
367 const gchar *title;
368 gchar *t;
369
370 if (!seahorse_operation_is_running (operation)) {
371 /* Matches the ref in seahorse_progress_show */
372 g_object_unref (operation);
373 return FALSE;
374 }
375
376 swidget = seahorse_widget_new_allow_multiple ("progress", NULL);
377 g_return_val_if_fail (swidget != NULL, FALSE);
378
379 /* Release our reference on the operation when this window is destroyed */
380 g_object_set_data_full (G_OBJECT (swidget), "operation", operation,
381 (GDestroyNotify)g_object_unref);
382
383 w = GTK_WIDGET (seahorse_widget_get_widget (swidget, swidget->name));
384 gtk_window_move (GTK_WINDOW (w), 10, 10);
385
386 /* Setup the title */
387 title = (const gchar*)g_object_get_data (G_OBJECT (operation), "progress-title");
388 if (title) {
389
390 /* The window title */
391 w = GTK_WIDGET (seahorse_widget_get_widget (swidget, swidget->name));
392 g_return_val_if_fail (w != NULL, FALSE);
393 gtk_window_set_title (GTK_WINDOW (w), title);
394
395 /* The main message title */
396 w = GTK_WIDGET (seahorse_widget_get_widget (swidget, "operation-title"));
397 g_return_val_if_fail (w != NULL, FALSE);
398 t = g_strdup_printf ("<b>%s</b>", title);
399 gtk_label_set_markup (GTK_LABEL (w), t);
400 g_free (t);
401 }
402
403 /* The details */
404 progress_operation_update (operation, NULL,
405 seahorse_operation_get_progress (operation), swidget);
406 g_signal_connect (operation, "progress",
407 G_CALLBACK (progress_operation_update), swidget);
408
409 /* Cancel events */
410 g_signal_connect (seahorse_widget_get_toplevel (swidget), "delete_event",
411 G_CALLBACK (progress_delete_event), operation);
412
413 /* Done and cleanup */
414 w = GTK_WIDGET (seahorse_widget_get_widget (swidget, swidget->name));
415 g_signal_connect (w, "destroy", G_CALLBACK (progress_destroy), operation);
416 g_signal_connect (operation, "done", G_CALLBACK (progress_operation_done), swidget);
417
418 return FALSE;
419 }
420
421 /**
422 * seahorse_progress_show:
423 * @operation: The operation to create a progress window for
424 * @title: Optional title of this window
425 * @delayed: TRUE: wait 1 second before displaying the window
426 *
427 * Displays a progress window and adds an operation to it.
428 *
429 */
430 void
seahorse_progress_show(SeahorseOperation * operation,const gchar * title,gboolean delayed)431 seahorse_progress_show (SeahorseOperation *operation, const gchar *title,
432 gboolean delayed)
433 {
434 /* Unref in the timeout callback */
435 g_object_ref (operation);
436 g_object_set_data_full (G_OBJECT (operation), "progress-title",
437 title ? g_strdup (title) : NULL, (GDestroyNotify)g_free);
438
439 /* Show the progress, after one second */
440 if (delayed)
441 g_timeout_add_seconds (1, (GSourceFunc)progress_show, operation);
442
443 /* Right away */
444 else
445 progress_show (operation);
446 }
447