1 /*
2 * gretl -- Gnu Regression, Econometrics and Time-series Library
3 * Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 #include <errno.h>
21
22 #if GTK_MAJOR_VERSION >= 3
23 # include <gdk/gdkkeysyms-compat.h>
24 #else
25 # include <gdk/gdkkeysyms.h>
26 #endif
27
28 #include "gretl_zip.h"
29
strip_vname_illegals(char * s)30 static void strip_vname_illegals (char *s)
31 {
32 char name[VNAMELEN] = {0};
33 int i, j = 0;
34
35 for (i=0; s[i] != '\0'; i++) {
36 if (isalnum(s[i]) || s[i] == '_') {
37 name[j++] = s[i];
38 }
39 }
40
41 name[j] = '\0';
42 strcpy(s, name);
43 }
44
missing_varname(void)45 static int missing_varname (void)
46 {
47 gretl_errmsg_set("Variable name is missing");
48 return E_DATA;
49 }
50
51 /* The following is modeled on process_csv_varname() in csvdata.c,
52 adapted slightly for the specifics of the spreadsheet
53 importers.
54 */
55
check_imported_varname(char * vname,int vnum,int row,int col,PRN * prn)56 static int check_imported_varname (char *vname, int vnum,
57 int row, int col,
58 PRN *prn)
59 {
60 int err = 0;
61
62 if (*vname == '\0') {
63 if (vnum > 0) {
64 fprintf(stderr, "variable name %d is missing\n", vnum);
65 sprintf(vname, "v%d", vnum);
66 } else {
67 err = missing_varname();
68 }
69 } else if (numeric_string(vname)) {
70 err = check_varname(vname);
71 } else {
72 char *s, tmp[VNAMELEN];
73
74 *tmp = '\0';
75 strncat(tmp, vname, VNAMELEN - 1);
76 s = tmp;
77 *vname = '\0';
78
79 while (*s && !isalpha(*s)) s++;
80 if (*s == '\0') {
81 if (vnum > 0) {
82 fprintf(stderr, "variable name %d is garbage\n", vnum);
83 sprintf(vname, "v%d", vnum);
84 } else {
85 err = missing_varname();
86 }
87 } else {
88 strncat(vname, s, VNAMELEN - 1);
89 }
90 iso_to_ascii(vname);
91 strip_vname_illegals(vname);
92 err = check_varname(vname);
93 }
94
95 if (err) {
96 err = E_DATA;
97 if (row >= 0 && col >= 0) {
98 pputc(prn, '\n');
99 pprintf(prn, _("At row %d, column %d:\n"), row+1, col+1);
100 }
101 pputs(prn, gretl_errmsg_get());
102 }
103
104 return err;
105 }
106
107 #ifndef EXCEL_IMPORTER /* FIXME? */
108
import_ts_check(DATASET * dset)109 static void import_ts_check (DATASET *dset)
110 {
111 PRN *prn = gretl_print_new(GRETL_PRINT_STDERR, NULL);
112 int reversed = 0;
113 int mpd = -1;
114
115 mpd = test_markers_for_dates(dset, &reversed, NULL, prn);
116
117 if (mpd > 0) {
118 pputs(prn, _("taking date information from row labels\n\n"));
119 if (dset->markers != DAILY_DATE_STRINGS) {
120 dataset_destroy_obs_markers(dset);
121 }
122 if (reversed) {
123 reverse_data(dset, prn);
124 }
125 }
126
127 #if ODEBUG
128 fprintf(stderr, "dset->pd = %d\n", dset->pd);
129 #endif
130
131 if (dset->pd != 1 || strcmp(dset->stobs, "1")) {
132 dset->structure = TIME_SERIES;
133 }
134
135 gretl_print_destroy(prn);
136 }
137
138 #endif /* !EXCEL_IMPORTER */
139
140 #if defined(ODS_IMPORTER) || defined(XLSX_IMPORTER) || defined(GNUMERIC_IMPORTER)
141
142 /* check for spurious empty columns at the right of the sheet */
143
import_prune_columns(DATASET * dset)144 static int import_prune_columns (DATASET *dset)
145 {
146 int allmiss = 1, ndel = 0;
147 int i, t, err = 0;
148
149 for (i=dset->v-1; i>0 && allmiss; i--) {
150 for (t=0; t<dset->n; t++) {
151 if (!na(dset->Z[i][t])) {
152 allmiss = 0;
153 break;
154 }
155 }
156 if (allmiss) ndel++;
157 }
158
159 if (ndel == dset->v - 1) {
160 gretl_errmsg_set(_("No numeric data were found"));
161 err = E_DATA;
162 } else if (ndel > 0) {
163 fprintf(stderr, "Sheet has %d trailing empty variables\n", ndel);
164 err = dataset_drop_last_variables(dset, ndel);
165 }
166
167 return err;
168 }
169
170 #endif
171
172 #if defined(ODS_IMPORTER) || defined(XLSX_IMPORTER)
173
174 /* we want this for unzipping purposes */
175
get_absolute_path(const char * fname)176 static gchar *get_absolute_path (const char *fname)
177 {
178 gchar *cwd = g_get_current_dir();
179 gchar *ret = NULL;
180
181 if (cwd != NULL) {
182 ret = g_build_filename(cwd, fname, NULL);
183 }
184
185 return ret;
186 }
187
remove_temp_dir(char * dname)188 static void remove_temp_dir (char *dname)
189 {
190 # ifdef G_OS_WIN32
191 /* use of a full path is recommended */
192 gchar *fullpath = gretl_make_dotpath(dname);
193
194 if (gretl_chdir(gretl_dotdir()) == 0) {
195 gretl_deltree(fullpath);
196 }
197 g_free(fullpath);
198 # else
199 if (gretl_chdir(gretl_dotdir()) == 0) {
200 gretl_deltree(dname);
201 }
202 # endif
203 }
204
205 # ifdef G_OS_WIN32
206
gretl_make_tempdir(char * dname)207 static int gretl_make_tempdir (char *dname)
208 {
209 strcpy(dname, ".gretl-ssheet-XXXXXX");
210 mktemp(dname);
211
212 if (*dname == '\0') {
213 return E_FOPEN;
214 } else {
215 return gretl_mkdir(dname);
216 }
217 }
218
219 # else
220
gretl_make_tempdir(char * dname)221 static int gretl_make_tempdir (char *dname)
222 {
223 char *s;
224 int err = 0;
225
226 strcpy(dname, ".gretl-ssheet-XXXXXX");
227 s = mkdtemp(dname);
228
229 if (s == NULL) {
230 gretl_errmsg_set_from_errno("gretl_make_tempdir", errno);
231 err = E_FOPEN;
232 }
233
234 return err;
235 }
236
237 # endif /* G_OS_WIN32 or not */
238
239 /* For ODS and XLSX: unzip the target file in the user's
240 "dotdir". On successful completion @dname holds the
241 name of the temporary subdirectory, in the dotdir,
242 holding the contents of the zipfile.
243 */
244
open_import_zipfile(const char * fname,char * dname,PRN * prn)245 static int open_import_zipfile (const char *fname, char *dname,
246 PRN *prn)
247 {
248 const char *real_fname = fname;
249 gchar *abspath = NULL;
250 int err = 0;
251
252 errno = 0;
253 *dname = '\0';
254
255 if (gretl_test_fopen(real_fname, "r") != 0) {
256 return E_FOPEN;
257 }
258
259 /* by doing chdir, we may lose track of the file if
260 its path is relative */
261 if (!g_path_is_absolute(real_fname)) {
262 abspath = get_absolute_path(real_fname);
263 if (abspath != NULL) {
264 real_fname = abspath;
265 }
266 }
267
268 /* cd to dotdir and make temporary dir */
269 if (gretl_chdir(gretl_dotdir()) != 0) {
270 err = E_FOPEN;
271 } else {
272 err = gretl_make_tempdir(dname);
273 if (!err) {
274 err = gretl_chdir(dname);
275 if (err) {
276 gretl_remove(dname);
277 }
278 }
279 }
280
281 if (!err) {
282 /* if all has gone OK, we're now in the temporary
283 directory under dotdir, and @real_fname is the
284 absolute path to the file to be unzipped.
285 */
286 err = gretl_unzip(real_fname);
287 if (err) {
288 pprintf(prn, "gretl_unzip: %s\n", gretl_errmsg_get());
289 }
290 }
291
292 g_free(abspath);
293
294 return err;
295 }
296
297 #else /* !ODS, !XLSX */
298
299 # ifndef GNUMERIC_IMPORTER
300
worksheet_start_dataset(DATASET * newinfo)301 static int worksheet_start_dataset (DATASET *newinfo)
302 {
303 if (newinfo->v == 1) {
304 /* only the constant is present! */
305 gretl_errmsg_set(_("No numeric data were found"));
306 return E_DATA;
307 } else {
308 /* create import dataset */
309 return start_new_Z(newinfo, 0);
310 }
311 }
312
313 static int
importer_dates_check(char ** labels,BookFlag * pflags,DATASET * newset,PRN * prn,int * err)314 importer_dates_check (char **labels, BookFlag *pflags,
315 DATASET *newset, PRN *prn,
316 int *err)
317 {
318 int d, t;
319 char dstr[12];
320 char *s;
321 int ret = 0;
322
323 for (t=0; t<newset->n; t++) {
324 s = labels[t];
325 if (s == NULL || *s == '\0') {
326 fprintf(stderr, "importer_dates_check: got blank label\n");
327 return 0;
328 }
329 }
330
331 *err = dataset_allocate_obs_markers(newset);
332 if (*err) {
333 return 0;
334 }
335
336 for (t=0; t<newset->n && !*err; t++) {
337 s = labels[t];
338 if (*s == '"' || *s == '\'') s++;
339 if (*pflags & BOOK_NUMERIC_DATES) {
340 if (sscanf(s, "%d", &d)) {
341 MS_excel_date_string(dstr, d, 0, *pflags & BOOK_DATE_BASE_1904);
342 s = dstr;
343 } else {
344 pprintf(prn, "Bad date on row %d: '%s'\n", t+1, s);
345 *err = E_DATA;
346 }
347 }
348 strncat(newset->S[t], s, OBSLEN - 1);
349 }
350
351 if (!*err) {
352 int reversed = 0;
353
354 ret = test_markers_for_dates(newset, &reversed, NULL, prn);
355 if (reversed) {
356 *pflags |= BOOK_DATA_REVERSED;
357 } else if (ret < 0) {
358 /* not really time series */
359 fprintf(stderr, "importer_dates_check: scrubbing time series\n");
360 }
361 }
362
363 if (newset->markers != DAILY_DATE_STRINGS) {
364 dataset_destroy_obs_markers(newset);
365 }
366
367 return ret;
368 }
369
370 # endif /* !gnumeric */
371
wbook_print_info(wbook * book)372 static void wbook_print_info (wbook *book)
373 {
374 int i;
375
376 fprintf(stderr, "Found %d sheet%s\n", book->nsheets,
377 (book->nsheets > 1)? "s" : "");
378
379 for (i=0; i<book->nsheets; i++) {
380 if (book->byte_offsets != NULL) {
381 fprintf(stderr, "%d: '%s' at offset %u\n", i,
382 book->sheetnames[i], book->byte_offsets[i]);
383 } else {
384 fprintf(stderr, "%d: '%s'\n", i, book->sheetnames[i]);
385 }
386 }
387 }
388
wbook_free(wbook * book)389 static void wbook_free (wbook *book)
390 {
391 int i;
392
393 for (i=0; i<book->nsheets; i++) {
394 free(book->sheetnames[i]);
395 }
396 free(book->sheetnames);
397 free(book->targname);
398 free(book->byte_offsets);
399 free(book->xf_list);
400 }
401
wbook_check_params(wbook * book)402 static int wbook_check_params (wbook *book)
403 {
404 if (book->targname != NULL) {
405 int i;
406
407 book->selected = -1;
408 for (i=0; i<book->nsheets; i++) {
409 if (!strcmp(book->targname, book->sheetnames[i])) {
410 book->selected = i;
411 }
412 }
413 if (book->selected < 0) {
414 gretl_errmsg_sprintf("\"%s\": no such sheet", book->targname);
415 return E_DATA;
416 }
417 }
418
419 if (book->selected < 0 || book->selected >= book->nsheets) {
420 return E_DATA;
421 } else if (book->col_offset < 0 || book->row_offset < 0) {
422 return E_DATA;
423 } else {
424 return 0;
425 }
426 }
427
wbook_record_params(wbook * book,int * list)428 static void wbook_record_params (wbook *book, int *list)
429 {
430 if (list != NULL && list[0] == 3) {
431 list[1] = book->selected + 1;
432 list[2] = book->col_offset;
433 list[3] = book->row_offset;
434 }
435 }
436
437 #endif /* !ODS_IMPORTER */
438
439 /* @list may contain sheet number, row and/or column offset;
440 @sheetname may contain the name of a specific sheet; but
441 both may be NULL
442 */
443
wbook_init(wbook * book,const int * list,char * sheetname)444 static void wbook_init (wbook *book, const int *list, char *sheetname)
445 {
446 book->version = 0;
447 book->nsheets = 0;
448 book->col_offset = book->row_offset = 0;
449 book->targname = NULL;
450 book->sheetnames = NULL;
451 book->byte_offsets = NULL;
452 book->selected = 0;
453 book->flags = 0;
454 book->xf_list = NULL;
455 book->get_min_offset = NULL;
456 book->data = NULL;
457
458 if (sheetname != NULL && *sheetname != '\0') {
459 book->targname = gretl_strdup(sheetname);
460 tailstrip(book->targname);
461 }
462
463 if (list != NULL && list[0] == 3) {
464 if (book->targname == NULL && list[1] > 0) {
465 book->selected = list[1] - 1;
466 }
467 book->col_offset = list[2];
468 book->row_offset = list[3];
469 }
470 }
471
column_label(int col)472 static const char *column_label (int col)
473 {
474 const char *abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
475 static char label[5];
476 char a1, a2;
477
478 if (col < 26) {
479 a2 = abc[col];
480 sprintf(label, "(%c)", a2);
481 } else {
482 a1 = abc[col / 26 - 1];
483 a2 = abc[col % 26];
484 sprintf(label, "(%c%c)", a1, a2);
485 }
486
487 return label;
488 }
489
colspin_changed(GtkEditable * ed,GtkWidget * w)490 static void colspin_changed (GtkEditable *ed, GtkWidget *w)
491 {
492 const gchar *text = gtk_entry_get_text(GTK_ENTRY(ed));
493
494 if (text != NULL && isdigit((unsigned char) *text)) {
495 int col = atoi(text);
496
497 if (col > 0 && col < 257) {
498 gtk_label_set_text(GTK_LABEL(w), column_label(col - 1));
499 }
500 }
501 }
502
503 #ifdef EXCEL_IMPORTER
504 # ifndef WIN32
505
debug_infobox(const gchar * msg,GtkWidget * parent)506 static void debug_infobox (const gchar *msg, GtkWidget *parent)
507 {
508 GtkWidget *dialog;
509
510 dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
511 GTK_DIALOG_DESTROY_WITH_PARENT,
512 GTK_MESSAGE_INFO,
513 GTK_BUTTONS_CLOSE,
514 "%s",
515 msg);
516 gtk_dialog_run(GTK_DIALOG(dialog));
517 gtk_widget_destroy(dialog);
518 }
519
debug_callback(GtkWidget * w,wbook * book)520 static void debug_callback (GtkWidget *w, wbook *book)
521 {
522 static int done;
523
524 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))) {
525 book_set_debug(book);
526 }
527
528 if (book_debugging(book) && !done) {
529 gchar *msg = g_strdup_printf(_("Sending debugging output to %s"),
530 "stderr");
531
532 debug_infobox(msg, gtk_widget_get_toplevel(w));
533 g_free(msg);
534 done = 1;
535 }
536 }
537
538 # endif /* !WIN32 */
539 #endif /* EXCEL_IMPORTER */
540
book_get_min_offset(wbook * book,int k)541 static int book_get_min_offset (wbook *book, int k)
542 {
543 if (book->get_min_offset != NULL) {
544 return book->get_min_offset(book, k);
545 } else {
546 return 1;
547 }
548 }
549
550 static
wsheet_menu_select_row(GtkTreeSelection * selection,wbook * book)551 void wsheet_menu_select_row (GtkTreeSelection *selection, wbook *book)
552 {
553 GtkTreeModel *model;
554 GtkTreeIter iter;
555 GtkTreePath *path;
556 gint *idx;
557
558 gtk_tree_selection_get_selected(selection, &model, &iter);
559 path = gtk_tree_model_get_path(model, &iter);
560 idx = gtk_tree_path_get_indices(path);
561
562 if (book->selected != idx[0]) {
563 int offmin, offcurr;
564
565 book->selected = idx[0];
566
567 offmin = book_get_min_offset(book, COL_OFFSET);
568 offcurr = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(book->colspin));
569 gtk_spin_button_set_range(GTK_SPIN_BUTTON(book->colspin), offmin, 256);
570 if (offcurr != offmin) {
571 gtk_spin_button_set_value(GTK_SPIN_BUTTON(book->colspin),
572 offmin);
573 }
574
575 offmin = book_get_min_offset(book, ROW_OFFSET);
576 offcurr = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(book->rowspin));
577 gtk_spin_button_set_range(GTK_SPIN_BUTTON(book->rowspin), offmin, 256);
578 if (offcurr != offmin) {
579 gtk_spin_button_set_value(GTK_SPIN_BUTTON(book->rowspin),
580 offmin);
581 }
582 }
583 }
584
585 static
wsheet_menu_make_list(GtkTreeView * view,wbook * book)586 void wsheet_menu_make_list (GtkTreeView *view, wbook *book)
587 {
588 GtkTreeModel *model = gtk_tree_view_get_model(view);
589 GtkTreeIter iter;
590 int i;
591
592 gtk_list_store_clear(GTK_LIST_STORE(model));
593 gtk_tree_model_get_iter_first(model, &iter);
594
595 for (i=0; i<book->nsheets; i++) {
596 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
597 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
598 0, book->sheetnames[i], -1);
599 }
600
601 gtk_tree_model_get_iter_first(model, &iter);
602 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(view),
603 &iter);
604 }
605
606 static
wsheet_menu_cancel(GtkWidget * w,wbook * book)607 void wsheet_menu_cancel (GtkWidget *w, wbook *book)
608 {
609 book->selected = -1;
610 }
611
612 static
wbook_set_col_offset(GtkWidget * w,wbook * book)613 void wbook_set_col_offset (GtkWidget *w, wbook *book)
614 {
615 book->col_offset = gtk_spin_button_get_value_as_int
616 (GTK_SPIN_BUTTON(book->colspin)) - 1;
617 }
618
619 static
wbook_set_row_offset(GtkWidget * w,wbook * book)620 void wbook_set_row_offset (GtkWidget *w, wbook *book)
621 {
622 book->row_offset = gtk_spin_button_get_value_as_int
623 (GTK_SPIN_BUTTON(book->rowspin)) - 1;
624 }
625
626 static
add_sheets_list(GtkWidget * vbox,wbook * book)627 void add_sheets_list (GtkWidget *vbox, wbook *book)
628 {
629 GtkWidget *label, *view, *sw, *hsep;
630 GtkListStore *store;
631 GtkTreeSelection *select;
632 GtkCellRenderer *renderer;
633 GtkTreeViewColumn *column;
634
635 store = gtk_list_store_new(1, G_TYPE_STRING);
636 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
637 g_object_unref(G_OBJECT(store));
638
639 renderer = gtk_cell_renderer_text_new();
640 g_object_set(renderer, "ypad", 0, NULL);
641 column = gtk_tree_view_column_new_with_attributes(NULL,
642 renderer,
643 "text",
644 0, NULL);
645 gtk_tree_view_append_column(GTK_TREE_VIEW(view), column);
646 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
647
648 select = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
649 gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
650 g_signal_connect(G_OBJECT(select), "changed",
651 G_CALLBACK(wsheet_menu_select_row),
652 book);
653
654 wsheet_menu_make_list(GTK_TREE_VIEW(view), book);
655
656 /* now set up the widgets */
657
658 hsep = gtk_hseparator_new();
659 gtk_box_pack_start(GTK_BOX(vbox), hsep, FALSE, FALSE, 5);
660
661 label = gtk_label_new(_("Sheet to import:"));
662 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
663
664 sw = gtk_scrolled_window_new(NULL, NULL);
665 gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 5);
666
667 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
668 GTK_POLICY_AUTOMATIC,
669 GTK_POLICY_AUTOMATIC);
670 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
671 GTK_SHADOW_IN);
672 gtk_container_add(GTK_CONTAINER(sw), view);
673 }
674
make_wmenu_modal(GtkWidget * w,gpointer p)675 static void make_wmenu_modal (GtkWidget *w, gpointer p)
676 {
677 gtk_window_set_modal(GTK_WINDOW(w), TRUE);
678 }
679
esc_cancels(GtkWidget * w,GdkEventKey * key,wbook * book)680 gboolean esc_cancels (GtkWidget *w, GdkEventKey *key, wbook *book)
681 {
682 if (key->keyval == GDK_Escape) {
683 if (book != NULL) {
684 book->selected = -1;
685 }
686 gtk_widget_destroy(w);
687 return TRUE;
688 } else {
689 return FALSE;
690 }
691 }
692
wsheet_menu(wbook * book,int multisheet)693 static void wsheet_menu (wbook *book, int multisheet)
694 {
695 GtkWidget *w, *tmp, *label;
696 GtkWidget *vbox, *hbox;
697 GtkAdjustment *c_adj, *r_adj;
698 int offmin;
699
700 w = gtk_dialog_new();
701 gtk_window_set_title(GTK_WINDOW(w), _("gretl: spreadsheet import"));
702
703 g_signal_connect_after(G_OBJECT(w), "delete_event",
704 G_CALLBACK(wsheet_menu_cancel), book);
705 g_signal_connect(G_OBJECT(w), "destroy",
706 G_CALLBACK(gtk_main_quit), NULL);
707 g_signal_connect(G_OBJECT(w), "realize",
708 G_CALLBACK(make_wmenu_modal), NULL);
709
710 vbox = gtk_dialog_get_content_area(GTK_DIALOG(w));
711 gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
712
713 /* selection of starting column and row */
714 label = gtk_label_new(_("Start import at:"));
715 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
716
717 hbox = gtk_hbox_new(FALSE, 5);
718 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
719
720 /* starting column spinner */
721 tmp = gtk_label_new(_("column:"));
722 offmin = book->col_offset + 1;
723 c_adj = (GtkAdjustment *) gtk_adjustment_new(offmin, offmin, 256, 1, 1, 0);
724 book->colspin = gtk_spin_button_new(c_adj, 1, 0);
725 g_signal_connect(c_adj, "value_changed",
726 G_CALLBACK(wbook_set_col_offset), book);
727 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(book->colspin),
728 GTK_UPDATE_IF_VALID);
729 gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 5);
730 gtk_box_pack_start(GTK_BOX(hbox), book->colspin, FALSE, FALSE, 5);
731
732 /* starting row spinner */
733 tmp = gtk_label_new(_("row:"));
734 offmin = book->row_offset + 1;
735 r_adj = (GtkAdjustment *) gtk_adjustment_new(offmin, offmin, 256, 1, 1, 0);
736 book->rowspin = gtk_spin_button_new(r_adj, 1, 0);
737 g_signal_connect(r_adj, "value_changed",
738 G_CALLBACK(wbook_set_row_offset), book);
739 gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(book->rowspin),
740 GTK_UPDATE_IF_VALID);
741 gtk_box_pack_start(GTK_BOX(hbox), tmp, FALSE, FALSE, 5);
742 gtk_box_pack_start(GTK_BOX(hbox), book->rowspin, FALSE, FALSE, 5);
743
744 /* column label feedback */
745 hbox = gtk_hbox_new(FALSE, 5);
746 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
747 label = gtk_label_new("(A)");
748 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
749 g_signal_connect(GTK_EDITABLE(book->colspin), "changed",
750 G_CALLBACK(colspin_changed), label);
751
752 /* choose the worksheet (if applicable) */
753 if (multisheet) {
754 add_sheets_list(vbox, book);
755 }
756
757 #if defined(EXCEL_IMPORTER) && !defined(G_OS_WIN32)
758 tmp = gtk_check_button_new_with_label(_("Produce debugging output"));
759 g_signal_connect(G_OBJECT(tmp), "toggled", G_CALLBACK(debug_callback),
760 book);
761 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tmp), FALSE);
762 gtk_box_pack_start(GTK_BOX(vbox), tmp, FALSE, FALSE, 5);
763 #endif
764
765 hbox = gtk_dialog_get_action_area(GTK_DIALOG(w));
766 gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
767 gtk_box_set_spacing(GTK_BOX(hbox), 10);
768
769 /* Cancel button */
770 tmp = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
771 gtk_container_add(GTK_CONTAINER(hbox), tmp);
772 g_signal_connect(G_OBJECT (tmp), "clicked",
773 G_CALLBACK(wsheet_menu_cancel), book);
774 g_signal_connect_swapped(G_OBJECT (tmp), "clicked",
775 G_CALLBACK (gtk_widget_destroy),
776 G_OBJECT (w));
777
778 /* OK button */
779 tmp = gtk_button_new_from_stock(GTK_STOCK_OK);
780 gtk_container_add(GTK_CONTAINER(hbox), tmp);
781 g_signal_connect_swapped(G_OBJECT (tmp), "clicked",
782 G_CALLBACK (gtk_widget_destroy),
783 G_OBJECT (w));
784 gtk_widget_set_can_default(tmp, TRUE);
785 gtk_widget_grab_default(tmp);
786
787 g_signal_connect(G_OBJECT(w), "key-press-event",
788 G_CALLBACK(esc_cancels), book);
789
790 gtk_entry_set_activates_default(GTK_ENTRY(book->colspin), TRUE);
791 gtk_entry_set_activates_default(GTK_ENTRY(book->rowspin), TRUE);
792
793 gtk_widget_show_all(w);
794 gtk_main();
795 }
796