Update Spanish translation
[gnumeric.git] / src / io-context-gtk.c
blob7d7de877e0370b3a3e906e811e69117b4a9ce05b
2 /*
3 * io-context-gtk.c : gtk based io error context.
4 * It may be used e.g. for displaying progress and error messages
5 * before the first workbook is displayed.
7 * Author:
8 * Jon K Hellan <hellan@acm.org>
10 * (C) 2002 Jon K Hellan
12 #include <gnumeric-config.h>
13 #include <gnumeric.h>
14 #include <gui-util.h>
15 #include <io-context-gtk.h>
16 #include <goffice/goffice.h>
17 #include <application.h>
18 #include <libgnumeric.h>
19 #include <dialogs/dialogs.h>
20 #include <gnm-i18n.h>
22 #include <gsf/gsf-impl-utils.h>
23 #include <stdlib.h>
24 #include <string.h>
26 #define ICG_POPUP_DELAY 3.0
28 #define IO_CONTEXT_GTK_CLASS(klass) \
29 (G_TYPE_CHECK_CLASS_CAST ((klass), GNM_TYPE_IO_CONTEXT_GTK, GnmIOContextGtk))
30 #define GNM_IS_IO_CONTEXT_GTK_CLASS(klass) \
31 (G_TYPE_CHECK_CLASS_TYPE ((klass), GNM_TYPE_IO_CONTEXT_GTK))
33 struct GnmIOContextGtk_ {
34 GOIOContext parent;
35 GtkWindow *window;
36 GtkWindow *parent_window;
37 GtkProgressBar *file_bar;
38 GtkProgressBar *work_bar;
39 GTimer *timer;
40 guint files_total;
41 guint files_done;
43 double progress;
44 char *progress_msg;
45 gdouble latency;
47 gboolean interrupted;
49 gboolean show_splash;
50 gboolean show_warnings;
53 struct GnmIOContextGtkClass_ {
54 GOIOContextClass parent_class;
57 enum {
58 PROP_0,
59 PROP_SHOW_SPLASH,
60 PROP_SHOW_WARNINGS
63 static void
64 cb_icg_window_destroyed (GObject *window, GnmIOContextGtk *icg)
66 icg->window = NULL;
67 icg->parent_window = NULL;
68 icg->work_bar = NULL;
69 icg->file_bar = NULL;
70 if (icg->files_done == 0) {
71 gnm_shutdown (); /* Pretend to be well behaved */
72 gnm_pre_parse_shutdown ();
73 exit (0); /* Stop pretending */
74 } else
75 icg->interrupted = TRUE;
78 static gboolean
79 cb_hide_splash (G_GNUC_UNUSED GtkWidget *widget,
80 G_GNUC_UNUSED GdkEventButton *event,
81 GnmIOContextGtk *icg)
83 gtk_widget_hide (GTK_WIDGET (icg->window));
84 return TRUE;
87 static void
88 cb_realize (GtkWindow *window, void *dummy)
90 int sx, sy;
91 GdkWindowHints hints;
92 GtkAllocation allocation;
93 GdkGeometry geom;
94 GdkRectangle rect;
96 /* In a Xinerama setup, we want the geometry of the actual display
97 * unit, if available. See bug 59902. */
98 gdk_screen_get_monitor_geometry (gtk_window_get_screen (window),
99 0, &rect);
100 sx = rect.width;
101 sy = rect.height;
102 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
104 geom.base_width = allocation.width;
105 geom.base_height = allocation.height;
106 geom.min_width = geom.max_width = geom.base_width;
107 geom.min_height = geom.max_height = geom.base_height;
109 gtk_window_move (window,
110 sx / 2 - geom.min_width / 2,
111 sy / 2 - geom.min_height / 2);
112 hints = GDK_HINT_POS | GDK_HINT_USER_POS |
113 GDK_HINT_BASE_SIZE | GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE |
114 GDK_HINT_USER_SIZE;
116 gtk_window_set_geometry_hints (window, NULL, &geom, hints);
117 gtk_window_set_decorated (window, FALSE);
120 static void
121 icg_show_gui (GnmIOContextGtk *icg)
123 GtkBox *box;
124 GtkWidget *frame;
126 box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
127 if (icg->show_splash)
128 gtk_box_pack_start (box, gtk_image_new_from_resource ("/org/gnumeric/gnumeric/images/gnumeric_splash_1.4.png"),
129 TRUE, FALSE, 0);
131 /* Don't show this unless we need it. */
132 if (icg->files_total > 1) {
133 double f = icg->files_done / (double)icg->files_total;
134 icg->file_bar = GTK_PROGRESS_BAR
135 (g_object_new (GTK_TYPE_PROGRESS_BAR,
136 "text", "Files",
137 "show-text", TRUE,
138 "fraction", f,
139 "inverted", FALSE,
140 NULL));
141 gtk_box_pack_start (box, GTK_WIDGET (icg->file_bar),
142 FALSE, FALSE, 0);
145 icg->work_bar = GTK_PROGRESS_BAR
146 (g_object_new (GTK_TYPE_PROGRESS_BAR,
147 "inverted", FALSE,
148 "text", icg->progress_msg,
149 "show-text", TRUE,
150 "fraction", icg->progress,
151 NULL));
152 gtk_box_pack_start (box, GTK_WIDGET (icg->work_bar),
153 FALSE, FALSE, 0);
155 icg->window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
156 gtk_window_set_type_hint (GTK_WINDOW (icg->window),
157 GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
158 g_signal_connect (G_OBJECT (icg->window),
159 "button_release_event",
160 G_CALLBACK (cb_hide_splash), NULL);
161 g_signal_connect (G_OBJECT (icg->window),
162 "destroy",
163 G_CALLBACK (cb_icg_window_destroyed), icg);
165 frame = gtk_frame_new (NULL);
166 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
167 gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (box));
168 gtk_container_add (GTK_CONTAINER (icg->window), frame);
170 g_signal_connect (G_OBJECT (icg->window), "realize",
171 G_CALLBACK (cb_realize), NULL);
173 if (icg->parent_window)
174 gnm_io_context_gtk_set_transient_for (icg, icg->parent_window);
176 gtk_widget_show_all (GTK_WIDGET (icg->window));
179 static gboolean
180 icg_user_is_impatient (GnmIOContextGtk *icg)
182 gdouble t = g_timer_elapsed (icg->timer, NULL);
183 double progress = icg->progress;
184 double forecast_delay = ICG_POPUP_DELAY / 3.0;
185 gboolean ret = FALSE;
187 if (icg->progress == 0. && icg->files_done == 0)
188 icg->latency = t;
190 if (t >= forecast_delay) {
191 if (icg->files_total > 1) {
192 progress += icg->files_done;
193 progress /= icg->files_total;
195 if (progress <= 0.0) {
196 /* We're likely to be back shortly. */
197 ret = (t > ICG_POPUP_DELAY * 0.8);
198 } else {
199 double forecast = icg->latency;
200 forecast += (t - icg->latency) / progress;
201 ret = (forecast > ICG_POPUP_DELAY);
205 return ret;
208 static char *
209 icg_get_password (GOCmdContext *cc, char const *filename)
211 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
212 return icg->show_warnings ?
213 dialog_get_password (icg->window, filename) : NULL;
216 static void
217 icg_progress_set (GOCmdContext *cc, double val)
219 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
221 if (!icg->show_splash)
222 return;
224 if (icg->window == NULL) {
225 icg->progress = val;
226 if (!icg_user_is_impatient (icg))
227 return;
228 icg_show_gui (icg);
230 gtk_progress_bar_set_fraction (icg->work_bar, val);
233 static void
234 icg_progress_message_set (GOCmdContext *cc, gchar const *msg)
236 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
238 if (!icg->show_splash)
239 return;
241 if (icg->window == NULL) {
242 if (!icg_user_is_impatient (icg)) {
243 g_free (icg->progress_msg);
244 icg->progress_msg = g_strdup (msg);
245 return;
247 icg_show_gui (icg);
249 gtk_progress_bar_set_text (icg->work_bar, msg);
252 static void
253 icg_error_error_info (GOCmdContext *cc, GOErrorInfo *error)
255 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
256 if (icg->show_warnings) {
257 GtkWidget *dialog = gnm_go_error_info_dialog_create (error);
258 gtk_widget_show_all (GTK_WIDGET (dialog));
259 gtk_dialog_run (GTK_DIALOG (dialog));
260 gtk_widget_destroy (dialog);
264 static void
265 icg_error_error_info_list (GOCmdContext *cc, GSList *error)
267 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
268 if (icg->show_warnings && error != NULL && error->data != NULL) {
269 GtkWidget *dialog = gnm_go_error_info_dialog_create
270 (error->data);
271 gtk_widget_show_all (GTK_WIDGET (dialog));
272 gtk_dialog_run (GTK_DIALOG (dialog));
273 gtk_widget_destroy (dialog);
277 static void
278 icg_set_num_files (GOIOContext *icg, guint files_total)
280 GNM_IO_CONTEXT_GTK (icg)->files_total = files_total;
283 static void
284 icg_processing_file (GOIOContext *ioc, char const *file)
286 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (ioc);
288 g_return_if_fail (icg->files_done < icg->files_total);
290 icg->files_done++;
291 if (icg->window != NULL && icg->file_bar != NULL) {
292 int len = strlen (file);
293 int maxlen = 40;
295 if (icg->files_total > 0)
296 gtk_progress_bar_set_fraction
297 (icg->file_bar,
298 icg->files_done / (double)icg->files_total);
300 gtk_progress_bar_set_fraction (icg->work_bar, 0.0);
302 if (len <= maxlen)
303 gtk_progress_bar_set_text (icg->file_bar, file);
304 else {
305 char *shown_text = g_strdup (file);
306 char *p = shown_text + len;
308 while (1) {
309 char *last_p = p;
310 while (p > shown_text && G_IS_DIR_SEPARATOR (p[-1]))
311 p--;
312 if (p > shown_text && shown_text + len - p < maxlen) {
313 p--;
314 continue;
316 p = g_strdup_printf ("...%s", last_p);
317 gtk_progress_bar_set_text (icg->file_bar, p);
318 g_free (p);
319 break;
322 g_free (shown_text);
327 static void
328 icg_finalize (GObject *obj)
330 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (obj);
332 gnm_io_context_gtk_discharge_splash (icg);
333 g_free (icg->progress_msg);
334 G_OBJECT_CLASS (g_type_class_peek (GO_TYPE_IO_CONTEXT))->finalize (obj);
337 static void
338 icg_set_property (GObject *obj, guint property_id,
339 GValue const *value, GParamSpec *pspec)
341 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (obj);
343 switch (property_id) {
344 case PROP_SHOW_SPLASH:
345 icg->show_splash = g_value_get_boolean (value);
346 break;
347 case PROP_SHOW_WARNINGS:
348 icg->show_warnings = g_value_get_boolean (value);
349 break;
350 default:
351 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
352 break;
355 static void
356 icg_gnm_cmd_context_init (GOCmdContextClass *cc_class)
358 cc_class->get_password = icg_get_password;
359 cc_class->progress_set = icg_progress_set;
360 cc_class->progress_message_set = icg_progress_message_set;
361 cc_class->error.error_info = icg_error_error_info;
362 cc_class->error.error_info_list = icg_error_error_info_list;
365 static void
366 icg_class_init (GObjectClass *gobj_klass)
368 GOIOContextClass *ioc_klass = (GOIOContextClass *)gobj_klass;
370 gobj_klass->finalize = icg_finalize;
371 gobj_klass->set_property = icg_set_property;
373 g_object_class_install_property (gobj_klass, PROP_SHOW_SPLASH,
374 g_param_spec_boolean ("show-splash",
375 P_("Show splash"),
376 P_("Show a splash screen if loading takes more than a moment"),
377 TRUE,
378 GSF_PARAM_STATIC | G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
379 g_object_class_install_property (gobj_klass, PROP_SHOW_WARNINGS,
380 g_param_spec_boolean ("show-warnings",
381 P_("Show warnings"),
382 P_("Show warning and password dialogs"),
383 TRUE,
384 GSF_PARAM_STATIC | G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
386 ioc_klass->set_num_files = icg_set_num_files;
387 ioc_klass->processing_file = icg_processing_file;
390 static void
391 icg_init (GnmIOContextGtk *icg)
393 icg->show_splash = TRUE;
394 icg->show_warnings = TRUE;
396 icg->window = NULL;
397 icg->work_bar = NULL;
398 icg->file_bar = NULL;
399 icg->files_total = 0;
400 icg->files_done = 0;
401 icg->progress = 0.;
402 icg->progress_msg = NULL;
403 icg->latency = 0.;
404 icg->interrupted = FALSE;
405 icg->timer = g_timer_new ();
406 g_timer_start (icg->timer);
409 GSF_CLASS_FULL (GnmIOContextGtk, gnm_io_context_gtk,
410 NULL, NULL, icg_class_init, NULL,
411 icg_init, GO_TYPE_IO_CONTEXT, 0,
412 GSF_INTERFACE (icg_gnm_cmd_context_init, GO_TYPE_CMD_CONTEXT))
414 void
415 gnm_io_context_gtk_set_transient_for (GnmIOContextGtk *icg, GtkWindow *parent_window)
417 icg->parent_window = parent_window;
418 if (icg->window)
419 go_gtk_window_set_transient (parent_window, icg->window);
422 gboolean
423 gnm_io_context_gtk_get_interrupted (GnmIOContextGtk *icg)
425 return icg->interrupted;
428 void
429 gnm_io_context_gtk_discharge_splash (GnmIOContextGtk *icg)
431 if (icg->window) {
432 g_signal_handlers_disconnect_by_func (
433 G_OBJECT (icg->window),
434 G_CALLBACK (cb_icg_window_destroyed), icg);
435 gtk_window_set_focus (icg->window, NULL);
436 gtk_window_set_default (icg->window, NULL);
437 gtk_widget_destroy (GTK_WIDGET (icg->window));
438 icg->window = NULL;
439 icg->work_bar = NULL;
440 icg->file_bar = NULL;
443 if (icg->timer) {
444 g_timer_destroy (icg->timer);
445 icg->timer = NULL;