1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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.
9 * Jon K Hellan <hellan@acm.org>
11 * (C) 2002 Jon K Hellan
13 #include <gnumeric-config.h>
16 #include "io-context-gtk.h"
17 #include <goffice/goffice.h>
18 #include "application.h"
19 #include "libgnumeric.h"
20 #include "dialogs/dialogs.h"
23 #include <gsf/gsf-impl-utils.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_
{
38 GtkWindow
*parent_window
;
39 GtkProgressBar
*file_bar
;
40 GtkProgressBar
*work_bar
;
52 gboolean show_warnings
;
55 struct GnmIOContextGtkClass_
{
56 GOIOContextClass parent_class
;
66 cb_icg_window_destroyed (GObject
*window
, GnmIOContextGtk
*icg
)
69 icg
->parent_window
= 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 */
77 icg
->interrupted
= TRUE
;
81 cb_hide_splash (G_GNUC_UNUSED GtkWidget
*widget
,
82 G_GNUC_UNUSED GdkEventButton
*event
,
85 gtk_widget_hide (GTK_WIDGET (icg
->window
));
90 cb_realize (GtkWindow
*window
, void *dummy
)
94 GtkAllocation allocation
;
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
),
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
|
118 gtk_window_set_geometry_hints (window
, NULL
, &geom
, hints
);
119 gtk_window_set_decorated (window
, FALSE
);
123 icg_show_gui (GnmIOContextGtk
*icg
)
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"),
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
,
143 gtk_box_pack_start (box
, GTK_WIDGET (icg
->file_bar
),
147 icg
->work_bar
= GTK_PROGRESS_BAR
148 (g_object_new (GTK_TYPE_PROGRESS_BAR
,
150 "text", icg
->progress_msg
,
152 "fraction", icg
->progress
,
154 gtk_box_pack_start (box
, GTK_WIDGET (icg
->work_bar
),
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
),
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
));
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)
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);
201 double forecast
= icg
->latency
;
202 forecast
+= (t
- icg
->latency
) / progress
;
203 ret
= (forecast
> ICG_POPUP_DELAY
);
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
;
219 icg_progress_set (GOCmdContext
*cc
, double val
)
221 GnmIOContextGtk
*icg
= GNM_IO_CONTEXT_GTK (cc
);
223 if (!icg
->show_splash
)
226 if (icg
->window
== NULL
) {
228 if (!icg_user_is_impatient (icg
))
232 gtk_progress_bar_set_fraction (icg
->work_bar
, val
);
236 icg_progress_message_set (GOCmdContext
*cc
, gchar
const *msg
)
238 GnmIOContextGtk
*icg
= GNM_IO_CONTEXT_GTK (cc
);
240 if (!icg
->show_splash
)
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
);
251 gtk_progress_bar_set_text (icg
->work_bar
, msg
);
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
);
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
273 gtk_widget_show_all (GTK_WIDGET (dialog
));
274 gtk_dialog_run (GTK_DIALOG (dialog
));
275 gtk_widget_destroy (dialog
);
280 icg_set_num_files (GOIOContext
*icg
, guint files_total
)
282 GNM_IO_CONTEXT_GTK (icg
)->files_total
= files_total
;
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
);
293 if (icg
->window
!= NULL
&& icg
->file_bar
!= NULL
) {
294 int len
= strlen (file
);
297 if (icg
->files_total
> 0)
298 gtk_progress_bar_set_fraction
300 icg
->files_done
/ (double)icg
->files_total
);
302 gtk_progress_bar_set_fraction (icg
->work_bar
, 0.0);
305 gtk_progress_bar_set_text (icg
->file_bar
, file
);
307 char *shown_text
= g_strdup (file
);
308 char *p
= shown_text
+ len
;
312 while (p
> shown_text
&& G_IS_DIR_SEPARATOR (p
[-1]))
314 if (p
> shown_text
&& shown_text
+ len
- p
< maxlen
) {
318 p
= g_strdup_printf ("...%s", last_p
);
319 gtk_progress_bar_set_text (icg
->file_bar
, p
);
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
);
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
);
349 case PROP_SHOW_WARNINGS
:
350 icg
->show_warnings
= g_value_get_boolean (value
);
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, property_id
, pspec
);
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
;
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",
378 P_("Show a splash screen if loading takes more than a moment"),
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",
384 P_("Show warning and password dialogs"),
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
;
393 icg_init (GnmIOContextGtk
*icg
)
395 icg
->show_splash
= TRUE
;
396 icg
->show_warnings
= TRUE
;
399 icg
->work_bar
= NULL
;
400 icg
->file_bar
= NULL
;
401 icg
->files_total
= 0;
404 icg
->progress_msg
= NULL
;
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
))
417 gnm_io_context_gtk_set_transient_for (GnmIOContextGtk
*icg
, GtkWindow
*parent_window
)
419 icg
->parent_window
= parent_window
;
421 go_gtk_window_set_transient (parent_window
, icg
->window
);
425 gnm_io_context_gtk_get_interrupted (GnmIOContextGtk
*icg
)
427 return icg
->interrupted
;
431 gnm_io_context_gtk_discharge_splash (GnmIOContextGtk
*icg
)
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
));
441 icg
->work_bar
= NULL
;
442 icg
->file_bar
= NULL
;
446 g_timer_destroy (icg
->timer
);