1 /*
2 * io-context.c : Place holder for an io error context.
3 * It is intended to become a place to handle errors
4 * as well as storing non-fatal warnings.
5 *
6 * Authors:
7 * Jody Goldberg <jody@gnome.org>
8 * Zbigniew Chyla <cyba@gnome.pl>
9 *
10 * (C) 2000-2005 Jody Goldberg
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) version 3.
16 *
17 * This library is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 * USA.
26 */
27 #include <goffice/goffice-config.h>
28 #include "io-context-priv.h"
29 #include "go-cmd-context.h"
30 #include <goffice/utils/go-file.h>
31 #include <gsf/gsf-impl-utils.h>
32 #ifdef GOFFICE_WITH_GTK
33 #include <gtk/gtk.h>
34 #endif
35 #include <glib/gi18n-lib.h>
36
37 #define PROGRESS_UPDATE_STEP 0.01
38 #define PROGRESS_UPDATE_STEP_END (1.0 / 400)
39 #define PROGRESS_UPDATE_PERIOD_SEC 0.20
40
41 #define IOC_CLASS(ioc) GO_IO_CONTEXT_CLASS(G_OBJECT_GET_CLASS(ioc))
42
43 enum {
44 IOC_PROP_0,
45 IOC_PROP_EXEC_LOOP,
46 };
47
48 static void
go_io_context_init(GOIOContext * ioc)49 go_io_context_init (GOIOContext *ioc)
50 {
51 ioc->impl = NULL;
52 ioc->info = NULL;
53 ioc->error_occurred = FALSE;
54 ioc->warning_occurred = FALSE;
55
56 ioc->progress_ranges = NULL;
57 ioc->progress_min = 0.0;
58 ioc->progress_max = 1.0;
59 ioc->last_progress = -1.0;
60 ioc->last_time = 0.0;
61 ioc->helper.helper_type = GO_PROGRESS_HELPER_NONE;
62 ioc->exec_main_loop = TRUE;
63 }
64
65 static void
ioc_finalize(GObject * obj)66 ioc_finalize (GObject *obj)
67 {
68 GOIOContext *ioc;
69
70 g_return_if_fail (GO_IS_IO_CONTEXT (obj));
71
72 ioc = GO_IO_CONTEXT (obj);
73 g_slist_free_full (ioc->info, (GDestroyNotify) go_error_info_free);
74 if (ioc->impl) {
75 go_cmd_context_progress_set (ioc->impl, 0.0);
76 go_cmd_context_progress_message_set (ioc->impl, NULL);
77 g_object_unref (ioc->impl);
78 }
79
80 g_list_free_full (ioc->progress_ranges, (GFreeFunc)g_free);
81 ioc->progress_ranges = NULL;
82
83 G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
84 }
85
86 static void
io_context_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)87 io_context_set_property (GObject *obj, guint param_id,
88 GValue const *value, GParamSpec *pspec)
89 {
90 GOIOContext *ioc = GO_IO_CONTEXT (obj);
91
92 switch (param_id) {
93 case IOC_PROP_EXEC_LOOP:
94 ioc->exec_main_loop = g_value_get_boolean (value);
95 break;
96 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
97 return; /* NOTE : RETURN */
98 }
99 }
100
101 static void
io_context_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)102 io_context_get_property (GObject *obj, guint param_id,
103 GValue *value, GParamSpec *pspec)
104 {
105 GOIOContext *ioc = GO_IO_CONTEXT (obj);
106
107 switch (param_id) {
108 case IOC_PROP_EXEC_LOOP:
109 g_value_set_boolean (value, ioc->exec_main_loop);
110 break;
111 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
112 return; /* NOTE : RETURN */
113 }
114 }
115
116 static char *
ioc_get_password(GOCmdContext * cc,char const * filename)117 ioc_get_password (GOCmdContext *cc, char const *filename)
118 {
119 GOIOContext *ioc = (GOIOContext *)cc;
120 return go_cmd_context_get_password (ioc->impl, filename);
121 }
122
123 static void
ioc_set_sensitive(GOCmdContext * cc,gboolean sensitive)124 ioc_set_sensitive (GOCmdContext *cc, gboolean sensitive)
125 {
126 (void)cc; (void)sensitive;
127 }
128
129 static void
ioc_error_error(GOCmdContext * cc,GError * err)130 ioc_error_error (GOCmdContext *cc, GError *err)
131 {
132 go_io_error_string (GO_IO_CONTEXT (cc), err->message);
133 }
134
135 static void
ioc_error_go_error_info_(G_GNUC_UNUSED GOCmdContext * ctxt,GOErrorInfo * error)136 ioc_error_go_error_info_ (G_GNUC_UNUSED GOCmdContext *ctxt,
137 GOErrorInfo *error)
138 {
139 /* TODO what goes here */
140 go_error_info_print (error);
141 }
142
143 void
go_io_error_string(GOIOContext * context,const gchar * str)144 go_io_error_string (GOIOContext *context, const gchar *str)
145 {
146 GOErrorInfo *error;
147
148 g_return_if_fail (context != NULL);
149 g_return_if_fail (str != NULL);
150
151 error = go_error_info_new_str (str);
152 go_io_error_info_set (context, error);
153 }
154
155 static void
go_io_context_gnm_cmd_context_init(GOCmdContextClass * cc_class)156 go_io_context_gnm_cmd_context_init (GOCmdContextClass *cc_class)
157 {
158 cc_class->get_password = ioc_get_password;
159 cc_class->set_sensitive = ioc_set_sensitive;
160 cc_class->error.error = ioc_error_error;
161 cc_class->error.error_info = ioc_error_go_error_info_;
162 }
163
164 static void
go_io_context_class_init(GObjectClass * klass)165 go_io_context_class_init (GObjectClass *klass)
166 {
167 klass->finalize = ioc_finalize;
168 klass->set_property = io_context_set_property;
169 klass->get_property = io_context_get_property;
170 g_object_class_install_property (klass, IOC_PROP_EXEC_LOOP,
171 g_param_spec_boolean ("exec-main-loop", _("exec-main-loop"),
172 _("Execute main loop iteration"),
173 TRUE, G_PARAM_READWRITE));
174 }
175
176 GSF_CLASS_FULL (GOIOContext, go_io_context,
177 NULL, NULL, go_io_context_class_init, NULL,
178 go_io_context_init, G_TYPE_OBJECT, 0,
179 GSF_INTERFACE (go_io_context_gnm_cmd_context_init, GO_TYPE_CMD_CONTEXT))
180
181 GOIOContext *
go_io_context_new(GOCmdContext * cc)182 go_io_context_new (GOCmdContext *cc)
183 {
184 GOIOContext *ioc;
185
186 g_return_val_if_fail (GO_IS_CMD_CONTEXT (cc), NULL);
187
188 ioc = g_object_new (GO_TYPE_IO_CONTEXT, NULL);
189 /* The cc is optional for subclasses, but mandatory in this class. */
190 ioc->impl = cc;
191 g_object_ref (ioc->impl);
192
193 return ioc;
194 }
195
196 void
go_io_error_unknown(GOIOContext * context)197 go_io_error_unknown (GOIOContext *context)
198 {
199 g_return_if_fail (context != NULL);
200
201 context->error_occurred = TRUE;
202 }
203
204 void
go_io_error_info_set(GOIOContext * context,GOErrorInfo * error)205 go_io_error_info_set (GOIOContext *context, GOErrorInfo *error)
206 {
207 g_return_if_fail (context != NULL);
208 g_return_if_fail (error != NULL);
209
210 context->info = g_slist_prepend (context->info, error);
211
212 if (go_error_info_peek_severity (error) < GO_ERROR)
213 context->warning_occurred = TRUE;
214 else
215 context->error_occurred = TRUE;
216 }
217
218 void
go_io_error_push(GOIOContext * context,GOErrorInfo * error)219 go_io_error_push (GOIOContext *context, GOErrorInfo *error)
220 {
221 g_return_if_fail (context != NULL);
222 g_return_if_fail (error != NULL);
223
224 if (context->info == NULL)
225 go_io_error_info_set (context, error);
226 else {
227 GOErrorInfo *info = context->info->data;
228 go_error_info_add_details (error, info);
229 context->info->data = error;
230 }
231 }
232
233 void
go_io_error_display(GOIOContext * context)234 go_io_error_display (GOIOContext *context)
235 {
236 GOCmdContext *cc;
237
238 g_return_if_fail (context != NULL);
239
240 if (context->info != NULL) {
241 if (context->impl)
242 cc = context->impl;
243 else
244 cc = GO_CMD_CONTEXT (context);
245 go_cmd_context_error_info_list
246 (cc, context->info);
247 }
248 }
249
250 /* TODO: Rename to go_io_info_clear */
251 void
go_io_error_clear(GOIOContext * context)252 go_io_error_clear (GOIOContext *context)
253 {
254 g_return_if_fail (context != NULL);
255
256 context->error_occurred = FALSE;
257 context->warning_occurred = FALSE;
258 g_slist_free_full (context->info, (GDestroyNotify) go_error_info_free);
259 context->info = NULL;
260 }
261
262 gboolean
go_io_error_occurred(GOIOContext * context)263 go_io_error_occurred (GOIOContext *context)
264 {
265 return context->error_occurred;
266 }
267
268 gboolean
go_io_warning_occurred(GOIOContext * context)269 go_io_warning_occurred (GOIOContext *context)
270 {
271 return context->warning_occurred;
272 }
273
274 void
go_io_progress_update(GOIOContext * ioc,gdouble f)275 go_io_progress_update (GOIOContext *ioc, gdouble f)
276 {
277 gboolean at_end;
278
279 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
280
281 if (ioc->progress_ranges != NULL) {
282 f = f * (ioc->progress_max - ioc->progress_min)
283 + ioc->progress_min;
284 }
285
286 at_end = (f - ioc->last_progress > PROGRESS_UPDATE_STEP_END &&
287 f + PROGRESS_UPDATE_STEP > 1);
288 /* The use of fabs here means we can set progress back if we need to. */
289 if (at_end || fabs (f - ioc->last_progress) >= PROGRESS_UPDATE_STEP) {
290 double t = g_get_monotonic_time () / 1000000.0;
291
292 if (at_end || t - ioc->last_time >= PROGRESS_UPDATE_PERIOD_SEC) {
293 GOCmdContext *cc;
294
295 if (ioc->impl)
296 cc = ioc->impl;
297 else
298 cc = GO_CMD_CONTEXT (ioc);
299 go_cmd_context_progress_set (cc, f);
300 ioc->last_time = t;
301 ioc->last_progress = f;
302 }
303 }
304
305 #ifdef GOFFICE_WITH_GTK
306 /* FIXME : abstract this into the workbook control */
307 if (ioc->exec_main_loop)
308 while (gtk_events_pending ())
309 gtk_main_iteration_do (FALSE);
310 #endif
311 }
312
313 void
go_io_progress_message(GOIOContext * ioc,const gchar * msg)314 go_io_progress_message (GOIOContext *ioc, const gchar *msg)
315 {
316 GOCmdContext *cc;
317
318 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
319
320 if (ioc->impl)
321 cc = ioc->impl;
322 else
323 cc = GO_CMD_CONTEXT (ioc);
324 go_cmd_context_progress_message_set (cc, msg);
325 }
326
327 void
go_io_progress_range_push(GOIOContext * ioc,gdouble min,gdouble max)328 go_io_progress_range_push (GOIOContext *ioc, gdouble min, gdouble max)
329 {
330 GOProgressRange *r;
331 gdouble new_min, new_max;
332
333 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
334
335 r = g_new (GOProgressRange, 1);
336 r->min = min;
337 r->max = max;
338 ioc->progress_ranges = g_list_append (ioc->progress_ranges, r);
339
340 new_min = min / (ioc->progress_max - ioc->progress_min)
341 + ioc->progress_min;
342 new_max = max / (ioc->progress_max - ioc->progress_min)
343 + ioc->progress_min;
344 ioc->progress_min = new_min;
345 ioc->progress_max = new_max;
346 }
347
348 void
go_io_progress_range_pop(GOIOContext * ioc)349 go_io_progress_range_pop (GOIOContext *ioc)
350 {
351 GList *l;
352
353 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
354 g_return_if_fail (ioc->progress_ranges != NULL);
355
356 l = g_list_last (ioc->progress_ranges);
357 ioc->progress_ranges= g_list_remove_link (ioc->progress_ranges, l);
358 g_free (l->data);
359 g_list_free_1 (l);
360
361 ioc->progress_min = 0.0;
362 ioc->progress_max = 1.0;
363 for (l = ioc->progress_ranges; l != NULL; l = l->next) {
364 GOProgressRange *r = l->data;
365 gdouble new_min, new_max;
366
367 new_min = r->min / (ioc->progress_max - ioc->progress_min)
368 + ioc->progress_min;
369 new_max = r->max / (ioc->progress_max - ioc->progress_min)
370 + ioc->progress_min;
371 ioc->progress_min = new_min;
372 ioc->progress_max = new_max;
373 }
374 }
375
376 void
go_io_value_progress_set(GOIOContext * ioc,gint total,gint step)377 go_io_value_progress_set (GOIOContext *ioc, gint total, gint step)
378 {
379 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
380 g_return_if_fail (total >= 0);
381
382 ioc->helper.helper_type = GO_PROGRESS_HELPER_VALUE;
383 ioc->helper.v.value.total = MAX (total, 1);
384 ioc->helper.v.value.last = -step;
385 ioc->helper.v.value.step = step;
386 }
387
388 void
go_io_value_progress_update(GOIOContext * ioc,gint value)389 go_io_value_progress_update (GOIOContext *ioc, gint value)
390 {
391 gdouble complete;
392 gint step, total;
393
394 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
395 g_return_if_fail (ioc->helper.helper_type == GO_PROGRESS_HELPER_VALUE);
396
397 total = ioc->helper.v.value.total;
398 step = ioc->helper.v.value.step;
399
400 if (value - ioc->helper.v.value.last < step &&
401 value + step < total) {
402 return;
403 }
404 ioc->helper.v.value.last = value;
405
406 complete = (gdouble)value / total;
407 go_io_progress_update (ioc, complete);
408 }
409
410 void
go_io_count_progress_set(GOIOContext * ioc,gint total,gint step)411 go_io_count_progress_set (GOIOContext *ioc, gint total, gint step)
412 {
413 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
414 g_return_if_fail (total >= 0);
415
416 ioc->helper.helper_type = GO_PROGRESS_HELPER_COUNT;
417 ioc->helper.v.count.total = MAX (total, 1);
418 ioc->helper.v.count.last = -step;
419 ioc->helper.v.count.current = 0;
420 ioc->helper.v.count.step = step;
421 }
422
423 void
go_io_count_progress_update(GOIOContext * ioc,gint inc)424 go_io_count_progress_update (GOIOContext *ioc, gint inc)
425 {
426 gdouble complete;
427 gint current, step, total;
428
429 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
430 g_return_if_fail (ioc->helper.helper_type == GO_PROGRESS_HELPER_COUNT);
431
432 current = (ioc->helper.v.count.current += inc);
433 step = ioc->helper.v.count.step;
434 total = ioc->helper.v.count.total;
435
436 if (current - ioc->helper.v.count.last < step && current + step < total) {
437 return;
438 }
439 ioc->helper.v.count.last = current;
440
441 complete = (gdouble)current / total;
442 go_io_progress_update (ioc, complete);
443 }
444
445 void
go_io_progress_unset(GOIOContext * ioc)446 go_io_progress_unset (GOIOContext *ioc)
447 {
448 g_return_if_fail (GO_IS_IO_CONTEXT (ioc));
449
450 ioc->helper.helper_type = GO_PROGRESS_HELPER_NONE;
451 }
452
453 void
go_io_context_set_num_files(GOIOContext * ioc,guint count)454 go_io_context_set_num_files (GOIOContext *ioc, guint count)
455 {
456 GOIOContextClass *klass = IOC_CLASS(ioc);
457 g_return_if_fail (klass != NULL);
458 if (klass->set_num_files != NULL)
459 klass->set_num_files (ioc, count);
460 }
461
462 /**
463 * go_io_context_processing_file:
464 * @ioc: #GOIOContext
465 * @uri: An escaped uri (eg "foo\%20bar")
466 **/
467 void
go_io_context_processing_file(GOIOContext * ioc,char const * uri)468 go_io_context_processing_file (GOIOContext *ioc, char const *uri)
469 {
470 char *basename;
471 GOIOContextClass *klass = IOC_CLASS(ioc);
472
473 g_return_if_fail (klass != NULL);
474
475 basename = go_basename_from_uri (uri); /* unescape the uri */
476 if (basename != NULL && klass->processing_file != NULL)
477 klass->processing_file (ioc, basename);
478 g_free (basename);
479 }
480
481 void
go_io_warning(G_GNUC_UNUSED GOIOContext * context,char const * fmt,...)482 go_io_warning (G_GNUC_UNUSED GOIOContext *context,
483 char const *fmt, ...)
484 {
485 va_list args;
486
487 va_start (args, fmt);
488 go_io_warning_varargs (context, fmt, args);
489 va_end (args);
490 }
491
492 void
go_io_warning_varargs(GOIOContext * context,char const * fmt,va_list args)493 go_io_warning_varargs (GOIOContext *context, char const *fmt, va_list args)
494 {
495 context->info = g_slist_prepend
496 (context->info, go_error_info_new_vprintf
497 (GO_WARNING, fmt, args));
498 context->warning_occurred = TRUE;
499 }
500
501 void
go_io_warning_unknown_font(GOIOContext * context,G_GNUC_UNUSED char const * font_name)502 go_io_warning_unknown_font (GOIOContext *context,
503 G_GNUC_UNUSED char const *font_name)
504 {
505 g_return_if_fail (GO_IS_IO_CONTEXT (context));
506 }
507
508 void
go_io_warning_unknown_function(GOIOContext * context,G_GNUC_UNUSED char const * funct_name)509 go_io_warning_unknown_function (GOIOContext *context,
510 G_GNUC_UNUSED char const *funct_name)
511 {
512 g_return_if_fail (GO_IS_IO_CONTEXT (context));
513 }
514
515 void
go_io_warning_unsupported_feature(GOIOContext * context,char const * feature)516 go_io_warning_unsupported_feature (GOIOContext *context, char const *feature)
517 {
518 g_return_if_fail (GO_IS_IO_CONTEXT (context));
519 g_warning ("%s : are not supported yet", feature);
520 }
521