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