GUI: reduce vertical size of the toolbar area
[gnumeric.git] / src / io-context-gtk.c
blob93aa7accecd41c44c23e296f81cb0dce138c73e3
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * io-context-gtk.c : gtk based io error context.
5 * It may be used e.g. for displaying progress and error messages
6 * before the first workbook is displayed.
8 * Author:
9 * Jon K Hellan <hellan@acm.org>
11 * (C) 2002 Jon K Hellan
13 #include <gnumeric-config.h>
14 #include "gnumeric.h"
15 #include "gui-util.h"
16 #include "io-context-gtk.h"
17 #include <goffice/goffice.h>
18 #include "application.h"
19 #include "libgnumeric.h"
20 #include "dialogs.h"
21 #include "gnm-i18n.h"
23 #include <gsf/gsf-impl-utils.h>
24 #include <gtk/gtk.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #define ICG_POPUP_DELAY 3.0
30 #define IO_CONTEXT_GTK_CLASS(klass) \
31 (G_TYPE_CHECK_CLASS_CAST ((klass), GNM_TYPE_IO_CONTEXT_GTK, GnmIOContextGtk))
32 #define GNM_IS_IO_CONTEXT_GTK_CLASS(klass) \
33 (G_TYPE_CHECK_CLASS_TYPE ((klass), GNM_TYPE_IO_CONTEXT_GTK))
35 struct GnmIOContextGtk_ {
36 GOIOContext parent;
37 GtkWindow *window;
38 GtkWindow *parent_window;
39 GtkProgressBar *file_bar;
40 GtkProgressBar *work_bar;
41 GTimer *timer;
42 guint files_total;
43 guint files_done;
45 double progress;
46 char *progress_msg;
47 gdouble latency;
49 gboolean interrupted;
51 gboolean show_splash;
52 gboolean show_warnings;
55 struct GnmIOContextGtkClass_ {
56 GOIOContextClass parent_class;
59 enum {
60 PROP_0,
61 PROP_SHOW_SPLASH,
62 PROP_SHOW_WARNINGS
65 static void
66 cb_icg_window_destroyed (GObject *window, GnmIOContextGtk *icg)
68 icg->window = NULL;
69 icg->parent_window = NULL;
70 icg->work_bar = NULL;
71 icg->file_bar = NULL;
72 if (icg->files_done == 0) {
73 gnm_shutdown (); /* Pretend to be well behaved */
74 gnm_pre_parse_shutdown ();
75 exit (0); /* Stop pretending */
76 } else
77 icg->interrupted = TRUE;
80 static gboolean
81 cb_hide_splash (G_GNUC_UNUSED GtkWidget *widget,
82 G_GNUC_UNUSED GdkEventButton *event,
83 GnmIOContextGtk *icg)
85 gtk_widget_hide (GTK_WIDGET (icg->window));
86 return TRUE;
89 static void
90 cb_realize (GtkWindow *window, void *dummy)
92 int sx, sy;
93 GdkWindowHints hints;
94 GtkAllocation allocation;
95 GdkGeometry geom;
96 GdkRectangle rect;
98 /* In a Xinerama setup, we want the geometry of the actual display
99 * unit, if available. See bug 59902. */
100 gdk_screen_get_monitor_geometry (gtk_window_get_screen (window),
101 0, &rect);
102 sx = rect.width;
103 sy = rect.height;
104 gtk_widget_get_allocation (GTK_WIDGET (window), &allocation);
106 geom.base_width = allocation.width;
107 geom.base_height = allocation.height;
108 geom.min_width = geom.max_width = geom.base_width;
109 geom.min_height = geom.max_height = geom.base_height;
111 gtk_window_move (window,
112 sx / 2 - geom.min_width / 2,
113 sy / 2 - geom.min_height / 2);
114 hints = GDK_HINT_POS | GDK_HINT_USER_POS |
115 GDK_HINT_BASE_SIZE | GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE |
116 GDK_HINT_USER_SIZE;
118 gtk_window_set_geometry_hints (window, NULL, &geom, hints);
119 gtk_window_set_decorated (window, FALSE);
122 static void
123 icg_show_gui (GnmIOContextGtk *icg)
125 GtkBox *box;
126 GtkWidget *frame;
128 box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
129 if (icg->show_splash)
130 gtk_box_pack_start (box, gtk_image_new_from_resource ("/org/gnumeric/gnumeric/images/gnumeric_splash_1.4.png"),
131 TRUE, FALSE, 0);
133 /* Don't show this unless we need it. */
134 if (icg->files_total > 1) {
135 double f = icg->files_done / (double)icg->files_total;
136 icg->file_bar = GTK_PROGRESS_BAR
137 (g_object_new (GTK_TYPE_PROGRESS_BAR,
138 "text", "Files",
139 "show-text", TRUE,
140 "fraction", f,
141 "inverted", FALSE,
142 NULL));
143 gtk_box_pack_start (box, GTK_WIDGET (icg->file_bar),
144 FALSE, FALSE, 0);
147 icg->work_bar = GTK_PROGRESS_BAR
148 (g_object_new (GTK_TYPE_PROGRESS_BAR,
149 "inverted", FALSE,
150 "text", icg->progress_msg,
151 "show-text", TRUE,
152 "fraction", icg->progress,
153 NULL));
154 gtk_box_pack_start (box, GTK_WIDGET (icg->work_bar),
155 FALSE, FALSE, 0);
157 icg->window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
158 gtk_window_set_type_hint (GTK_WINDOW (icg->window),
159 GDK_WINDOW_TYPE_HINT_SPLASHSCREEN);
160 g_signal_connect (G_OBJECT (icg->window),
161 "button_release_event",
162 G_CALLBACK (cb_hide_splash), NULL);
163 g_signal_connect (G_OBJECT (icg->window),
164 "destroy",
165 G_CALLBACK (cb_icg_window_destroyed), icg);
167 frame = gtk_frame_new (NULL);
168 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
169 gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (box));
170 gtk_container_add (GTK_CONTAINER (icg->window), frame);
172 g_signal_connect (G_OBJECT (icg->window), "realize",
173 G_CALLBACK (cb_realize), NULL);
175 if (icg->parent_window)
176 gnm_io_context_gtk_set_transient_for (icg, icg->parent_window);
178 gtk_widget_show_all (GTK_WIDGET (icg->window));
181 static gboolean
182 icg_user_is_impatient (GnmIOContextGtk *icg)
184 gdouble t = g_timer_elapsed (icg->timer, NULL);
185 double progress = icg->progress;
186 double forecast_delay = ICG_POPUP_DELAY / 3.0;
187 gboolean ret = FALSE;
189 if (icg->progress == 0. && icg->files_done == 0)
190 icg->latency = t;
192 if (t >= forecast_delay) {
193 if (icg->files_total > 1) {
194 progress += icg->files_done;
195 progress /= icg->files_total;
197 if (progress <= 0.0) {
198 /* We're likely to be back shortly. */
199 ret = (t > ICG_POPUP_DELAY * 0.8);
200 } else {
201 double forecast = icg->latency;
202 forecast += (t - icg->latency) / progress;
203 ret = (forecast > ICG_POPUP_DELAY);
207 return ret;
210 static char *
211 icg_get_password (GOCmdContext *cc, char const *filename)
213 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
214 return icg->show_warnings ?
215 dialog_get_password (icg->window, filename) : NULL;
218 static void
219 icg_progress_set (GOCmdContext *cc, double val)
221 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
223 if (!icg->show_splash)
224 return;
226 if (icg->window == NULL) {
227 icg->progress = val;
228 if (!icg_user_is_impatient (icg))
229 return;
230 icg_show_gui (icg);
232 gtk_progress_bar_set_fraction (icg->work_bar, val);
235 static void
236 icg_progress_message_set (GOCmdContext *cc, gchar const *msg)
238 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
240 if (!icg->show_splash)
241 return;
243 if (icg->window == NULL) {
244 if (!icg_user_is_impatient (icg)) {
245 g_free (icg->progress_msg);
246 icg->progress_msg = g_strdup (msg);
247 return;
249 icg_show_gui (icg);
251 gtk_progress_bar_set_text (icg->work_bar, msg);
254 static void
255 icg_error_error_info (GOCmdContext *cc, GOErrorInfo *error)
257 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
258 if (icg->show_warnings) {
259 GtkWidget *dialog = gnm_go_error_info_dialog_create (error);
260 gtk_widget_show_all (GTK_WIDGET (dialog));
261 gtk_dialog_run (GTK_DIALOG (dialog));
262 gtk_widget_destroy (dialog);
266 static void
267 icg_error_error_info_list (GOCmdContext *cc, GSList *error)
269 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (cc);
270 if (icg->show_warnings && error != NULL && error->data != NULL) {
271 GtkWidget *dialog = gnm_go_error_info_dialog_create
272 (error->data);
273 gtk_widget_show_all (GTK_WIDGET (dialog));
274 gtk_dialog_run (GTK_DIALOG (dialog));
275 gtk_widget_destroy (dialog);
279 static void
280 icg_set_num_files (GOIOContext *icg, guint files_total)
282 GNM_IO_CONTEXT_GTK (icg)->files_total = files_total;
285 static void
286 icg_processing_file (GOIOContext *ioc, char const *file)
288 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (ioc);
290 g_return_if_fail (icg->files_done < icg->files_total);
292 icg->files_done++;
293 if (icg->window != NULL && icg->file_bar != NULL) {
294 int len = strlen (file);
295 int maxlen = 40;
297 if (icg->files_total > 0)
298 gtk_progress_bar_set_fraction
299 (icg->file_bar,
300 icg->files_done / (double)icg->files_total);
302 gtk_progress_bar_set_fraction (icg->work_bar, 0.0);
304 if (len <= maxlen)
305 gtk_progress_bar_set_text (icg->file_bar, file);
306 else {
307 char *shown_text = g_strdup (file);
308 char *p = shown_text + len;
310 while (1) {
311 char *last_p = p;
312 while (p > shown_text && G_IS_DIR_SEPARATOR (p[-1]))
313 p--;
314 if (p > shown_text && shown_text + len - p < maxlen) {
315 p--;
316 continue;
318 p = g_strdup_printf ("...%s", last_p);
319 gtk_progress_bar_set_text (icg->file_bar, p);
320 g_free (p);
321 break;
324 g_free (shown_text);
329 static void
330 icg_finalize (GObject *obj)
332 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (obj);
334 gnm_io_context_gtk_discharge_splash (icg);
335 g_free (icg->progress_msg);
336 G_OBJECT_CLASS (g_type_class_peek (GO_TYPE_IO_CONTEXT))->finalize (obj);
339 static void
340 icg_set_property (GObject *obj, guint property_id,
341 GValue const *value, GParamSpec *pspec)
343 GnmIOContextGtk *icg = GNM_IO_CONTEXT_GTK (obj);
345 switch (property_id) {
346 case PROP_SHOW_SPLASH:
347 icg->show_splash = g_value_get_boolean (value);
348 break;
349 case PROP_SHOW_WARNINGS:
350 icg->show_warnings = g_value_get_boolean (value);
351 break;
352 default:
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
354 break;
357 static void
358 icg_gnm_cmd_context_init (GOCmdContextClass *cc_class)
360 cc_class->get_password = icg_get_password;
361 cc_class->progress_set = icg_progress_set;
362 cc_class->progress_message_set = icg_progress_message_set;
363 cc_class->error.error_info = icg_error_error_info;
364 cc_class->error.error_info_list = icg_error_error_info_list;
367 static void
368 icg_class_init (GObjectClass *gobj_klass)
370 GOIOContextClass *ioc_klass = (GOIOContextClass *)gobj_klass;
372 gobj_klass->finalize = icg_finalize;
373 gobj_klass->set_property = icg_set_property;
375 g_object_class_install_property (gobj_klass, PROP_SHOW_SPLASH,
376 g_param_spec_boolean ("show-splash",
377 P_("Show splash"),
378 P_("Show a splash screen if loading takes more than a moment"),
379 TRUE,
380 GSF_PARAM_STATIC | G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
381 g_object_class_install_property (gobj_klass, PROP_SHOW_WARNINGS,
382 g_param_spec_boolean ("show-warnings",
383 P_("Show warnings"),
384 P_("Show warning and password dialogs"),
385 TRUE,
386 GSF_PARAM_STATIC | G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
388 ioc_klass->set_num_files = icg_set_num_files;
389 ioc_klass->processing_file = icg_processing_file;
392 static void
393 icg_init (GnmIOContextGtk *icg)
395 icg->show_splash = TRUE;
396 icg->show_warnings = TRUE;
398 icg->window = NULL;
399 icg->work_bar = NULL;
400 icg->file_bar = NULL;
401 icg->files_total = 0;
402 icg->files_done = 0;
403 icg->progress = 0.;
404 icg->progress_msg = NULL;
405 icg->latency = 0.;
406 icg->interrupted = FALSE;
407 icg->timer = g_timer_new ();
408 g_timer_start (icg->timer);
411 GSF_CLASS_FULL (GnmIOContextGtk, gnm_io_context_gtk,
412 NULL, NULL, icg_class_init, NULL,
413 icg_init, GO_TYPE_IO_CONTEXT, 0,
414 GSF_INTERFACE (icg_gnm_cmd_context_init, GO_TYPE_CMD_CONTEXT))
416 void
417 gnm_io_context_gtk_set_transient_for (GnmIOContextGtk *icg, GtkWindow *parent_window)
419 icg->parent_window = parent_window;
420 if (icg->window)
421 go_gtk_window_set_transient (parent_window, icg->window);
424 gboolean
425 gnm_io_context_gtk_get_interrupted (GnmIOContextGtk *icg)
427 return icg->interrupted;
430 void
431 gnm_io_context_gtk_discharge_splash (GnmIOContextGtk *icg)
433 if (icg->window) {
434 g_signal_handlers_disconnect_by_func (
435 G_OBJECT (icg->window),
436 G_CALLBACK (cb_icg_window_destroyed), icg);
437 gtk_window_set_focus (icg->window, NULL);
438 gtk_window_set_default (icg->window, NULL);
439 gtk_widget_destroy (GTK_WIDGET (icg->window));
440 icg->window = NULL;
441 icg->work_bar = NULL;
442 icg->file_bar = NULL;
445 if (icg->timer) {
446 g_timer_destroy (icg->timer);
447 icg->timer = NULL;