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 "gretl.h"
21 #include "winstack.h"
22 #include "textbuf.h"
23 #include "tabwin.h"
24
25 #ifdef G_OS_WIN32
26 # include "gretlwin32.h"
27 #endif
28
29 #define TDEBUG 0
30
31 struct tabwin_t_ {
32 int role; /* what's tabwin doing? */
33 GtkWidget *main; /* top-level GTK window */
34 GtkWidget *hbox; /* horizontal box to hold menu bar */
35 GtkWidget *mbar; /* menu bar */
36 GtkWidget *tabs; /* notebook for tabs */
37 GtkWidget *dialog; /* associated dialog */
38 GtkWidget *dlg_owner; /* the tab that "owns" dialog */
39 };
40
41 GtkTargetEntry tabwin_drag_targets[] = {
42 { "text/uri-list", 0, GRETL_FILENAME },
43 };
44
45 /* We support a tabbed editor for hansl scripts, one for
46 "alt" (foreign) scripts, and also a tabbed viewer for
47 gretl models.
48 */
49
50 static tabwin_t *tabhansl;
51 static tabwin_t *tabalt;
52 static tabwin_t *tabmod;
53
54 static void undock_tabbed_viewer (GtkWidget *w, windata_t *vwin);
55
tabwin_destroy(GtkWidget * w,tabwin_t * tabwin)56 static void tabwin_destroy (GtkWidget *w, tabwin_t *tabwin)
57 {
58 if (tabwin == tabhansl) {
59 tabhansl = NULL;
60 } else if (tabwin == tabalt) {
61 tabalt = NULL;
62 } else if (tabwin == tabmod) {
63 tabmod = NULL;
64 }
65
66 free(tabwin);
67 }
68
vwin_get_tabwin(windata_t * vwin)69 static tabwin_t *vwin_get_tabwin (windata_t *vwin)
70 {
71 return g_object_get_data(G_OBJECT(vwin->topmain), "tabwin");
72 }
73
maybe_block_tabedit_quit(tabwin_t * tabwin,GtkWidget * parent)74 static gboolean maybe_block_tabedit_quit (tabwin_t *tabwin,
75 GtkWidget *parent)
76 {
77 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
78 int np = gtk_notebook_get_n_pages(notebook);
79 gboolean ret = FALSE;
80
81 if (np > 1) {
82 gchar *msg = g_strdup_printf(_("Editing %d scripts: really quit?"), np);
83 gint resp;
84
85 gtk_window_present(GTK_WINDOW(parent));
86 resp = yes_no_dialog(_("gretl: script editor"), msg, parent);
87 if (resp != GRETL_YES) {
88 ret = TRUE;
89 }
90 g_free(msg);
91 } else if (np == 1) {
92 GtkWidget *page = gtk_notebook_get_nth_page(notebook, 0);
93
94 if (page != NULL) {
95 windata_t *vwin = g_object_get_data(G_OBJECT(page), "vwin");
96
97 if (vwin_content_changed(vwin)) {
98 gtk_window_present(GTK_WINDOW(parent));
99 ret = query_save_text(NULL, NULL, vwin);
100 }
101 }
102 }
103
104 return ret;
105 }
106
107 /* called from winstack.c on program exit: @w will
108 be the top-level of a tabbed window */
109
tabwin_exit_check(GtkWidget * w)110 gboolean tabwin_exit_check (GtkWidget *w)
111 {
112 tabwin_t *tabwin = g_object_get_data(G_OBJECT(w), "tabwin");
113
114 if (tabwin->role != EDIT_HANSL &&
115 !editing_alt_script(tabwin->role)) {
116 return FALSE;
117 }
118
119 return maybe_block_tabedit_quit(tabwin, w);
120 }
121
122 /* called on delete-event: @w is the top-level */
123
tabedit_quit_check(GtkWidget * w,GdkEvent * event,tabwin_t * tabwin)124 static gboolean tabedit_quit_check (GtkWidget *w, GdkEvent *event,
125 tabwin_t *tabwin)
126 {
127 if (tabwin->role != EDIT_HANSL &&
128 !editing_alt_script(tabwin->role)) {
129 return FALSE;
130 }
131
132 return maybe_block_tabedit_quit(tabwin, w);
133 }
134
135 /* activate or de-activate a tab's closer button */
136
viewer_tab_show_closer(GtkNotebook * notebook,GtkWidget * tab,gboolean show)137 static void viewer_tab_show_closer (GtkNotebook *notebook,
138 GtkWidget *tab,
139 gboolean show)
140 {
141 GtkWidget *lbl = gtk_notebook_get_tab_label(notebook, tab);
142 GtkWidget *button = g_object_get_data(G_OBJECT(lbl), "closer");
143
144 if (button != NULL) {
145 if (show) {
146 gtk_widget_show(button);
147 } else {
148 gtk_widget_hide(button);
149 }
150 }
151 }
152
tabwin_remove_toolbar(tabwin_t * tabwin)153 static void tabwin_remove_toolbar (tabwin_t *tabwin)
154 {
155 #if TDEBUG
156 fprintf(stderr, "*** tabwin_remove_toolbar: removing toolbar at %p\n",
157 (void *) tabwin->mbar);
158 #endif
159 gtk_container_remove(GTK_CONTAINER(tabwin->hbox),
160 tabwin->mbar);
161 tabwin->mbar = NULL;
162 }
163
tabwin_insert_toolbar(tabwin_t * tabwin,windata_t * vwin)164 static void tabwin_insert_toolbar (tabwin_t *tabwin, windata_t *vwin)
165 {
166 #if TDEBUG
167 fprintf(stderr, "*** tabwin_insert_toolbar: inserting toolbar at %p\n",
168 (void *) vwin->mbar);
169 #endif
170 gtk_box_pack_start(GTK_BOX(tabwin->hbox), vwin->mbar,
171 TRUE, TRUE, 0);
172 gtk_widget_show_all(vwin->mbar);
173 tabwin->mbar = vwin->mbar;
174 }
175
page_removed_callback(GtkNotebook * notebook,GtkWidget * child,gint pgnum,gpointer data)176 static void page_removed_callback (GtkNotebook *notebook,
177 GtkWidget *child,
178 gint pgnum,
179 gpointer data)
180 {
181 int np = gtk_notebook_get_n_pages(notebook);
182
183 #if TDEBUG
184 fprintf(stderr, "*** page_removed_callback: child=%p\n", (void *) child);
185 #endif
186
187 if (np < 5) {
188 gtk_notebook_popup_disable(notebook);
189 }
190
191 if (np == 1) {
192 /* only one tab left after removal: this page should
193 not display its own closer button, nor should it
194 be detachable
195 */
196 GtkWidget *tab = gtk_notebook_get_nth_page(notebook, 0);
197
198 gtk_notebook_set_tab_detachable(notebook, tab, FALSE);
199 viewer_tab_show_closer(notebook, tab, FALSE);
200 }
201 }
202
page_added_callback(GtkNotebook * notebook,GtkWidget * child,gint pgnum,gpointer data)203 static void page_added_callback (GtkNotebook *notebook,
204 GtkWidget *child,
205 gint pgnum,
206 gpointer data)
207 {
208 int i, np = gtk_notebook_get_n_pages(notebook);
209 GtkWidget *tab;
210
211 if (np >= 5) {
212 gtk_notebook_popup_enable(notebook);
213 }
214
215 if (np > 1) {
216 for (i=0; i<np; i++) {
217 tab = gtk_notebook_get_nth_page(notebook, i);
218 gtk_notebook_set_tab_detachable(notebook, tab, TRUE);
219 viewer_tab_show_closer(notebook, tab, TRUE);
220 }
221 } else {
222 tab = gtk_notebook_get_nth_page(notebook, 0);
223 viewer_tab_show_closer(notebook, tab, FALSE);
224 }
225 }
226
227 /* callback for tab-specific close button: this should be
228 invoked only if there's at least one tab left after
229 trashing the selected one
230 */
231
tabwin_tab_close(GtkWidget * w,windata_t * vwin)232 static void tabwin_tab_close (GtkWidget *w, windata_t *vwin)
233 {
234 tabwin_t *tabwin = vwin_get_tabwin(vwin);
235 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
236 gint pg = gtk_notebook_page_num(notebook, vwin->main);
237
238 #if TDEBUG
239 fprintf(stderr, "*** tabwin_tab_close: vwin = %p\n", (void *) vwin);
240 #endif
241
242 if (vwin->main == tabwin->dlg_owner) {
243 /* this tab has a dialog open: don't close it */
244 gtk_window_present(GTK_WINDOW(tabwin->dialog));
245 return;
246 }
247
248 if ((tabwin->role == EDIT_HANSL ||
249 editing_alt_script(tabwin->role)) &&
250 vwin_content_changed(vwin)) {
251 gint cancel = query_save_text(NULL, NULL, vwin);
252
253 if (cancel) {
254 return;
255 }
256 }
257
258 /* note: vwin->mbar is packed under tabwin, so it will not
259 get destroyed automatically when the page is removed
260 */
261 if (tabwin->mbar != NULL && tabwin->mbar == vwin->mbar) {
262 tabwin_remove_toolbar(tabwin);
263 }
264
265 /* relinquish the extra reference */
266 #if TDEBUG
267 fprintf(stderr, " unrefing toolbar at %p\n", (void *) vwin->mbar);
268 #endif
269 g_object_unref(vwin->mbar);
270
271 gtk_notebook_remove_page(notebook, pg);
272 }
273
tabwin_tab_destroy(windata_t * vwin)274 void tabwin_tab_destroy (windata_t *vwin)
275 {
276 tabwin_t *tabwin = vwin_get_tabwin(vwin);
277 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
278
279 #if TDEBUG
280 fprintf(stderr, "*** tabwin_tab_destroy: vwin = %p\n", (void *) vwin);
281 #endif
282
283 if (gtk_notebook_get_n_pages(notebook) > 1) {
284 gint pg = gtk_notebook_page_num(notebook, vwin->main);
285
286 if (tabwin->mbar != NULL && tabwin->mbar == vwin->mbar) {
287 tabwin_remove_toolbar(tabwin);
288 }
289 #if 0
290 /* 2020-11-27: this seems to be erroneous */
291 g_object_unref(vwin->mbar);
292 #endif
293 vwin->mbar = NULL;
294 gtk_notebook_remove_page(notebook, pg);
295 } else {
296 gtk_widget_destroy(vwin->topmain);
297 }
298 }
299
300 /* on switching the current page, put the new page's
301 toolbar into place in tabwin (and remove the old
302 one, if present)
303 */
304
switch_page_callback(GtkNotebook * tabs,gpointer arg1,gint pgnum,tabwin_t * tabwin)305 static gboolean switch_page_callback (GtkNotebook *tabs,
306 gpointer arg1,
307 gint pgnum,
308 tabwin_t *tabwin)
309 {
310 GtkWidget *tab = gtk_notebook_get_nth_page(tabs, pgnum);
311 windata_t *vwin = NULL;
312
313 if (tab != NULL) {
314 vwin = g_object_get_data(G_OBJECT(tab), "vwin");
315 }
316
317 #if TDEBUG
318 fprintf(stderr, "*** switch_page_callback: tab=%p, vwin=%p\n",
319 (void *) tab, (void *) vwin);
320 #endif
321
322 if (vwin != NULL) {
323 if (tabwin->mbar != NULL && tabwin->mbar != vwin->mbar) {
324 /* there's an "old" toolbar in place */
325 tabwin_remove_toolbar(tabwin);
326 }
327 if (vwin->mbar != NULL && vwin->mbar != tabwin->mbar) {
328 /* a "new" toolbar should be shown */
329 tabwin_insert_toolbar(tabwin, vwin);
330 }
331 }
332
333 return FALSE;
334 }
335
336 /* callback for the "create-window" signal */
337
detach_tab_callback(GtkNotebook * book,GtkWidget * page,gint x,gint y,gpointer data)338 static GtkNotebook *detach_tab_callback (GtkNotebook *book,
339 GtkWidget *page,
340 gint x, gint y,
341 gpointer data)
342 {
343 windata_t *vwin = g_object_get_data(G_OBJECT(page), "vwin");
344
345 if (vwin != NULL) {
346 undock_tabbed_viewer(NULL, vwin);
347 }
348
349 /* return NULL since we're not adding the detached
350 tab to another GtkNotebook */
351
352 return NULL;
353 }
354
355 /* avoid excessive padding in a tab's close button */
356
no_button_padding(GtkWidget * w)357 static void no_button_padding (GtkWidget *w)
358 {
359 static int style_done;
360
361 gtk_widget_set_name(w, "closer");
362
363 if (!style_done) {
364 gtk_rc_parse_string("style \"closer-style\"\n{\n"
365 " GtkWidget::focus-padding = 0\n"
366 " GtkWidget::focus-line-width = 0\n"
367 " xthickness = 0\n"
368 " ythickness = 0\n"
369 "}\n"
370 "widget \"*.closer\" style \"closer-style\"");
371 style_done = 1;
372 }
373 }
374
375 /* put a tab-specific close button next to the tab's label */
376
viewer_tab_add_closer(GtkWidget * tab,windata_t * vwin)377 static void viewer_tab_add_closer (GtkWidget *tab, windata_t *vwin)
378 {
379 GtkWidget *img, *button;
380
381 img = gtk_image_new_from_stock(GTK_STOCK_CLOSE,
382 GTK_ICON_SIZE_MENU);
383 button = gtk_button_new();
384 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
385 gtk_container_set_border_width(GTK_CONTAINER(button), 0);
386 no_button_padding(button);
387 gtk_container_add(GTK_CONTAINER(button), img);
388 g_signal_connect(G_OBJECT(button), "clicked",
389 G_CALLBACK(tabwin_tab_close),
390 vwin);
391 gtk_container_add(GTK_CONTAINER(tab), button);
392 g_object_set_data(G_OBJECT(tab), "closer", button);
393 }
394
395 /* try to ensure unique dummy title strings for unsaved
396 new scripts */
397
untitled_title(tabwin_t * tabwin)398 static gchar *untitled_title (tabwin_t *tabwin)
399 {
400 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
401 GtkWidget *page;
402 windata_t *vwin;
403 int i, np, idx = 0;
404
405 np = gtk_notebook_get_n_pages(notebook);
406
407 for (i=0; i<np; i++) {
408 page = gtk_notebook_get_nth_page(notebook, i);
409 vwin = g_object_get_data(G_OBJECT(page), "vwin");
410 if (strstr(vwin->fname, "script_tmp") != NULL) {
411 idx++;
412 }
413 }
414
415 if (idx > 0) {
416 return g_strdup_printf("%s(%d)", _("untitled"), idx);
417 } else {
418 return g_strdup(_("untitled"));
419 }
420 }
421
422 /* create and add tab with filename and closer button */
423
make_viewer_tab(tabwin_t * tabwin,windata_t * vwin,const gchar * info)424 static GtkWidget *make_viewer_tab (tabwin_t *tabwin,
425 windata_t *vwin,
426 const gchar *info)
427 {
428 GtkNotebook *notebook;
429 gchar *title = NULL;
430 const gchar *fname = NULL;
431 GtkWidget *tab;
432 GtkWidget *label;
433 GtkWidget *mlabel;
434
435 tab = gtk_hbox_new(FALSE, 5);
436 gtk_container_set_border_width(GTK_CONTAINER(tab), 0);
437
438 if (tabwin->role == EDIT_HANSL || editing_alt_script(tabwin->role)) {
439 if (strstr(info, "script_tmp") != NULL) {
440 title = untitled_title(tabwin);
441 } else {
442 title = title_from_filename(info, tabwin->role, FALSE);
443 fname = info;
444 }
445 } else if (info != NULL) {
446 title = g_strdup(info);
447 } else {
448 title = g_strdup("unknown");
449 }
450
451 label = gtk_label_new(title);
452 mlabel = gtk_label_new(fname == NULL ? title : fname);
453 gtk_container_add(GTK_CONTAINER(tab), label);
454 g_object_set_data(G_OBJECT(tab), "label", label);
455 g_object_set_data(G_OBJECT(tab), "mlabel", mlabel);
456 g_free(title);
457
458 viewer_tab_add_closer(tab, vwin);
459 gtk_widget_set_size_request(tab, -1, 18);
460 gtk_widget_show_all(tab);
461
462 notebook = GTK_NOTEBOOK(tabwin->tabs);
463 gtk_notebook_append_page_menu(notebook, vwin->main,
464 tab, mlabel);
465
466 return tab;
467 }
468
469 /* Note: provides a means of connecting catch_viewer_key(),
470 for a viewer that's embedded in a GtkNotebook.
471 */
472
catch_tabwin_key(GtkWidget * w,GdkEventKey * key,tabwin_t * tabwin)473 static gint catch_tabwin_key (GtkWidget *w, GdkEventKey *key,
474 tabwin_t *tabwin)
475 {
476 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
477 gint pg = gtk_notebook_get_current_page(notebook);
478 GtkWidget *tab = gtk_notebook_get_nth_page(notebook, pg);
479 windata_t *vwin = g_object_get_data(G_OBJECT(tab), "vwin");
480
481 return catch_viewer_key(w, key, vwin);
482 }
483
484 static void
tabwin_handle_drag(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,gpointer p)485 tabwin_handle_drag (GtkWidget *widget,
486 GdkDragContext *context,
487 gint x,
488 gint y,
489 GtkSelectionData *data,
490 guint info,
491 guint time,
492 gpointer p)
493 {
494 const guchar *seldata = NULL;
495 gchar *dfname;
496 char tmp[MAXLEN];
497 int pos, skip = 5;
498
499 if (data != NULL) {
500 seldata = gtk_selection_data_get_data(data);
501 }
502
503 if (info != GRETL_FILENAME) {
504 return;
505 }
506
507 /* ignore the wrong sort of data */
508 if (data == NULL || (dfname = (gchar *) seldata) == NULL ||
509 strlen(dfname) <= 5 || strncmp(dfname, "file:", 5)) {
510 return;
511 }
512
513 if (strncmp(dfname, "file://", 7) == 0) skip = 7;
514 #ifdef G_OS_WIN32
515 if (strncmp(dfname, "file:///", 8) == 0) skip = 8;
516 #endif
517
518 /* there may be multiple files: we ignore all but the first */
519 *tmp = 0;
520 if ((pos = gretl_charpos('\r', dfname)) > 0 ||
521 (pos = gretl_charpos('\n', dfname) > 0)) {
522 strncat(tmp, dfname + skip, pos - skip);
523 } else {
524 strcat(tmp, dfname + skip);
525 }
526
527 /* handle spaces and such */
528 unescape_url(tmp);
529
530 if (has_suffix(tmp, ".inp")) {
531 /* FIXME generalize? */
532 set_tryfile(tmp);
533 do_open_script(EDIT_HANSL);
534 }
535 }
536
537 /* build a tabbed viewer/editor */
538
make_tabbed_viewer(int role)539 static tabwin_t *make_tabbed_viewer (int role)
540 {
541 tabwin_t *tabwin;
542 GtkWidget *vbox;
543
544 tabwin = mymalloc(sizeof *tabwin);
545
546 if (tabwin == NULL) {
547 return NULL;
548 }
549
550 tabwin->role = role;
551 tabwin->dialog = NULL;
552 tabwin->dlg_owner = NULL;
553
554 /* top-level window */
555 tabwin->main = gretl_gtk_window();
556 if (role == EDIT_HANSL) {
557 gtk_window_set_title(GTK_WINDOW(tabwin->main),
558 _("gretl: script editor"));
559 g_signal_connect(G_OBJECT(tabwin->main), "delete-event",
560 G_CALLBACK(tabedit_quit_check), tabwin);
561 } else if (editing_alt_script(role)) {
562 gtk_window_set_title(GTK_WINDOW(tabwin->main),
563 _("gretl: foreign script editor"));
564 g_signal_connect(G_OBJECT(tabwin->main), "delete-event",
565 G_CALLBACK(tabedit_quit_check), tabwin);
566 } else {
567 gtk_window_set_title(GTK_WINDOW(tabwin->main), _("gretl: models"));
568 }
569 g_signal_connect(G_OBJECT(tabwin->main), "destroy",
570 G_CALLBACK(tabwin_destroy), tabwin);
571 g_object_set_data(G_OBJECT(tabwin->main), "tabwin", tabwin);
572
573 /* vertically oriented container */
574 vbox = gtk_vbox_new(FALSE, 1);
575 gtk_box_set_spacing(GTK_BOX(vbox), 0);
576 gtk_container_set_border_width(GTK_CONTAINER(vbox), 0);
577 gtk_container_add(GTK_CONTAINER(tabwin->main), vbox);
578
579 /* box to hold menu bar */
580 tabwin->hbox = gtk_hbox_new(FALSE, 0);
581 gtk_box_pack_start(GTK_BOX(vbox), tabwin->hbox, FALSE, FALSE, 0);
582 tabwin->mbar = NULL;
583
584 /* notebook with its signal handlers */
585 tabwin->tabs = gtk_notebook_new();
586 gtk_notebook_popup_enable(GTK_NOTEBOOK(tabwin->tabs));
587 gtk_notebook_set_scrollable(GTK_NOTEBOOK(tabwin->tabs), TRUE);
588 g_signal_connect(G_OBJECT(tabwin->tabs), "switch-page",
589 G_CALLBACK(switch_page_callback), tabwin);
590 g_signal_connect(G_OBJECT(tabwin->tabs), "create-window",
591 G_CALLBACK(detach_tab_callback), tabwin);
592 g_signal_connect(G_OBJECT(tabwin->tabs), "page-added",
593 G_CALLBACK(page_added_callback), tabwin);
594 g_signal_connect(G_OBJECT(tabwin->tabs), "page-removed",
595 G_CALLBACK(page_removed_callback), tabwin);
596 gtk_container_add(GTK_CONTAINER(vbox), tabwin->tabs);
597
598 #ifndef G_OS_WIN32
599 set_wm_icon(tabwin->main);
600 #endif
601
602 return tabwin;
603 }
604
get_tabwin_for_role(int role,int * starting)605 static tabwin_t *get_tabwin_for_role (int role, int *starting)
606 {
607 tabwin_t *tabwin = NULL;
608
609 if (role == EDIT_HANSL) {
610 if (tabhansl != NULL) {
611 tabwin = tabhansl;
612 } else {
613 *starting = 1;
614 tabhansl = tabwin = make_tabbed_viewer(role);
615 }
616 } else if (editing_alt_script(role)) {
617 if (tabalt != NULL) {
618 tabwin = tabalt;
619 } else {
620 *starting = 1;
621 tabalt = tabwin = make_tabbed_viewer(role);
622 }
623 } else if (role == VIEW_MODEL) {
624 if (tabmod != NULL) {
625 tabwin = tabmod;
626 } else {
627 *starting = 1;
628 tabmod = tabwin = make_tabbed_viewer(role);
629 }
630 }
631
632 return tabwin;
633 }
634
635 /* Create a viewer/editor tab, as an alternative to a stand-alone
636 window. We build the tabbed top-level if need be, otherwise we
637 stick a new tab into the existing window.
638 */
639
viewer_tab_new(int role,const char * info,gpointer data)640 windata_t *viewer_tab_new (int role, const char *info,
641 gpointer data)
642 {
643 tabwin_t *tabwin;
644 windata_t *vwin;
645 int starting = 0;
646 gulong handler_id;
647
648 tabwin = get_tabwin_for_role(role, &starting);
649 if (tabwin == NULL) {
650 return NULL;
651 }
652
653 vwin = vwin_new(role, data);
654 if (vwin == NULL) {
655 return NULL;
656 }
657
658 vwin->flags = VWIN_TABBED;
659 vwin->main = gtk_hbox_new(FALSE, 0);
660 g_object_set_data(G_OBJECT(vwin->main), "vwin", vwin);
661 handler_id = g_signal_connect(G_OBJECT(vwin->main), "destroy",
662 G_CALLBACK(free_windata), vwin);
663 g_object_set_data(G_OBJECT(vwin->main), "destroy-id",
664 GUINT_TO_POINTER(handler_id));
665
666 #if TDEBUG
667 fprintf(stderr, "*** viewer_tab_new: vwin=%p, main hbox=%p\n",
668 (void *) vwin, (void *) vwin->main);
669 #endif
670
671 make_viewer_tab(tabwin, vwin, info);
672 vwin->topmain = tabwin->main;
673
674 vwin->vbox = gtk_vbox_new(FALSE, 1);
675 gtk_container_set_border_width(GTK_CONTAINER(vwin->vbox), 1);
676 gtk_container_add(GTK_CONTAINER(vwin->main), vwin->vbox);
677
678 if (starting) {
679 window_list_add(tabwin->main, role);
680 if (!vwin_editing_script(role)) {
681 g_signal_connect(G_OBJECT(tabwin->main), "key-press-event",
682 G_CALLBACK(catch_tabwin_key), tabwin);
683 vwin->flags |= WVIN_KEY_SIGNAL_SET;
684 }
685 }
686
687 return vwin;
688 }
689
690 /* called when a new editor tab is added: if this is the
691 first such tab then tabwin->mbar will be NULL, otherwise
692 if will be some other page's mbar, which will have to
693 be swapped out
694 */
695
tabwin_register_toolbar(windata_t * vwin)696 void tabwin_register_toolbar (windata_t *vwin)
697 {
698 tabwin_t *tabwin = vwin_get_tabwin(vwin);
699 gulong handler_id;
700
701 /* take out a reference to @vwin's toolbar to prevent
702 its auto-destruction; also ensure that the pointer
703 goes to NULL on destruction
704 */
705 g_object_ref(vwin->mbar);
706 handler_id = g_signal_connect(G_OBJECT(vwin->mbar), "destroy",
707 G_CALLBACK(gtk_widget_destroyed),
708 &vwin->mbar);
709 g_object_set_data(G_OBJECT(vwin->mbar), "destroy-id",
710 GUINT_TO_POINTER(handler_id));
711
712 #if TDEBUG
713 fprintf(stderr, "*** tabwin_register_toolbar: vwin=%p has toolbar=%p\n",
714 (void *) vwin, (void *) vwin->mbar);
715 fprintf(stderr, " new handler_id for mbar destruction callback: %lu\n",
716 handler_id);
717 #endif
718
719 if (tabwin->mbar != NULL) {
720 tabwin_remove_toolbar(tabwin);
721 }
722
723 tabwin_insert_toolbar(tabwin, vwin);
724 }
725
726 /* This is used, inter alia, for an "untitled" tab:
727 to set its real filename when it is saved
728 */
729
tabwin_tab_set_title(windata_t * vwin,const char * title)730 void tabwin_tab_set_title (windata_t *vwin, const char *title)
731 {
732 tabwin_t *tabwin = vwin_get_tabwin(vwin);
733 GtkWidget *tab, *label;
734
735 tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(tabwin->tabs),
736 vwin->main);
737
738 /* label shown on the tab itself */
739 label = g_object_get_data(G_OBJECT(tab), "label");
740 if (label != NULL) {
741 gtk_label_set_text(GTK_LABEL(label), title);
742 }
743
744 /* label shown in tab-switching right-click menu */
745 label = g_object_get_data(G_OBJECT(tab), "mlabel");
746 if (label != NULL) {
747 const gchar *mtext = title;
748
749 if (vwin_editing_script(tabwin->role) && vwin->fname[0] != '\0') {
750 mtext = vwin->fname;
751 }
752 gtk_label_set_text(GTK_LABEL(label), mtext);
753 }
754 }
755
756 /* set or unset the "modified flag" (trailing asterisk on
757 the filename) for the tab label for a page in tabbed
758 editor
759 */
760
tabwin_tab_set_status(windata_t * vwin)761 void tabwin_tab_set_status (windata_t *vwin)
762 {
763 gboolean unsaved = (vwin->flags & VWIN_CONTENT_CHANGED);
764 tabwin_t *tabwin = vwin_get_tabwin(vwin);
765 GtkWidget *tab, *label;
766 const gchar *text, *p;
767 gchar *modtext = NULL;
768
769 tab = gtk_notebook_get_tab_label(GTK_NOTEBOOK(tabwin->tabs),
770 vwin->main);
771 label = g_object_get_data(G_OBJECT(tab), "label");
772
773 if (label != NULL) {
774 text = gtk_label_get_text(GTK_LABEL(label));
775 p = strstr(text, " *");
776 if (unsaved && p == NULL) {
777 modtext = g_strdup_printf("%s *", text);
778 } else if (!unsaved && p != NULL) {
779 modtext = g_strndup(text, strlen(text) - 2);
780 }
781 }
782
783 if (modtext != NULL) {
784 gtk_label_set_text(GTK_LABEL(label), modtext);
785 label = g_object_get_data(G_OBJECT(tab), "mlabel");
786 if (label != NULL) {
787 gtk_label_set_text(GTK_LABEL(label), modtext);
788 }
789 g_free(modtext);
790 }
791 }
792
show_tabbed_viewer(windata_t * vwin)793 void show_tabbed_viewer (windata_t *vwin)
794 {
795 tabwin_t *tabwin = vwin_get_tabwin(vwin);
796 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
797 int np = gtk_notebook_get_n_pages(notebook);
798
799 gtk_widget_show_all(vwin->main);
800
801 if (np > 1) {
802 int pgnum = gtk_notebook_page_num(notebook, vwin->main);
803
804 gtk_notebook_set_current_page(notebook, pgnum);
805 }
806
807 #if GTK_MAJOR_VERSION == 2 && GTK_MAJOR_VERSION < 18
808 if (!GTK_WIDGET_VISIBLE(tabwin->main)) {
809 gtk_widget_show_all(tabwin->main);
810 }
811 #else
812 if (!gtk_widget_get_visible(tabwin->main)) {
813 gtk_widget_show_all(tabwin->main);
814 }
815 #endif
816
817 if (vwin->role == EDIT_HANSL) {
818 gtk_drag_dest_set(vwin->text,
819 GTK_DEST_DEFAULT_ALL,
820 tabwin_drag_targets, 1,
821 GDK_ACTION_COPY);
822 g_signal_connect(G_OBJECT(vwin->text), "drag-data-received",
823 G_CALLBACK(tabwin_handle_drag),
824 tabwin);
825 }
826
827 gtk_window_present(GTK_WINDOW(tabwin->main));
828 }
829
830 /* move among the editor tabs via keyboard */
831
tabwin_navigate(windata_t * vwin,guint key)832 void tabwin_navigate (windata_t *vwin, guint key)
833 {
834 tabwin_t *tabwin = vwin_get_tabwin(vwin);
835 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
836
837 if (key == GDK_less || key == GDK_Page_Up) {
838 gtk_notebook_prev_page(notebook);
839 } else if (key == GDK_greater || key == GDK_Page_Down) {
840 gtk_notebook_next_page(notebook);
841 } else {
842 /* numeric value, 1 to 9 */
843 gtk_notebook_set_current_page(notebook, key - 1);
844 }
845 }
846
size_new_toplevel(windata_t * vwin)847 static void size_new_toplevel (windata_t *vwin)
848 {
849 int cw = get_char_width(vwin->text);
850 int hsize, vsize;
851
852 if (vwin->role == EDIT_HANSL || editing_alt_script(vwin->role)) {
853 hsize = SCRIPT_WIDTH;
854 vsize = SCRIPT_HEIGHT;
855 } else {
856 hsize = 63; /* MODEL_WIDTH ? */
857 vsize = MODEL_HEIGHT;
858 }
859
860 hsize *= cw;
861 hsize += 48;
862
863 gtk_window_set_default_size(GTK_WINDOW(vwin->main), hsize, vsize);
864 }
865
title_from_vwin(windata_t * vwin)866 static gchar *title_from_vwin (windata_t *vwin)
867 {
868 if (vwin->role == VIEW_MODEL) {
869 MODEL *pmod = vwin->data;
870
871 return g_strdup_printf(_("gretl: model %d"), pmod->ID);
872 } else {
873 return title_from_filename(vwin->fname, vwin->role, TRUE);
874 }
875 }
876
877 /* show or hide the New and Open toolbar items, which occupy
878 the first two slots on the toolbar
879 */
880
script_editor_show_new_open(windata_t * vwin,gboolean show)881 void script_editor_show_new_open (windata_t *vwin, gboolean show)
882 {
883 GtkToolItem *item0, *item1;
884
885 item0 = gtk_toolbar_get_nth_item(GTK_TOOLBAR(vwin->mbar), 0);
886 item1 = gtk_toolbar_get_nth_item(GTK_TOOLBAR(vwin->mbar), 1);
887
888 if (show) {
889 gtk_widget_show(GTK_WIDGET(item0));
890 gtk_widget_show(GTK_WIDGET(item1));
891 } else {
892 gtk_widget_hide(GTK_WIDGET(item0));
893 gtk_widget_hide(GTK_WIDGET(item1));
894 }
895 }
896
897 /* response to pulling a script or model out of the tabbed
898 context: we need to give the content its own window
899 */
900
undock_tabbed_viewer(GtkWidget * w,windata_t * vwin)901 static void undock_tabbed_viewer (GtkWidget *w, windata_t *vwin)
902 {
903 tabwin_t *tabwin = vwin_get_tabwin(vwin);
904 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
905 GtkWidget *page = vwin->main;
906 gulong handler_id;
907 gchar *title;
908
909 if (gtk_notebook_get_n_pages(notebook) < 2) {
910 /* we won't do this if there's only one page in the
911 viewer */
912 return;
913 }
914
915 /* disconnect stuff */
916 vwin->main = vwin->topmain = NULL;
917
918 /* remove signals and data from hbox (ex vwin->main) */
919 g_object_steal_data(G_OBJECT(page), "vwin");
920 g_signal_handlers_disconnect_by_func(page,
921 free_windata,
922 vwin);
923 handler_id = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(vwin->mbar),
924 "destroy-id"));
925 #if TDEBUG
926 fprintf(stderr, "*** undock_tabbed_viewer: w = %p, vwin = %p, page = %p\n",
927 (void *) w, (void *) vwin, page);
928 fprintf(stderr, " handler_id from mbar: %lu\n", handler_id);
929 #endif
930 if (handler_id > 0) {
931 g_signal_handler_disconnect(vwin->mbar, handler_id);
932 g_object_steal_data(G_OBJECT(vwin->mbar), "destroy-id");
933 }
934
935 /* extract vwin->vbox from its tabbed holder */
936 g_object_ref(vwin->vbox);
937 gtk_container_remove(GTK_CONTAINER(page), vwin->vbox);
938 #if GTK_MAJOR_VERSION >= 3
939 gtk_notebook_detach_tab(GTK_NOTEBOOK(notebook), page);
940 #else /* GTK2 */
941 gtk_container_remove(GTK_CONTAINER(notebook), page);
942 /* tweak vbox params */
943 gtk_box_set_spacing(GTK_BOX(vwin->vbox), 4);
944 gtk_container_set_border_width(GTK_CONTAINER(vwin->vbox), 4);
945 #endif
946
947 /* build new shell for @vwin */
948 vwin->main = gretl_gtk_window();
949 title = title_from_vwin(vwin);
950 gtk_window_set_title(GTK_WINDOW(vwin->main), title);
951 g_free(title);
952 handler_id = g_signal_connect(G_OBJECT(vwin->main), "destroy",
953 G_CALLBACK(free_windata), vwin);
954 g_object_set_data(G_OBJECT(vwin->main), "destroy-id",
955 GUINT_TO_POINTER(handler_id));
956 g_object_set_data(G_OBJECT(vwin->main), "vwin", vwin);
957 size_new_toplevel(vwin);
958
959 #if TDEBUG
960 fprintf(stderr, " undock: new main=%p, mbar=%p\n",
961 (void *) vwin->main, (void *) vwin->mbar);
962 fprintf(stderr, " new handler_id for main destruction: %lu\n",
963 handler_id);
964 #endif
965
966 /* add box for toolbar, pack it, drop extra ref., then
967 remove the "tabbed" flag (note that the tabbed flag
968 is wanted so that vwin_pack_toolbar will put the
969 toolbar up top)
970 */
971 vwin_pack_toolbar(vwin);
972 g_object_unref(vwin->mbar);
973 vwin->flags &= ~VWIN_TABBED;
974
975 if (vwin->role == EDIT_HANSL || editing_alt_script(vwin->role)) {
976 /* invalidate some menubar items */
977 script_editor_show_new_open(vwin, FALSE);
978 /* connect delete signal for single-script window */
979 g_signal_connect(G_OBJECT(vwin->main), "delete-event",
980 G_CALLBACK(query_save_text), vwin);
981 }
982
983 /* put vbox into new top-level window and drop extra ref. */
984 gtk_container_add(GTK_CONTAINER(vwin->main), vwin->vbox);
985 g_object_unref(vwin->vbox);
986
987 /* add to window list and attach window-key signals */
988 window_list_add(vwin->main, vwin->role);
989
990 /* add key-catcher for single-item window */
991 g_signal_connect(G_OBJECT(vwin->main), "key-press-event",
992 G_CALLBACK(catch_viewer_key), vwin);
993
994 #ifndef G_OS_WIN32
995 set_wm_icon(vwin->main);
996 #endif
997
998 gtk_widget_show(vwin->main);
999 gtk_widget_grab_focus(vwin->text);
1000
1001 #if TDEBUG
1002 fprintf(stderr, "undock_tabbed_viewer: done\n");
1003 #endif
1004 }
1005
dock_viewer(GtkWidget * w,windata_t * vwin)1006 static void dock_viewer (GtkWidget *w, windata_t *vwin)
1007 {
1008 tabwin_t *tabwin = NULL;
1009 GtkWidget *oldmain;
1010 GtkWidget *box;
1011 gulong handler_id;
1012 gchar *info = NULL;
1013
1014 if (vwin->role == VIEW_MODEL) {
1015 tabwin = tabmod;
1016 } else if (vwin->role == EDIT_HANSL) {
1017 tabwin = tabhansl;
1018 } else if (editing_alt_script(vwin->role)) {
1019 tabwin = tabalt;
1020 }
1021
1022 if (tabwin == NULL) {
1023 return;
1024 }
1025
1026 #if TDEBUG
1027 fprintf(stderr, "dock_viewer: starting on vwin at %p\n",
1028 (void *) vwin);
1029 #endif
1030
1031 oldmain = vwin->main;
1032 gtk_widget_hide(oldmain);
1033
1034 /* disconnect */
1035 vwin->main = NULL;
1036
1037 /* remove data, and also remove destruction-related signals,
1038 from stand-alone vwin->main */
1039 g_object_steal_data(G_OBJECT(oldmain), "vwin");
1040 g_signal_handlers_disconnect_by_func(oldmain,
1041 free_windata,
1042 vwin);
1043 g_signal_handlers_disconnect_by_func(oldmain,
1044 query_save_text,
1045 vwin);
1046
1047 /* grab info for title */
1048 if (vwin->role == EDIT_HANSL || editing_alt_script(vwin->role)) {
1049 info = g_strdup(vwin->fname);
1050 } else {
1051 const gchar *tmp = gtk_window_get_title(GTK_WINDOW(oldmain));
1052
1053 if (!strncmp(tmp, "gretl: ", 7)) {
1054 tmp += 7;
1055 }
1056 info = g_strdup(tmp);
1057 }
1058
1059 /* extract vwin->vbox from oldmain and trash oldmain */
1060 g_object_ref(vwin->vbox);
1061 gtk_container_remove(GTK_CONTAINER(oldmain), vwin->vbox);
1062 gtk_widget_destroy(oldmain);
1063
1064 #if TDEBUG
1065 fprintf(stderr, "dock_viewer: vwin->vbox at %p\n",
1066 (void *) vwin->vbox);
1067 #endif
1068
1069 if (vwin->role == EDIT_HANSL || editing_alt_script(vwin->role)) {
1070 script_editor_show_new_open(vwin, TRUE);
1071 }
1072
1073 /* extract vwin->mbar */
1074 g_object_ref(vwin->mbar);
1075 box = gtk_widget_get_parent(vwin->mbar);
1076 gtk_container_remove(GTK_CONTAINER(box), vwin->mbar);
1077 gtk_widget_destroy(box);
1078
1079 /* create new vwin->main, etc. */
1080 vwin->main = gtk_hbox_new(FALSE, 0);
1081 g_object_set_data(G_OBJECT(vwin->main), "vwin", vwin);
1082 handler_id = g_signal_connect(G_OBJECT(vwin->main), "destroy",
1083 G_CALLBACK(free_windata), vwin);
1084 g_object_set_data(G_OBJECT(vwin->main), "destroy-id",
1085 GUINT_TO_POINTER(handler_id));
1086
1087 make_viewer_tab(tabwin, vwin, info);
1088 vwin->topmain = tabwin->main;
1089 vwin->flags = VWIN_TABBED;
1090 g_free(info);
1091
1092 /* tweak vbox params and insert */
1093 gtk_box_set_spacing(GTK_BOX(vwin->vbox), 1);
1094 gtk_container_set_border_width(GTK_CONTAINER(vwin->vbox), 1);
1095 gtk_container_add(GTK_CONTAINER(vwin->main), vwin->vbox);
1096 g_object_unref(vwin->vbox);
1097
1098 /* repack toolbar in tabwin */
1099 tabwin_register_toolbar(vwin);
1100 g_object_unref(vwin->mbar);
1101
1102 show_tabbed_viewer(vwin);
1103 gtk_widget_grab_focus(vwin->text);
1104
1105 #if TDEBUG
1106 fprintf(stderr, "dock_viewer: done\n");
1107 #endif
1108 }
1109
window_is_undockable(windata_t * vwin)1110 gboolean window_is_undockable (windata_t *vwin)
1111 {
1112 if (vwin->topmain != NULL) {
1113 tabwin_t *tabwin = vwin_get_tabwin(vwin);
1114
1115 if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(tabwin->tabs)) > 1) {
1116 return TRUE;
1117 }
1118 }
1119
1120 return FALSE;
1121 }
1122
window_is_dockable(windata_t * vwin)1123 gboolean window_is_dockable (windata_t *vwin)
1124 {
1125 if (vwin->topmain == NULL) {
1126 if (vwin->role == EDIT_HANSL && tabhansl != NULL) {
1127 return TRUE;
1128 } else if (editing_alt_script(vwin->role) && tabalt != NULL) {
1129 return TRUE;
1130 } else if (vwin->role == VIEW_MODEL && tabmod != NULL) {
1131 return TRUE;
1132 }
1133 }
1134
1135 return FALSE;
1136 }
1137
add_undock_popup_item(GtkWidget * menu,windata_t * vwin)1138 void add_undock_popup_item (GtkWidget *menu, windata_t *vwin)
1139 {
1140 GtkWidget *item;
1141
1142 item = gtk_menu_item_new_with_label(_("Move to new window"));
1143 g_signal_connect(G_OBJECT(item), "activate",
1144 G_CALLBACK(undock_tabbed_viewer),
1145 vwin);
1146 gtk_widget_show(item);
1147 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1148 }
1149
add_dock_popup_item(GtkWidget * menu,windata_t * vwin)1150 void add_dock_popup_item (GtkWidget *menu, windata_t *vwin)
1151 {
1152 GtkWidget *item;
1153
1154 item = gtk_menu_item_new_with_label(_("Move to tabbed window"));
1155 g_signal_connect(G_OBJECT(item), "activate",
1156 G_CALLBACK(dock_viewer),
1157 vwin);
1158 gtk_widget_show(item);
1159 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
1160 }
1161
tabwin_get_editor_for_file(const char * filename,GtkWidget * w)1162 windata_t *tabwin_get_editor_for_file (const char *filename,
1163 GtkWidget *w)
1164 {
1165 tabwin_t *tabwin = NULL;
1166 windata_t *ret = NULL;
1167
1168 if (tabhansl != NULL && w == tabhansl->main) {
1169 tabwin = tabhansl;
1170 } else if (tabalt != NULL && w == tabalt->main) {
1171 tabwin = tabalt;
1172 }
1173
1174 if (tabwin != NULL) {
1175 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1176 int i, n = gtk_notebook_get_n_pages(notebook);
1177 GtkWidget *tab;
1178 windata_t *vwin;
1179
1180 for (i=0; i<n; i++) {
1181 tab = gtk_notebook_get_nth_page(notebook, i);
1182 vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1183 if (vwin != NULL && !strcmp(filename, vwin->fname)) {
1184 ret = vwin;
1185 break;
1186 }
1187 }
1188 }
1189
1190 return ret;
1191 }
1192
get_tab_for_data(const gpointer data,GtkWidget * w)1193 windata_t *get_tab_for_data (const gpointer data,
1194 GtkWidget *w)
1195 {
1196 windata_t *ret = NULL;
1197
1198 if (tabmod != NULL && w == tabmod->main) {
1199 GtkNotebook *notebook = GTK_NOTEBOOK(tabmod->tabs);
1200 int i, n = gtk_notebook_get_n_pages(notebook);
1201 GtkWidget *tab;
1202 windata_t *vwin;
1203
1204 for (i=0; i<n; i++) {
1205 tab = gtk_notebook_get_nth_page(notebook, i);
1206 vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1207 if (vwin != NULL && vwin->data == data) {
1208 ret = vwin;
1209 break;
1210 }
1211 }
1212 }
1213
1214 return ret;
1215 }
1216
tabwin_tab_present(windata_t * vwin)1217 void tabwin_tab_present (windata_t *vwin)
1218 {
1219 tabwin_t *tabwin = g_object_get_data(G_OBJECT(vwin->topmain),
1220 "tabwin");
1221 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1222 int i, n = gtk_notebook_get_n_pages(notebook);
1223 GtkWidget *tab;
1224
1225 for (i=0; i<n; i++) {
1226 tab = gtk_notebook_get_nth_page(notebook, i);
1227 if (vwin == g_object_get_data(G_OBJECT(tab), "vwin")) {
1228 gint pg = gtk_notebook_page_num(notebook, vwin->main);
1229
1230 gtk_notebook_set_current_page(notebook, pg);
1231 break;
1232 }
1233 }
1234
1235 gtk_window_present(GTK_WINDOW(vwin->topmain));
1236 }
1237
tabwin_close_models_viewer(GtkWidget * w)1238 void tabwin_close_models_viewer (GtkWidget *w)
1239 {
1240 if (tabmod != NULL && w == tabmod->main) {
1241 gtk_widget_destroy(w);
1242 }
1243 }
1244
tabwin_unregister_dialog(GtkWidget * w,tabwin_t * tabwin)1245 static void tabwin_unregister_dialog (GtkWidget *w, tabwin_t *tabwin)
1246 {
1247 if (tabwin != NULL && (tabwin == tabmod ||
1248 tabwin == tabhansl ||
1249 tabwin == tabalt)) {
1250 /* @tabwin will be an invalid pointer if it
1251 got a delete-event before execution gets
1252 here
1253 */
1254 GtkWidget *tab = tabwin->dlg_owner;
1255
1256 #if TDEBUG
1257 fprintf(stderr, "*** unregister_dialog: owner = %p\n", (void *) tab);
1258 #endif
1259
1260 if (tab != NULL) {
1261 windata_t *vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1262 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1263
1264 gtk_widget_set_sensitive(GTK_WIDGET(vwin->mbar), TRUE);
1265 if (gtk_notebook_get_n_pages(notebook) > 1) {
1266 gtk_notebook_set_tab_detachable(notebook, tab, TRUE);
1267 }
1268 tabwin->dlg_owner = NULL;
1269 }
1270 tabwin->dialog = NULL;
1271 }
1272 }
1273
1274 /* Called when a tabbed viewer spawns a dialog that becomes
1275 invalid if the currently active tab is destroyed. We make
1276 make the current tab undestroyable and undetachable for the
1277 duration.
1278 */
1279
tabwin_register_dialog(GtkWidget * w,gpointer p)1280 void tabwin_register_dialog (GtkWidget *w, gpointer p)
1281 {
1282 tabwin_t *tabwin = g_object_get_data(G_OBJECT(p), "tabwin");
1283 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1284 gint pg = gtk_notebook_get_current_page(notebook);
1285 GtkWidget *tab = gtk_notebook_get_nth_page(notebook, pg);
1286 windata_t *vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1287
1288 #if TDEBUG
1289 fprintf(stderr, "*** tabwin_register_dialog: w = %p, tab=%p\n",
1290 (void *) w, (void *) tab);
1291 #endif
1292
1293 gtk_widget_set_sensitive(vwin->mbar, FALSE);
1294 gtk_notebook_set_tab_detachable(notebook, tab, FALSE);
1295
1296 tabwin->dialog = w;
1297 tabwin->dlg_owner = tab;
1298
1299 g_signal_connect(G_OBJECT(w), "destroy",
1300 G_CALLBACK(tabwin_unregister_dialog),
1301 tabwin);
1302 }
1303
viewer_n_siblings(windata_t * vwin)1304 int viewer_n_siblings (windata_t *vwin)
1305 {
1306 tabwin_t *tabwin = vwin_get_tabwin(vwin);
1307 int n = 0;
1308
1309 if (tabwin != NULL) {
1310 n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(tabwin->tabs));
1311 if (n > 0) n--;
1312 }
1313
1314 return n;
1315 }
1316
highest_numbered_var_in_tabwin(tabwin_t * tabwin,const DATASET * dset)1317 int highest_numbered_var_in_tabwin (tabwin_t *tabwin,
1318 const DATASET *dset)
1319 {
1320 int vmax = 0;
1321
1322 if (tabwin->role == VIEW_MODEL) {
1323 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1324 int n = gtk_notebook_get_n_pages(notebook);
1325 const MODEL *pmod;
1326 windata_t *vwin;
1327 GtkWidget *tab;
1328 int i, m_vmax;
1329
1330 for (i=0; i<n; i++) {
1331 tab = gtk_notebook_get_nth_page(notebook, i);
1332 vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1333 pmod = vwin->data;
1334 m_vmax = highest_numbered_var_in_model(pmod, dset);
1335 if (m_vmax > vmax) {
1336 vmax = m_vmax;
1337 }
1338 }
1339 }
1340
1341 return vmax;
1342 }
1343
list_add_tabwin_models(tabwin_t * tabwin,GList ** plist)1344 void list_add_tabwin_models (tabwin_t *tabwin, GList **plist)
1345 {
1346 if (tabwin->role == VIEW_MODEL) {
1347 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1348 int n = gtk_notebook_get_n_pages(notebook);
1349 windata_t *vwin;
1350 GtkWidget *tab;
1351 int i;
1352
1353 for (i=0; i<n; i++) {
1354 tab = gtk_notebook_get_nth_page(notebook, i);
1355 vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1356 if (vwin != NULL && vwin->data != NULL) {
1357 *plist = g_list_append(*plist, vwin->data);
1358 }
1359 }
1360 }
1361 }
1362
window_get_active_vwin(GtkWidget * window)1363 windata_t *window_get_active_vwin (GtkWidget *window)
1364 {
1365 windata_t *vwin = g_object_get_data(G_OBJECT(window), "vwin");
1366
1367 if (vwin == NULL) {
1368 tabwin_t *tabwin = g_object_get_data(G_OBJECT(window), "tabwin");
1369
1370 if (tabwin != NULL) {
1371 GtkNotebook *notebook = GTK_NOTEBOOK(tabwin->tabs);
1372 gint pg = gtk_notebook_get_current_page(notebook);
1373 GtkWidget *tab = gtk_notebook_get_nth_page(notebook, pg);
1374
1375 vwin = g_object_get_data(G_OBJECT(tab), "vwin");
1376 }
1377 }
1378
1379 return vwin;
1380 }
1381