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