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 "var.h"
22 #include "dlgutils.h"
23 #include "guiprint.h"
24 #include "session.h"
25 #include "tabwin.h"
26 #include "fnsave.h"
27 #include "toolbar.h"
28 #include "cmdstack.h"
29 #include "winstack.h"
30 #include "gretl_ipc.h"
31 
32 #include "uservar.h"
33 
34 #define WDEBUG 0
35 
36 enum {
37     WINDOW_NEXT,
38     WINDOW_PREV
39 };
40 
41 static gint select_other_window (gpointer self, int seq);
42 
43 /* Below: apparatus for keeping track of open gretl windows.
44 
45    This provides the basis for a pop-up listing of windows as
46    a means of navigating the multi-window gretl GUI; it also
47    gives the basis for checking whether a window performing
48    a given role is already open, so as to avoid duplication,
49    and for closing any windows that are "invalidated" when
50    the gretl session is switched (e.g. by opening a new
51    data file).
52 */
53 
54 /* get the top-level widget associated with pre-defined
55    @action
56 */
57 
window_from_action(GtkAction * action)58 static GtkWidget *window_from_action (GtkAction *action)
59 {
60     GtkWidget *w;
61 
62     w = g_object_get_data(G_OBJECT(action), "target");
63 
64     if (w != NULL && !GTK_IS_WIDGET(w)) {
65 	/* shouldn't happen, but... */
66 	w = NULL;
67     }
68 
69     return w;
70 }
71 
vwin_from_action(GtkAction * action)72 static windata_t *vwin_from_action (GtkAction *action)
73 {
74     GtkWidget *w = window_from_action(action);
75 
76     if (w != NULL) {
77 	return g_object_get_data(G_OBJECT(w), "vwin");
78     }
79 
80     return NULL;
81 }
82 
83 /* callback to bring a selected window to the top */
84 
gretl_window_raise(GtkAction * action,gpointer data)85 static void gretl_window_raise (GtkAction *action, gpointer data)
86 {
87     GtkWidget *w = window_from_action(action);
88 
89     if (w != NULL && GTK_IS_WINDOW(w)) {
90 	gtk_window_present(GTK_WINDOW(w));
91     }
92 }
93 
94 /* select an icon to represent a window playing
95    the given @role in the gretl GUI */
96 
window_list_icon(int role)97 static const gchar *window_list_icon (int role)
98 {
99     const gchar *id = NULL;
100 
101     if (role == MAINWIN) {
102 	id = GRETL_STOCK_GRETL;
103     } else if (role == VIEW_MODEL || role == VAR ||
104 	       role == VECM || role == SYSTEM) {
105 	id = GRETL_STOCK_MODEL;
106     } else if (role == CONSOLE) {
107 	id = GRETL_STOCK_CONSOLE;
108     } else if (role >= EDIT_HEADER &&
109 	       role < EDIT_MAX) {
110 	id = GTK_STOCK_EDIT;
111     } else if (role == GNUPLOT) {
112 	id = GRETL_STOCK_SCATTER;
113     } else if (role == PKG_REGISTRY) {
114 	id = GTK_STOCK_PREFERENCES;
115     } else if (role == DBNOMICS_TOP ||
116 	       role == DBNOMICS_DB ||
117 	       role == DBNOMICS_SERIES ||
118 	       role == VIEW_DBSEARCH) {
119 	id = GRETL_STOCK_DBN;
120     } else if (BROWSER_ROLE(role)) {
121 	id = GTK_STOCK_INDEX;
122     } else if (help_role(role)) {
123 	id = GRETL_STOCK_BOOK;
124     } else if (role == STAT_TABLE) {
125 	id = GRETL_STOCK_CALC;
126     } else if (role == VIEW_SCRIPT ||
127 	       role == VIEW_PKG_SAMPLE) {
128 	id = GTK_STOCK_EXECUTE;
129     } else if (role == OPEN_SESSION) {
130 	id = GRETL_STOCK_ICONS;
131     } else if (role == PRINT ||
132 	       role == SCRIPT_OUT ||
133 	       role == VIEW_LOG) {
134 	id = GRETL_STOCK_PAGE;
135     } else if (role == SSHEET) {
136 	id = GRETL_STOCK_TABLE;
137     } else if (role == SAVE_FUNCTIONS) {
138 	id = GRETL_STOCK_TOOLS;
139     } else if (role == VIEW_DBNOMICS || role == VIEW_BUNDLE) {
140 	id = GRETL_STOCK_BUNDLE;
141     }
142 
143     return id;
144 }
145 
146 static int n_listed_windows;
147 static GtkActionGroup *window_group;
148 
get_window_title(GtkWidget * w)149 static const gchar *get_window_title (GtkWidget *w)
150 {
151     const gchar *s = NULL;
152 
153     if (GTK_IS_WINDOW(w)) {
154 	s = gtk_window_get_title(GTK_WINDOW(w));
155 
156 	if (s != NULL && !strncmp(s, "gretl", 5)) {
157 	    s += 5;
158 	    s += strspn(s, " ");
159 	    if (*s == ':') {
160 		s++;
161 		s += strspn(s, " ");
162 	    }
163 	}
164     }
165 
166     return s;
167 }
168 
window_label(GtkWidget * w,int role)169 static const char *window_label (GtkWidget *w, int role)
170 {
171     if (role == MAINWIN) {
172 #ifdef GRETL_PID_FILE
173 	static char label[32];
174 	int seqno = gretl_sequence_number();
175 	gchar *tmp;
176 
177 	if (seqno > 1) {
178 	    strcpy(label, _("Main window"));
179 	    tmp = g_strdup_printf(" (%d)", seqno);
180 	    strcat(label, tmp);
181 	    g_free(tmp);
182 	    return label;
183 	}
184 #endif
185 	return _("Main window");
186     } else {
187 	return get_window_title(w);
188     }
189 }
190 
plot_window_set_label(GtkWidget * w)191 void plot_window_set_label (GtkWidget *w)
192 {
193     gchar *aname = g_strdup_printf("%p", (void *) w);
194     GtkAction *action;
195 
196     action = gtk_action_group_get_action(window_group, aname);
197     if (action != NULL) {
198 	gtk_action_set_label(action, window_label(w, GNUPLOT));
199     }
200     g_free(aname);
201 }
202 
203 /* callback to be invoked just before destroying a window that's
204    on the list of open windows: remove its entry from the list
205 */
206 
window_list_remove(GtkWidget * w,GtkActionGroup * group)207 static void window_list_remove (GtkWidget *w, GtkActionGroup *group)
208 {
209     GtkAction *action;
210     gchar *aname = g_strdup_printf("%p", (void *) w);
211 
212 #if WDEBUG
213     fprintf(stderr, "window_list_remove: %s (%s)\n", aname,
214 	    window_label(w, 0));
215 #endif
216 
217     action = gtk_action_group_get_action(group, aname);
218     if (action != NULL) {
219 	gtk_action_group_remove_action(group, action);
220 	n_listed_windows--;
221     }
222     g_free(aname);
223 }
224 
225 /* callback for command-accent on Mac or Alt-PgUp/PgDn on
226    X11 and Windows: switch window-focus within gretl
227 */
228 
maybe_select_other_window(GdkEventKey * event,gpointer data)229 static gint maybe_select_other_window (GdkEventKey *event,
230 				       gpointer data)
231 {
232 #ifdef OS_OSX
233     if (cmd_key(event)) {
234 	if (event->keyval == GDK_asciitilde) {
235 	    return select_other_window(data, WINDOW_PREV);
236 	} else if (event->keyval == GDK_grave) {
237 	    return select_other_window(data, WINDOW_NEXT);
238 	}
239     }
240 #else
241     if (event->state & GDK_MOD1_MASK) {
242 	if (event->keyval == GDK_Page_Up ||
243 	    event->keyval == GDK_KP_Page_Up) {
244 	    return select_other_window(data, WINDOW_PREV);
245 	} else if (event->keyval == GDK_Page_Down ||
246 		   event->keyval == GDK_KP_Page_Down) {
247 	    return select_other_window(data, WINDOW_NEXT);
248 	}
249     }
250 #endif
251 
252     return FALSE;
253 }
254 
catch_winlist_key(GtkWidget * w,GdkEventKey * event,gpointer data)255 static gint catch_winlist_key (GtkWidget *w, GdkEventKey *event,
256 			       gpointer data)
257 {
258 #ifdef OS_OSX
259     if ((event->state & GDK_MOD1_MASK) && event->keyval == alt_w_key) {
260 	/* alt-w -> Sigma */
261 	window_list_popup(w, (GdkEvent *) event, data);
262 	return TRUE;
263     }
264 #else /* non-Mac */
265     if (event->state & GDK_MOD1_MASK) {
266 	if (event->keyval == GDK_w) {
267 	    window_list_popup(w, (GdkEvent *) event, data);
268 	    return TRUE;
269 	}
270     }
271 #endif
272 
273     return maybe_select_other_window(event, data);
274 }
275 
window_list_add(GtkWidget * w,int role)276 void window_list_add (GtkWidget *w, int role)
277 {
278     GtkActionEntry entry = {
279 	/* name, stock_id, label, accelerator, tooltip, callback */
280 	NULL, NULL, NULL, NULL, NULL, G_CALLBACK(gretl_window_raise)
281     };
282     GtkAction *action;
283     const char *label;
284     gchar *modlabel = NULL;
285     gchar *aname = NULL;
286 
287     label = window_label(w, role);
288     if (label == NULL) {
289 	return;
290     } else if (strchr(label, '_') != NULL) {
291 	modlabel = double_underscores_new(label);
292     }
293 
294 #if WDEBUG
295     fprintf(stderr, "window_list_add: %p (%s)\n", (void *) w, label);
296 #endif
297 
298     if (window_group == NULL) {
299 	/* create the window list action group */
300 	window_group = gtk_action_group_new("WindowList");
301     }
302 
303     /* set up an action entry for window @w */
304     entry.name = aname = g_strdup_printf("%p", (void *) w);
305     entry.stock_id = window_list_icon(role);
306     entry.label = (modlabel != NULL)? modlabel : label,
307 
308     /* add new action entry to group */
309     gtk_action_group_add_actions(window_group, &entry, 1, NULL);
310 
311     /* grab the added action and stick @w onto it as data */
312     action = gtk_action_group_get_action(window_group, aname);
313     g_object_set_data(G_OBJECT(action), "target", w);
314 
315     if (role != MAINWIN) {
316 	/* attach time to window */
317 	g_object_set_data(G_OBJECT(w), "time", GUINT_TO_POINTER(time(NULL)));
318 	/* attach callback to remove from window list */
319 	g_signal_connect(G_OBJECT(w), "destroy",
320 			 G_CALLBACK(window_list_remove),
321 			 window_group);
322     }
323 
324     if (role != EDIT_HANSL) {
325 	/* allow for Alt-w = omega */
326 	g_signal_connect(G_OBJECT(w), "key-press-event",
327 			 G_CALLBACK(catch_winlist_key), w);
328     }
329 
330     n_listed_windows++;
331 
332     g_free(aname);
333     g_free(modlabel);
334 }
335 
336 /* GCompareFunc: returns "a negative integer if the first value comes
337    before the second, 0 if they are equal, or a positive integer if
338    the first value comes after the second."
339 */
340 
sort_window_list(gconstpointer a,gconstpointer b)341 static gint sort_window_list (gconstpointer a, gconstpointer b)
342 {
343     GtkWidget *wa = window_from_action((GtkAction *) a);
344     GtkWidget *wb = window_from_action((GtkAction *) b);
345     guint ta, tb;
346 
347     /* sort main window first, otherwise by time when the
348        window was created */
349 
350     if (wa == mdata->main) return -1;
351     if (wb == mdata->main) return 1;
352 
353     /* bullet-proofing */
354     if (wa == NULL || wb == NULL) {
355 	return 0;
356     }
357 
358     ta = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(wa), "time"));
359     tb = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(wb), "time"));
360 
361     return ta - tb;
362 }
363 
364 /* use real UTF-8 bullet character if possible, otherwise asterisk */
365 
make_bullet(char * bullet)366 static void make_bullet (char *bullet)
367 {
368     GtkSettings *settings = gtk_settings_get_default();
369     gchar *fontname = NULL;
370 
371     g_object_get(G_OBJECT(settings), "gtk-font-name", &fontname, NULL);
372 
373     if (fontname != NULL) {
374 	PangoFontDescription *desc;
375 
376 	desc = pango_font_description_from_string(fontname);
377 	if (font_has_symbol(desc, 0x2022)) {
378 	    sprintf(bullet, " %c%c%c", 0xE2, 0x80, 0xA2);
379 	}
380 	if (desc != NULL) {
381 	    pango_font_description_free(desc);
382 	}
383     }
384 
385     if (*bullet == '\0') {
386 	strcpy(bullet, " *");
387     }
388 }
389 
390 /* show a bullet or asterisk next to the entry for
391    the current window */
392 
maybe_revise_action_label(GtkAction * action,GtkWidget * test)393 static void maybe_revise_action_label (GtkAction *action,
394 				       GtkWidget *test)
395 {
396     static char bullet[5];
397     static int blen;
398     const gchar *label = gtk_action_get_label(action);
399     gchar *repl = NULL;
400     int n = strlen(label);
401     int marked = 0;
402 
403     if (*bullet == '\0') {
404 	make_bullet(bullet);
405 	blen = strlen(bullet);
406     }
407 
408     if (n > blen && !strcmp(label + n - blen, bullet)) {
409 	marked = 1;
410     }
411 
412     if (test == window_from_action(action)) {
413 	if (!marked) {
414 	    /* add asterisk */
415 	    repl = g_strdup_printf("%s %s", label, bullet);
416 	}
417     } else if (marked) {
418 	/* remove asterisk */
419 	repl = g_strndup(label, strlen(label) - blen);
420     }
421 
422     if (repl != NULL) {
423 	gtk_action_set_label(action, repl);
424 	g_free(repl);
425     }
426 }
427 
window_list_revise_label(GtkWidget * targ,const char * label)428 void window_list_revise_label (GtkWidget *targ,
429 			       const char *label)
430 {
431     GList *wlist = gtk_action_group_list_actions(window_group);
432     GList *list = wlist;
433     GtkAction *action;
434 
435     while (list != NULL) {
436 	action = (GtkAction *) list->data;
437 	if (targ == window_from_action(action)) {
438 	    gtk_action_set_label(action, label);
439 	    break;
440 	}
441 	list = list->next;
442     }
443 
444     g_list_free(wlist);
445 }
446 
winlist_popup_done(GtkMenuShell * mshell,GtkWidget * window)447 static gboolean winlist_popup_done (GtkMenuShell *mshell,
448 				    GtkWidget *window)
449 {
450     windata_t *vwin = window_get_active_vwin(window);
451 
452     if (vwin != NULL) {
453 	/* don't leave focus on the winlist button */
454 	if (vwin->role == VIEW_MODEL ||
455 	    vwin->role == VAR ||
456 	    vwin->role == VECM) {
457 	    gtk_widget_grab_focus(vwin->text);
458 	} else if (vwin == mdata) {
459 	    gtk_widget_grab_focus(vwin->listbox);
460 	}
461     }
462 
463     return FALSE;
464 }
465 
add_cascade_item(GtkWidget * menu,GtkWidget * item)466 static void add_cascade_item (GtkWidget *menu,
467 			      GtkWidget *item)
468 {
469     GtkWidget *image;
470 
471     item = gtk_image_menu_item_new_with_label(_("Arrange"));
472     image = gtk_image_new_from_stock(GRETL_STOCK_WINLIST,
473 				     GTK_ICON_SIZE_MENU);
474     gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
475 				  image);
476     g_signal_connect(G_OBJECT(item), "activate",
477 		     G_CALLBACK(cascade_session_windows),
478 		     NULL);
479     gtk_widget_show(item);
480     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
481 }
482 
add_log_item(GtkWidget * menu,GtkWidget * item)483 static void add_log_item (GtkWidget *menu,
484 			  GtkWidget *item)
485 {
486     GtkWidget *image;
487 
488     item = gtk_image_menu_item_new_with_label(_("command log"));
489     image = gtk_image_new_from_stock(GRETL_STOCK_PAGE,
490 				     GTK_ICON_SIZE_MENU);
491     gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
492 				  image);
493     g_signal_connect(G_OBJECT(item), "activate",
494 		     G_CALLBACK(view_command_log),
495 		     NULL);
496     gtk_widget_show(item);
497     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
498 }
499 
add_iconview_item(GtkWidget * menu,GtkWidget * item)500 static void add_iconview_item (GtkWidget *menu,
501 			       GtkWidget *item)
502 {
503     GtkWidget *image;
504 
505     item = gtk_image_menu_item_new_with_label(_("icon view"));
506     image = gtk_image_new_from_stock(GRETL_STOCK_ICONS,
507 				     GTK_ICON_SIZE_MENU);
508     gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
509 				  image);
510     g_signal_connect(G_OBJECT(item), "activate",
511 		     G_CALLBACK(view_session),
512 		     NULL);
513     gtk_widget_show(item);
514     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
515 }
516 
517 /* pop up a list of open windows from which the user can
518    select one to raise and focus */
519 
window_list_popup(GtkWidget * src,GdkEvent * event,gpointer data)520 void window_list_popup (GtkWidget *src, GdkEvent *event,
521 			gpointer data)
522 {
523     static GtkWidget *menu;
524     GdkEventType evtype;
525     GList *wlist = gtk_action_group_list_actions(window_group);
526     GList *list;
527     GtkWidget *item, *lwin;
528     GtkWidget *thiswin = NULL;
529     GtkAction *action;
530     int log_up = 0;
531     int icons_up = 0;
532 
533     if (menu != NULL) {
534 	/* we need to make sure this is up to date */
535 	gtk_widget_destroy(menu);
536 	menu = NULL;
537     }
538 
539     if (n_listed_windows > 1) {
540 	wlist = g_list_sort(wlist, sort_window_list);
541     }
542 
543     if (data != NULL) {
544 	thiswin = GTK_WIDGET(data);
545     }
546 
547     menu = gtk_menu_new();
548     list = g_list_first(wlist);
549 
550     while (list != NULL) {
551 	action = (GtkAction *) list->data;
552 	lwin = window_from_action(action);
553 	if (is_command_log_viewer(lwin)) {
554 	    log_up = 1;
555 	} else if (widget_is_iconview(lwin)) {
556 	    icons_up = 1;
557 	}
558 	if (n_listed_windows > 1 && thiswin != NULL) {
559 	    maybe_revise_action_label(action, thiswin);
560 	}
561 	gtk_action_set_accel_path(action, NULL);
562 	item = gtk_action_create_menu_item(action);
563 	gtk_widget_show(item);
564 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
565 	list = list->next;
566     }
567     g_list_free(wlist);
568 
569     if (n_listed_windows > 1) {
570 	add_cascade_item(menu, item);
571     }
572 
573     if (!log_up || !icons_up) {
574 	item = gtk_separator_menu_item_new();
575 	gtk_widget_show(item);
576 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
577 	if (!log_up) {
578 	    add_log_item(menu, item);
579 	}
580 	if (!icons_up) {
581 	    add_iconview_item(menu, item);
582 	}
583     }
584 
585     if (thiswin != NULL) {
586 	g_signal_connect(G_OBJECT(menu), "deactivate",
587 			 G_CALLBACK(winlist_popup_done),
588 			 thiswin);
589     }
590 
591     evtype = event != NULL ? event->type : 0;
592 
593     if (evtype == GDK_BUTTON_PRESS) {
594 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
595 		       event->button.button, event->button.time);
596     } else if (evtype == GDK_KEY_PRESS) {
597 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
598 		       0, event->key.time);
599     } else {
600 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
601 		       0, gtk_get_current_event_time());
602     }
603 }
604 
vwin_winlist_popup(GtkWidget * src,GdkEvent * event,windata_t * vwin)605 static void vwin_winlist_popup (GtkWidget *src,
606 				GdkEvent *event,
607 				windata_t *vwin)
608 {
609     /* Note: this function may look redundant, given the
610        window_list_popup() function, but it's not. This is
611        due to the presence of tabbed windows whose content
612        (scripts, models) can be dragged out of their
613        tabbed context: in that case @vwin's associated
614        "toplevel" can change between invocations of
615        this function and therefore cannot be "hard-wired"
616        into the callback setup, rather it must be
617        evaluated on each invocation.
618     */
619     window_list_popup(src, event, vwin_toplevel(vwin));
620 }
621 
window_is_package_editor(GtkWidget * w)622 static int window_is_package_editor (GtkWidget *w)
623 {
624     if (w != NULL) {
625 	const gchar *wname = gtk_widget_get_name(w);
626 
627 	if (wname != NULL && strcmp(wname, "pkg-editor") == 0) {
628 	    return 1;
629 	}
630     }
631 
632     return 0;
633 }
634 
635 /* On exiting, check for any editing windows with unsaved
636    changes: if we find any, give the user a chance to
637    save the changes or cancel the exit.
638 */
639 
window_list_exit_check(void)640 gboolean window_list_exit_check (void)
641 {
642     gboolean ret = FALSE;
643 
644     if (n_listed_windows > 1) {
645 	GList *wlist = gtk_action_group_list_actions(window_group);
646 	GList *list = wlist;
647 	windata_t *vwin;
648 	GtkWidget *w;
649 
650 	while (list != NULL) {
651 	    w = window_from_action((GtkAction *) list->data);
652 	    if (w != NULL) {
653 		vwin = g_object_get_data(G_OBJECT(w), "vwin");
654 		if (vwin != NULL) {
655 		    if (vwin_is_editing(vwin) && vwin_content_changed(vwin)) {
656 			gtk_window_present(GTK_WINDOW(vwin->main));
657 			ret = query_save_text(NULL, NULL, vwin);
658 		    }
659 		} else {
660 		    /* vwin is NULL */
661 		    if (g_object_get_data(G_OBJECT(w), "tabwin")) {
662 			ret = tabwin_exit_check(w);
663 		    } else if (window_is_package_editor(w)) {
664 			ret = package_editor_exit_check(w);
665 		    }
666 		}
667 	    }
668 	    list = list->next;
669 	}
670 	g_list_free(wlist);
671     }
672 
673     return ret;
674 }
675 
676 /* windows that should _not_ be automatically closed when
677    closing the current gretl session (e.g. on opening a
678    new data file)
679 */
680 
681 #define other_dont_close(r) (r == SCRIPT_OUT ||		\
682 			     r == EDIT_PKG_CODE  ||	\
683 			     r == EDIT_PKG_SAMPLE ||	\
684 			     r == VIEW_LOG ||		\
685 			     r == VIEW_SCRIPT ||	\
686 			     r == VIEW_PKG_SAMPLE ||	\
687 	                     r == TEXTBOOK_DATA ||	\
688 			     r == PS_FILES ||		\
689 			     r == NATIVE_DB ||		\
690 			     r == REMOTE_DB ||		\
691 			     r == NATIVE_SERIES ||	\
692 			     r == REMOTE_SERIES ||	\
693 			     r == FUNC_FILES ||		\
694 			     r == REMOTE_FUNC_FILES ||	\
695 			     r == PKG_REGISTRY ||	\
696 			     r == CONSOLE)
697 
698 
keep_window_open(GtkWidget * w,gretlopt opt)699 static int keep_window_open (GtkWidget *w, gretlopt opt)
700 {
701     return window_is_package_editor(w);
702 
703     /* FIXME maybe keep plot windows open if opt & OPT_P? */
704 }
705 
706 /* called from session.c on switching the session: close all
707    windows that ought to be closed, but be careful not to
708    close ones that need to stay open!
709 */
710 
close_session_windows(gretlopt opt)711 void close_session_windows (gretlopt opt)
712 {
713     if (n_listed_windows > 1) {
714 	GList *wlist = gtk_action_group_list_actions(window_group);
715 	GList *list = wlist;
716 	windata_t *vwin;
717 	GtkWidget *w;
718 
719 	while (list) {
720 	    w = window_from_action((GtkAction *) list->data);
721 	    if (w != NULL) {
722 		vwin = g_object_get_data(G_OBJECT(w), "vwin");
723 		if (vwin == mdata) {
724 		    ; /* main window: no-op! */
725 		} else if (vwin != NULL && (vwin_editing_script(vwin->role) ||
726 					    help_role(vwin->role) ||
727 					    other_dont_close(vwin->role))) {
728 		    ; /* no-op */
729 		} else if (vwin == NULL && g_object_get_data(G_OBJECT(w), "tabwin")) {
730 		    /* tabbed script editor stays open, but tabbed model
731 		       viewer should be closed */
732 		    tabwin_close_models_viewer(w);
733 		} else if (w != NULL && !keep_window_open(w, opt)) {
734 		    gtk_widget_destroy(w);
735 		}
736 	    }
737 	    list = list->next;
738 	}
739 	g_list_free(wlist);
740     }
741 }
742 
cascade_session_windows(void)743 void cascade_session_windows (void)
744 {
745     if (n_listed_windows > 1) {
746 	GList *wlist = gtk_action_group_list_actions(window_group);
747 	GList *list;
748 	GtkWidget *w;
749 	gint x = 50, y = 50;
750 	gint d = 30;
751 
752 	wlist = g_list_sort(wlist, sort_window_list);
753 	list = g_list_first(wlist);
754 
755 	while (list != NULL) {
756 	    w = window_from_action((GtkAction *) list->data);
757 	    if (w != NULL) {
758 		gtk_window_move(GTK_WINDOW(w), x, y);
759 		gtk_window_present(GTK_WINDOW(w));
760 		x += d;
761 		y += d;
762 	    }
763 	    list = list->next;
764 	}
765 	g_list_free(wlist);
766     }
767 }
768 
select_other_window(gpointer self,int seq)769 static gint select_other_window (gpointer self, int seq)
770 {
771     if (n_listed_windows > 1) {
772 	GList *wlist = gtk_action_group_list_actions(window_group);
773 	GList *list;
774 	GtkWidget *w;
775 
776 	wlist = g_list_sort(wlist, sort_window_list);
777 	list = g_list_first(wlist);
778 
779 	/* find the window from which the keystroke emanated, @self,
780 	   and then select the next or previous window in the list,
781 	   wrapping around at the ends */
782 
783 	while (list != NULL) {
784 	    w = window_from_action((GtkAction *) list->data);
785 	    if (w == (GtkWidget *) self) {
786 		if (seq == WINDOW_PREV) {
787 		    list = list->prev != NULL ? list->prev :
788 			g_list_last(wlist);
789 		} else {
790 		    list = list->next != NULL ? list->next :
791 			g_list_first(wlist);
792 		}
793 		gretl_window_raise((GtkAction *) list->data, NULL);
794 		break;
795 	    }
796 	    list = list->next;
797 	}
798 	g_list_free(wlist);
799 	return TRUE;
800     }
801 
802     return FALSE;
803 }
804 
get_editor_for_file(const char * filename)805 windata_t *get_editor_for_file (const char *filename)
806 {
807     windata_t *ret = NULL;
808 
809     if (n_listed_windows > 1) {
810 	GList *wlist = gtk_action_group_list_actions(window_group);
811 	GList *list = wlist;
812 	windata_t *vwin;
813 	GtkWidget *w;
814 
815 	while (list != NULL && ret == NULL) {
816 	    w = window_from_action((GtkAction *) list->data);
817 	    if (w != NULL) {
818 		vwin = g_object_get_data(G_OBJECT(w), "vwin");
819 		if (vwin != NULL && vwin_is_editing(vwin)) {
820 		    if (!strcmp(filename, vwin->fname)) {
821 			ret = vwin;
822 		    }
823 		}
824 		if (vwin == NULL && g_object_get_data(G_OBJECT(w), "tabwin")) {
825 		    ret = tabwin_get_editor_for_file(filename, w);
826 		}
827 	    }
828 	    list = list->next;
829 	}
830 	g_list_free(wlist);
831     }
832 
833     return ret;
834 }
835 
get_viewer_for_plot(const char * filename)836 GtkWidget *get_viewer_for_plot (const char *filename)
837 {
838     GtkWidget *ret = NULL;
839 
840     if (n_listed_windows > 1) {
841 	GList *wlist = gtk_action_group_list_actions(window_group);
842 	GList *list = wlist;
843 	GtkWidget *w;
844 
845 	while (list != NULL && ret == NULL) {
846 	    w = window_from_action((GtkAction *) list->data);
847 	    if (is_shell_for_plotfile(w, filename)) {
848 		ret = w;
849 	    }
850 	    list = list->next;
851 	}
852 	g_list_free(wlist);
853     }
854 
855     return ret;
856 }
857 
db_role_matches(windata_t * vwin,int code)858 static int db_role_matches (windata_t *vwin, int code)
859 {
860     int ret = 0;
861 
862     if (code == NATIVE_SERIES) {
863 	ret = vwin->role == code;
864     } else {
865 	ret = (vwin->role == NATIVE_SERIES ||
866 	       vwin->role == RATS_SERIES ||
867 	       vwin->role == PCGIVE_SERIES ||
868 	       vwin->role == REMOTE_SERIES);
869     }
870 
871     if (ret) {
872 	ret = *vwin->fname != '\0';
873     }
874 
875     return ret;
876 }
877 
878 static windata_t *
real_get_browser_for_database(const char * filename,int code)879 real_get_browser_for_database (const char *filename, int code)
880 {
881     windata_t *ret = NULL;
882 
883     if (n_listed_windows > 1) {
884 	GList *wlist = gtk_action_group_list_actions(window_group);
885 	GList *list = wlist;
886 	windata_t *vwin;
887 
888 	while (list != NULL && ret == NULL) {
889 	    vwin = vwin_from_action((GtkAction *) list->data);
890 	    if (vwin != NULL && db_role_matches(vwin, code)) {
891 		if (!strncmp(filename, vwin->fname,
892 			     strlen(vwin->fname))) {
893 		    ret = vwin;
894 		}
895 	    }
896 	    list = list->next;
897 	}
898 	g_list_free(wlist);
899     }
900 
901     return ret;
902 }
903 
get_browser_for_database(const char * filename)904 windata_t *get_browser_for_database (const char *filename)
905 {
906     return real_get_browser_for_database(filename, 0);
907 }
908 
get_browser_for_gretl_database(const char * filename)909 windata_t *get_browser_for_gretl_database (const char *filename)
910 {
911     return real_get_browser_for_database(filename, NATIVE_SERIES);
912 }
913 
get_viewer_for_data(const gpointer data)914 windata_t *get_viewer_for_data (const gpointer data)
915 {
916     windata_t *ret = NULL;
917 
918     if (n_listed_windows > 1) {
919 	GList *wlist = gtk_action_group_list_actions(window_group);
920 	GList *list = wlist;
921 	windata_t *vwin;
922 	GtkWidget *w;
923 
924 	while (list != NULL && ret == NULL) {
925 	    w = window_from_action((GtkAction *) list->data);
926 	    if (w != NULL) {
927 		vwin = g_object_get_data(G_OBJECT(w), "vwin");
928 		if (vwin != NULL) {
929 		    if (vwin->data == data) {
930 			ret = vwin;
931 		    }
932 		} else if (g_object_get_data(G_OBJECT(w), "tabwin")) {
933 		    ret = get_tab_for_data(data, w);
934 		}
935 	    }
936 	    list = list->next;
937 	}
938 	g_list_free(wlist);
939     }
940 
941     return ret;
942 }
943 
paths_match(const char * path,windata_t * vwin)944 static int paths_match (const char *path, windata_t *vwin)
945 {
946     const char *wstr = NULL;
947 
948     if (vwin->role == DBNOMICS_DB) {
949 	wstr = g_object_get_data(G_OBJECT(vwin->listbox), "provider");
950     } else {
951 	wstr = g_object_get_data(G_OBJECT(vwin->listbox), "path");
952     }
953 
954     return wstr != NULL && !strcmp(path, wstr);
955 }
956 
get_browser_for_role(int role,const char * path)957 windata_t *get_browser_for_role (int role, const char *path)
958 {
959     windata_t *ret = NULL;
960 
961     if (n_listed_windows > 1) {
962 	GList *wlist = gtk_action_group_list_actions(window_group);
963 	GList *list = wlist;
964 	int checkpath = 0;
965 	windata_t *vwin;
966 
967 	if (path != NULL && (role == DBNOMICS_DB || role == DBNOMICS_SERIES)) {
968 	    checkpath = 1;
969 	}
970 	while (list != NULL && ret == NULL) {
971 	    vwin = vwin_from_action((GtkAction *) list->data);
972 	    if (vwin != NULL && vwin->role == role) {
973 		if (checkpath) {
974 		    /* "path" should match */
975 		    ret = paths_match(path, vwin) ? vwin : NULL;
976 		} else {
977 		    /* it's sufficient to match on role */
978 		    ret = vwin;
979 		}
980 	    }
981 	    list = list->next;
982 	}
983 	g_list_free(wlist);
984     }
985 
986     return ret;
987 }
988 
get_script_output_number(void)989 int get_script_output_number (void)
990 {
991     int ret = 0;
992 
993     if (n_listed_windows > 1) {
994 	GList *wlist = gtk_action_group_list_actions(window_group);
995 	GList *list = wlist;
996 	windata_t *vwin;
997 
998 	while (list != NULL) {
999 	    vwin = vwin_from_action((GtkAction *) list->data);
1000 	    if (vwin != NULL && vwin->role == SCRIPT_OUT) {
1001 		ret++;
1002 	    }
1003 	    list = list->next;
1004 	}
1005 	g_list_free(wlist);
1006     }
1007 
1008     return ret;
1009 }
1010 
get_unique_output_viewer(void)1011 windata_t *get_unique_output_viewer (void)
1012 {
1013     windata_t *ret = NULL;
1014     int vcount = 0;
1015 
1016     if (n_listed_windows > 1) {
1017 	GList *wlist = gtk_action_group_list_actions(window_group);
1018 	GList *list = wlist;
1019 	windata_t *vwin;
1020 
1021 	while (list != NULL) {
1022 	    vwin = vwin_from_action((GtkAction *) list->data);
1023 	    if (vwin != NULL && vwin->role == SCRIPT_OUT) {
1024 		vcount++;
1025 		if (vcount == 1) {
1026 		    ret = vwin;
1027 		} else {
1028 		    ret = NULL;
1029 		    break;
1030 		}
1031 	    }
1032 	    list = list->next;
1033 	}
1034 	g_list_free(wlist);
1035     }
1036 
1037     return ret;
1038 }
1039 
get_window_for_data(const gpointer data)1040 GtkWidget *get_window_for_data (const gpointer data)
1041 {
1042     GtkWidget *ret = NULL;
1043 
1044     /* this handles the case where the window in question
1045        is not part of a windata_t "viewer": e.g. a
1046        spreadsheet window editing a matrix
1047     */
1048 
1049     if (n_listed_windows > 1) {
1050 	GList *wlist = gtk_action_group_list_actions(window_group);
1051 	GList *list = wlist;
1052 	GtkWidget *w;
1053 	gpointer p;
1054 
1055 	while (list != NULL && ret == NULL) {
1056 	    w = window_from_action((GtkAction *) list->data);
1057 	    if (w != NULL) {
1058 		p = g_object_get_data(G_OBJECT(w), "object");
1059 		if (p == data) {
1060 		    ret = w;
1061 		}
1062 	    }
1063 	    list = list->next;
1064 	}
1065 	g_list_free(wlist);
1066     }
1067 
1068     return ret;
1069 }
1070 
maybe_close_window_for_user_var(const gpointer data,GretlObjType otype)1071 void maybe_close_window_for_user_var (const gpointer data,
1072 				      GretlObjType otype)
1073 {
1074     if (otype == GRETL_OBJ_BUNDLE) {
1075 	void *ptr = user_var_get_value((user_var *) data);
1076 	windata_t *vwin = get_viewer_for_data(ptr);
1077 
1078 	if (vwin != NULL) {
1079 	    vwin->data = NULL; /* don't double-free */
1080 	    gtk_widget_destroy(vwin->main);
1081 	}
1082     } else {
1083 	GtkWidget *w = get_window_for_data(data);
1084 
1085 	if (w != NULL) {
1086 	    if (otype == GRETL_OBJ_MATRIX) {
1087 		/* don't double-free */
1088 		g_object_set_data(G_OBJECT(w), "object", NULL);
1089 	    }
1090 	    gtk_widget_destroy(w);
1091 	}
1092     }
1093 }
1094 
get_window_for_plot(void * session_plot)1095 GtkWidget *get_window_for_plot (void *session_plot)
1096 {
1097     GtkWidget *ret = NULL;
1098 
1099     /* special for plot windows */
1100 
1101     if (n_listed_windows > 1) {
1102 	GList *wlist = gtk_action_group_list_actions(window_group);
1103 	GList *list = wlist;
1104 	GtkWidget *w;
1105 	void *test;
1106 
1107 	while (list != NULL && ret == NULL) {
1108 	    w = window_from_action((GtkAction *) list->data);
1109 	    if (w != NULL) {
1110 		test = g_object_get_data(G_OBJECT(w), "session-ptr");
1111 		if (test != NULL && test == session_plot) {
1112 		    ret = w;
1113 		}
1114 	    }
1115 	    list = list->next;
1116 	}
1117 	g_list_free(wlist);
1118     }
1119 
1120     return ret;
1121 }
1122 
package_being_edited(const char * pkgname,GtkWidget ** pw)1123 gboolean package_being_edited (const char *pkgname, GtkWidget **pw)
1124 {
1125     gboolean ret = FALSE;
1126 
1127     if (n_listed_windows > 1) {
1128 	GList *wlist = gtk_action_group_list_actions(window_group);
1129 	GList *list = wlist;
1130 	GtkWidget *w;
1131 
1132 	while (list != NULL && !ret) {
1133 	    w = window_from_action((GtkAction *) list->data);
1134 	    if (window_is_package_editor(w)) {
1135 		ret = query_package_editor(w, pkgname);
1136 		if (ret && pw != NULL) {
1137 		    *pw = w;
1138 		}
1139 	    }
1140 	    list = list->next;
1141 	}
1142 	g_list_free(wlist);
1143     }
1144 
1145     return ret;
1146 }
1147 
highest_numbered_variable_in_winstack(void)1148 int highest_numbered_variable_in_winstack (void)
1149 {
1150     int m_vmax, vmax = 0;
1151 
1152     if (n_listed_windows > 1) {
1153 	GList *wlist = gtk_action_group_list_actions(window_group);
1154 	GList *list = wlist;
1155 	tabwin_t *tabwin;
1156 	windata_t *vwin;
1157 	GtkWidget *w;
1158 
1159 	while (list != NULL) {
1160 	    vwin = NULL;
1161 	    w = window_from_action((GtkAction *) list->data);
1162 	    if (w != NULL) {
1163 		tabwin = g_object_get_data(G_OBJECT(w), "tabwin");
1164 		if (tabwin == NULL) {
1165 		    vwin = g_object_get_data(G_OBJECT(w), "vwin");
1166 		}
1167 		if (tabwin != NULL) {
1168 		    m_vmax = highest_numbered_var_in_tabwin(tabwin, dataset);
1169 		    if (m_vmax > vmax) {
1170 			vmax = m_vmax;
1171 		    }
1172 		} else if (vwin != NULL && vwin->role == VIEW_MODEL) {
1173 		    const MODEL *pmod = vwin->data;
1174 
1175 		    m_vmax = highest_numbered_var_in_model(pmod, dataset);
1176 		    if (m_vmax > vmax) {
1177 			vmax = m_vmax;
1178 		    }
1179 		} else if (vwin != NULL && (vwin->role == VAR || vwin->role == VECM)) {
1180 		    const GRETL_VAR *var = vwin->data;
1181 
1182 		    m_vmax = gretl_VAR_get_highest_variable(var);
1183 		    if (m_vmax > vmax) {
1184 			vmax = m_vmax;
1185 		    }
1186 		}
1187 	    }
1188 	    list = list->next;
1189 	}
1190 	g_list_free(wlist);
1191     }
1192 
1193     return vmax;
1194 }
1195 
1196 /* compose a GList holding pointers to all models in
1197    individual or tabbed viewer windows */
1198 
windowed_model_list(void)1199 GList *windowed_model_list (void)
1200 {
1201     GList *ret = NULL;
1202 
1203     if (n_listed_windows > 1) {
1204 	GList *wlist = gtk_action_group_list_actions(window_group);
1205 	GList *list = wlist;
1206 	tabwin_t *tabwin;
1207 	windata_t *vwin;
1208 	GtkWidget *w;
1209 
1210 	while (list != NULL) {
1211 	    vwin = NULL;
1212 	    w = window_from_action((GtkAction *) list->data);
1213 	    if (w != NULL) {
1214 		tabwin = g_object_get_data(G_OBJECT(w), "tabwin");
1215 		if (tabwin == NULL) {
1216 		    vwin = g_object_get_data(G_OBJECT(w), "vwin");
1217 		}
1218 		if (tabwin != NULL) {
1219 		    list_add_tabwin_models(tabwin, &ret);
1220 		} else if (vwin != NULL && vwin->role == VIEW_MODEL) {
1221 		    ret = g_list_append(ret, vwin->data);
1222 		}
1223 	    }
1224 	    list = list->next;
1225 	}
1226 	g_list_free(wlist);
1227     }
1228 
1229     return ret;
1230 }
1231 
1232 /* end of window-list apparatus */
1233 
1234 static windata_flags vwin_presets;
1235 
1236 /* This is used in a couple of special cases to apply a flag
1237    before the vwin GUI gets built. It's a bit of a hack, but
1238    avoids the alternatives of either (a) proliferating vwin
1239    "roles" or (b) adding another argument to all vwin-creating
1240    functions.
1241 */
1242 
preset_viewer_flag(windata_flags f)1243 void preset_viewer_flag (windata_flags f)
1244 {
1245     vwin_presets = f;
1246 }
1247 
vwin_new(int role,gpointer data)1248 windata_t *vwin_new (int role, gpointer data)
1249 {
1250     windata_t *vwin = mymalloc(sizeof *vwin);
1251 
1252     if (vwin != NULL) {
1253 	memset(vwin, 0, sizeof *vwin);
1254 	vwin->role = role;
1255 	vwin->data = data;
1256 	vwin->flags = vwin_presets;
1257     }
1258 
1259     vwin_presets = 0;
1260 
1261     return vwin;
1262 }
1263 
should_swallow_vwin(int role)1264 static int should_swallow_vwin (int role)
1265 {
1266     if (swallow) {
1267 	/* can add others here, after a lot of work! */
1268 	return role == CONSOLE;
1269     } else {
1270 	return 0;
1271     }
1272 }
1273 
1274 /* special setup for the case where the gretl main window
1275    will/may contain additional panes besides the dataset
1276 */
1277 
1278 #define TWO_ROWS 0 /* not yet! */
1279 
1280 #if TWO_ROWS
1281 
mainwin_swallow_setup(windata_t * vwin)1282 static void mainwin_swallow_setup (windata_t *vwin)
1283 {
1284     GtkWidget *BigV = gtk_vbox_new(FALSE, 0);
1285     GtkWidget *vp = gtk_vpaned_new();
1286     GtkWidget *topbox = gtk_hbox_new(FALSE, 5);
1287 
1288     g_object_set_data(G_OBJECT(vwin->main), "topbox", topbox);
1289     vwin->hpanes1 = gtk_hpaned_new();
1290     vwin->hpanes2 = gtk_hpaned_new();
1291 
1292     gtk_box_pack_start(GTK_BOX(BigV), topbox, FALSE, FALSE, 0);
1293     gtk_box_pack_start(GTK_BOX(BigV), vp, TRUE, TRUE, 0);
1294     gtk_paned_add1(GTK_PANED(vp), vwin->hpanes1);
1295     gtk_paned_add2(GTK_PANED(vp), vwin->hpanes2);
1296     gtk_paned_set_position(GTK_PANED(vp), mainwin_height);
1297     gtk_container_add(GTK_CONTAINER(vwin->main), BigV);
1298     gtk_paned_add1(GTK_PANED(vwin->hpanes1), vwin->vbox);
1299 #if GTK_MAJOR_VERSION == 3
1300     gtk_paned_set_wide_handle(GTK_PANED(vwin->hpanes1), TRUE);
1301     gtk_paned_set_wide_handle(GTK_PANED(vwin->hpanes2), TRUE);
1302 #endif
1303 }
1304 
1305 #else /* single row, just two panes total */
1306 
mainwin_swallow_setup(windata_t * vwin)1307 static void mainwin_swallow_setup (windata_t *vwin)
1308 {
1309     GtkWidget *BigV = gtk_vbox_new(FALSE, 0);
1310     GtkWidget *topbox = gtk_hbox_new(FALSE, 5);
1311 
1312     g_object_set_data(G_OBJECT(vwin->main), "topbox", topbox);
1313     vwin->hpanes1 = gtk_hpaned_new();
1314 
1315     /* BigV contains a top slot to hold the "global" menubar,
1316        and under this a paned horizontal box to hold the
1317        two major components. At this stage we add the original
1318        main vbox to the left-hand pane; the console will be
1319        added later.
1320     */
1321     gtk_box_pack_start(GTK_BOX(BigV), topbox, FALSE, FALSE, 0);
1322     gtk_box_pack_start(GTK_BOX(BigV), vwin->hpanes1, TRUE, TRUE, 0);
1323     gtk_container_add(GTK_CONTAINER(vwin->main), BigV);
1324     gtk_paned_add1(GTK_PANED(vwin->hpanes1), vwin->vbox);
1325 #if GTK_MAJOR_VERSION == 3
1326     gtk_paned_set_wide_handle(GTK_PANED(vwin->hpanes1), TRUE);
1327 #endif
1328 }
1329 
1330 #endif
1331 
1332 windata_t *
gretl_viewer_new_with_parent(windata_t * parent,int role,const gchar * title,gpointer data)1333 gretl_viewer_new_with_parent (windata_t *parent, int role,
1334 			      const gchar *title,
1335 			      gpointer data)
1336 {
1337     windata_t *vwin = vwin_new(role, data);
1338     int toplevel = 1;
1339 
1340     if (vwin == NULL) {
1341 	return NULL;
1342     }
1343 
1344     if (should_swallow_vwin(role) || (vwin->flags & VWIN_SWALLOW)) {
1345 	toplevel = 0;
1346     }
1347 
1348     if (toplevel) {
1349 	vwin->main = gretl_gtk_window();
1350 	if (title != NULL) {
1351 	    gtk_window_set_title(GTK_WINDOW(vwin->main), title);
1352 	}
1353 	g_object_set_data(G_OBJECT(vwin->main), "vwin", vwin);
1354     }
1355 
1356     vwin->vbox = gtk_vbox_new(FALSE, 4);
1357     gtk_container_set_border_width(GTK_CONTAINER(vwin->vbox), 4);
1358 
1359     if (swallow && role == MAINWIN) {
1360 	mainwin_swallow_setup(vwin);
1361     } else if (toplevel) {
1362 	gtk_container_add(GTK_CONTAINER(vwin->main), vwin->vbox);
1363     } else {
1364 	g_object_set_data(G_OBJECT(vwin->vbox), "vwin", vwin);
1365 	vwin->main = vwin->vbox;
1366 	return vwin; /* we're done here */
1367     }
1368 
1369     if (parent != NULL) {
1370 	vwin_add_child(parent, vwin);
1371     }
1372 
1373 #if 0
1374     fprintf(stderr, "viewer_new: vwin %p, gtk window %p, role %d\n", (void *) vwin,
1375 	    (void *) vwin->main, role);
1376 #endif
1377 
1378     if (role == MAINWIN) {
1379 	gtk_window_set_position(GTK_WINDOW(vwin->main),
1380 				GTK_WIN_POS_CENTER);
1381     } else {
1382 	g_signal_connect(G_OBJECT(vwin->main), "destroy",
1383 			 G_CALLBACK(free_windata), vwin);
1384 	gtk_window_set_position(GTK_WINDOW(vwin->main),
1385 				GTK_WIN_POS_MOUSE);
1386     }
1387 
1388     if (title != NULL) {
1389 	window_list_add(vwin->main, role);
1390 #ifndef G_OS_WIN32
1391 	set_wm_icon(vwin->main);
1392 #endif
1393     }
1394 
1395     return vwin;
1396 }
1397 
gretl_viewer_new(int role,const gchar * title,gpointer data)1398 windata_t *gretl_viewer_new (int role, const gchar *title,
1399 			     gpointer data)
1400 {
1401     return gretl_viewer_new_with_parent(NULL, role, title,
1402 					data);
1403 }
1404 
vwin_toplevel(windata_t * vwin)1405 GtkWidget *vwin_toplevel (windata_t *vwin)
1406 {
1407     if (vwin == NULL) {
1408 	return NULL;
1409     } else if (vwin->flags & VWIN_SWALLOW) {
1410 	/* vwin swallowed by main */
1411 	return mdata->main;
1412     } else if (vwin->topmain != NULL) {
1413 	/* the tabbed case */
1414 	return vwin->topmain;
1415     } else {
1416 	return gtk_widget_get_toplevel(vwin->main);
1417     }
1418 }
1419 
real_add_winlist(windata_t * vwin,GtkWidget * window,GtkWidget * hbox)1420 static GtkWidget *real_add_winlist (windata_t *vwin,
1421 				    GtkWidget *window,
1422 				    GtkWidget *hbox)
1423 {
1424     GtkWidget *button, *img, *tbar;
1425     GtkWidget *sibling = NULL;
1426     GtkToolItem *item;
1427 
1428     button = gtk_button_new();
1429     item = gtk_tool_item_new();
1430 
1431     if (vwin != NULL && vwin->mbar != NULL &&
1432 	GTK_IS_MENU_BAR(vwin->mbar)) {
1433 	sibling = vwin->mbar;
1434     }
1435 
1436     tbar = gretl_toolbar_new(sibling);
1437 
1438     gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Windows"));
1439     gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1440     img = gtk_image_new_from_stock(GRETL_STOCK_WINLIST, toolbar_icon_size);
1441     gtk_container_add(GTK_CONTAINER(button), img);
1442     gtk_container_add(GTK_CONTAINER(item), button);
1443 
1444     if (vwin != NULL) {
1445 	g_signal_connect(G_OBJECT(button), "button-press-event",
1446 			 G_CALLBACK(vwin_winlist_popup), vwin);
1447     } else {
1448 	g_signal_connect(G_OBJECT(button), "button-press-event",
1449 			 G_CALLBACK(window_list_popup), window);
1450     }
1451 
1452     gtk_toolbar_insert(GTK_TOOLBAR(tbar), item, -1);
1453     gtk_widget_show_all(tbar);
1454     gtk_box_pack_end(GTK_BOX(hbox), tbar, FALSE, FALSE, 0);
1455 
1456     return tbar;
1457 }
1458 
vwin_add_winlist(windata_t * vwin)1459 void vwin_add_winlist (windata_t *vwin)
1460 {
1461     GtkWidget *hbox = gtk_widget_get_parent(vwin->mbar);
1462 
1463     if (g_object_get_data(G_OBJECT(hbox), "winlist") == NULL) {
1464 	GtkWidget *winlist;
1465 
1466 	winlist = real_add_winlist(vwin, NULL, hbox);
1467 	g_object_set_data(G_OBJECT(hbox), "winlist", winlist);
1468     }
1469 }
1470 
window_add_winlist(GtkWidget * window,GtkWidget * hbox)1471 void window_add_winlist (GtkWidget *window, GtkWidget *hbox)
1472 {
1473     if (g_object_get_data(G_OBJECT(hbox), "winlist") == NULL) {
1474 	GtkWidget *winlist;
1475 
1476 	winlist = real_add_winlist(NULL, window, hbox);
1477 	g_object_set_data(G_OBJECT(hbox), "winlist", winlist);
1478     }
1479 }
1480 
1481 #if 0 /* specific to "swallow" and unused at present */
1482 
1483 static void menubar_add_closer (windata_t *vwin)
1484 {
1485     GtkWidget *hbox = gtk_widget_get_parent(vwin->mbar);
1486     GtkWidget *button, *img, *tbar;
1487     GtkWidget *sibling = NULL;
1488     GtkToolItem *item;
1489 
1490     button = gtk_button_new();
1491     item = gtk_tool_item_new();
1492 
1493     if (vwin != NULL && vwin->mbar != NULL &&
1494 	GTK_IS_MENU_BAR(vwin->mbar)) {
1495 	sibling = vwin->mbar;
1496     }
1497 
1498     tbar = gretl_toolbar_new(sibling);
1499     gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1500     img = gtk_image_new_from_stock(GRETL_STOCK_CLOSE, GTK_ICON_SIZE_MENU);
1501     gtk_container_add(GTK_CONTAINER(button), img);
1502     gtk_container_add(GTK_CONTAINER(item), button);
1503 
1504     g_signal_connect_swapped(G_OBJECT(button), "button-press-event",
1505 			     G_CALLBACK(gtk_widget_destroy), vwin->main);
1506 
1507     gtk_toolbar_insert(GTK_TOOLBAR(tbar), item, -1);
1508     gtk_widget_show_all(tbar);
1509     gtk_box_pack_end(GTK_BOX(hbox), tbar, FALSE, FALSE, 0);
1510 }
1511 
1512 #endif
1513 
destroy_hbox_child(GtkWidget * w,gpointer p)1514 static void destroy_hbox_child (GtkWidget *w, gpointer p)
1515 {
1516     if (GTK_IS_SPINNER(w)) {
1517 	gtk_spinner_stop(GTK_SPINNER(w));
1518     }
1519     gtk_widget_destroy(w);
1520 }
1521 
want_winlist(windata_t * vwin)1522 static int want_winlist (windata_t *vwin)
1523 {
1524     if (vwin->flags & VWIN_SWALLOW) {
1525 	return 0;
1526     } else {
1527 	GtkWidget *hbox = gtk_widget_get_parent(vwin->mbar);
1528 
1529 	return g_object_get_data(G_OBJECT(hbox), "winlist") == NULL;
1530     }
1531 }
1532 
vwin_pack_toolbar(windata_t * vwin)1533 void vwin_pack_toolbar (windata_t *vwin)
1534 {
1535     if (vwin->topmain != NULL) {
1536 	/* @vwin is embedded in a tabbed window */
1537 	tabwin_register_toolbar(vwin);
1538 	if (want_winlist(vwin)) {
1539 	    vwin_add_winlist(vwin);
1540 	}
1541     } else {
1542 	GtkWidget *hbox;
1543 
1544 	/* check for presence of a temporary "top-hbox" -- as
1545 	   in a script output window that's waiting for full
1546 	   output
1547 	*/
1548 	hbox = g_object_get_data(G_OBJECT(vwin->main), "top-hbox");
1549 
1550 	if (hbox != NULL) {
1551 	    gtk_container_foreach(GTK_CONTAINER(hbox), destroy_hbox_child, NULL);
1552 	    gtk_box_pack_start(GTK_BOX(hbox), vwin->mbar, FALSE, FALSE, 0);
1553 	} else {
1554 	    hbox = gtk_hbox_new(FALSE, 0);
1555 	    gtk_box_set_spacing(GTK_BOX(vwin->vbox), 0);
1556 	    gtk_box_pack_start(GTK_BOX(vwin->vbox), hbox, FALSE, FALSE, 0);
1557 
1558 	    if (vwin->role == VIEW_MODEL || vwin->role == VAR ||
1559 		vwin->role == VECM) {
1560 		/* model viewer: the menubar extends full-length */
1561 		gtk_box_pack_start(GTK_BOX(hbox), vwin->mbar, TRUE, TRUE, 0);
1562 	    } else {
1563 		gtk_box_pack_start(GTK_BOX(hbox), vwin->mbar, FALSE, FALSE, 0);
1564 		if (vwin->role == SCRIPT_OUT) {
1565 		    /* added 2015-11-16 */
1566 		    g_object_set_data(G_OBJECT(vwin->main), "top-hbox", hbox);
1567 		}
1568 	    }
1569 	    if (window_is_tab(vwin)) {
1570 		/* here we're re-packing vwin->mbar: move it up top */
1571 		gtk_box_reorder_child(GTK_BOX(vwin->vbox), hbox, 0);
1572 	    }
1573 	}
1574 	if (want_winlist(vwin)) {
1575 	    vwin_add_winlist(vwin);
1576 	}
1577 	if (use_toolbar_search_box(vwin->role)) {
1578 	    vwin_add_finder(vwin);
1579 	}
1580 	if (vwin->flags & VWIN_SWALLOW) {
1581 #if 0 /* don't show a close for swallowed console */
1582 	    menubar_add_closer(vwin);
1583 #endif
1584 	    if (vwin->role == CONSOLE) {
1585 		GtkWidget *lbl = gtk_label_new(_("gretl console"));
1586 
1587 		gtk_box_pack_start(GTK_BOX(hbox), lbl, FALSE, FALSE, 5);
1588 		gtk_box_reorder_child(GTK_BOX(hbox), lbl, 0);
1589 	    }
1590 	}
1591 	gtk_widget_show_all(hbox);
1592     }
1593 }
1594 
vwin_reinstate_toolbar(windata_t * vwin)1595 void vwin_reinstate_toolbar (windata_t *vwin)
1596 {
1597     GtkWidget *hbox;
1598 
1599     hbox = g_object_get_data(G_OBJECT(vwin->main), "top-hbox");
1600 
1601     if (hbox != NULL) {
1602 	/* destroy the temporary stuff, put the "real" stuff back,
1603 	   and drop the extra references
1604 	*/
1605 	GtkWidget *winlist;
1606 
1607 	gtk_container_foreach(GTK_CONTAINER(hbox), destroy_hbox_child, NULL);
1608 	gtk_box_pack_start(GTK_BOX(hbox), vwin->mbar, FALSE, FALSE, 0);
1609 	g_object_unref(G_OBJECT(vwin->mbar));
1610 	winlist = g_object_get_data(G_OBJECT(hbox), "winlist");
1611 	if (winlist != NULL) {
1612 	    gtk_box_pack_end(GTK_BOX(hbox), winlist, FALSE, FALSE, 0);
1613 	    g_object_unref(G_OBJECT(winlist));
1614 	}
1615 	if (vwin->finder != NULL) {
1616 	    gtk_box_pack_end(GTK_BOX(hbox), vwin->finder, FALSE, FALSE, 5);
1617 	    g_object_unref(G_OBJECT(vwin->finder));
1618 	}
1619 	gtk_widget_show_all(hbox);
1620     }
1621 }
1622 
gretl_browser_new(int role,const gchar * title)1623 windata_t *gretl_browser_new (int role, const gchar *title)
1624 {
1625     windata_t *vwin = vwin_new(role, NULL);
1626 
1627     if (vwin == NULL) {
1628 	return NULL;
1629     }
1630 
1631     vwin->main = gretl_gtk_window();
1632     gtk_window_set_title(GTK_WINDOW(vwin->main), title);
1633     g_object_set_data(G_OBJECT(vwin->main), "vwin", vwin);
1634 
1635     g_signal_connect(G_OBJECT(vwin->main), "destroy",
1636 		     G_CALLBACK(free_windata), vwin);
1637     gtk_window_set_position(GTK_WINDOW(vwin->main),
1638 			    GTK_WIN_POS_MOUSE);
1639 
1640 #if 0
1641     fprintf(stderr, "browser_new: vwin %p, gtk window %p, role %d\n",
1642 	    (void *) vwin, (void *) vwin->main, role);
1643 #endif
1644 
1645     window_list_add(vwin->main, role);
1646 #ifndef G_OS_WIN32
1647     set_wm_icon(vwin->main);
1648 #endif
1649 
1650     return vwin;
1651 }
1652 
gretl_viewer_present(windata_t * vwin)1653 void gretl_viewer_present (windata_t *vwin)
1654 {
1655     if (window_is_tab(vwin)) {
1656 	tabwin_tab_present(vwin);
1657     } else {
1658 	gtk_window_present(GTK_WINDOW(vwin->main));
1659     }
1660 }
1661 
gretl_viewer_destroy(windata_t * vwin)1662 void gretl_viewer_destroy (windata_t *vwin)
1663 {
1664     if (window_is_tab(vwin)) {
1665 	tabwin_tab_destroy(vwin);
1666     } else {
1667 	gtk_widget_destroy(vwin->main);
1668     }
1669 }
1670 
gretl_viewer_set_title(windata_t * vwin,const char * title)1671 void gretl_viewer_set_title (windata_t *vwin, const char *title)
1672 {
1673     if (window_is_tab(vwin)) {
1674 	if (!strncmp(title, "gretl: ", 7)) {
1675 	    title += 7;
1676 	}
1677 	tabwin_tab_set_title(vwin, title);
1678     } else {
1679 	gtk_window_set_title(GTK_WINDOW(vwin->main), title);
1680     }
1681 }
1682 
1683 /* When we add popup menus as callbacks for buttons on @vwin's
1684    toolbar, we want to record pointers to them so we're
1685    able to destroy them when @vwin is closed, otherwise
1686    we'd be leaking memory.
1687 */
1688 
vwin_record_toolbar_popup(windata_t * vwin,GtkWidget * menu)1689 void vwin_record_toolbar_popup (windata_t *vwin, GtkWidget *menu)
1690 {
1691     GList *plist;
1692 
1693     plist = g_object_get_data(G_OBJECT(vwin->mbar), "toolbar-popups");
1694     plist = g_list_append(plist, menu);
1695     g_object_set_data(G_OBJECT(vwin->mbar), "toolbar-popups", plist);
1696 }
1697 
trash_toolbar_popup(gpointer data,gpointer p)1698 static void trash_toolbar_popup (gpointer data, gpointer p)
1699 {
1700     gtk_widget_destroy(GTK_WIDGET(data));
1701 }
1702 
vwin_free_toolbar_popups(windata_t * vwin)1703 void vwin_free_toolbar_popups (windata_t *vwin)
1704 {
1705     if (vwin->mbar != NULL) {
1706 	GList *plist;
1707 
1708 	plist = g_object_get_data(G_OBJECT(vwin->mbar), "toolbar-popups");
1709 	if (plist != NULL) {
1710 	    g_list_foreach(plist, trash_toolbar_popup, NULL);
1711 	}
1712 	g_list_free(plist);
1713     }
1714 }
1715 
vwin_record_action(windata_t * vwin,GtkAction * action)1716 void vwin_record_action (windata_t *vwin, GtkAction *action)
1717 {
1718     const gchar *name = gtk_action_get_name(action);
1719 
1720     g_object_set_data(G_OBJECT(vwin->main), name, action);
1721 }
1722 
vwin_action_set_sensitive(windata_t * vwin,const char * name,gboolean s)1723 void vwin_action_set_sensitive (windata_t *vwin, const char *name,
1724 				gboolean s)
1725 {
1726     GtkAction *action = g_object_get_data(G_OBJECT(vwin->main), name);
1727 
1728     if (action != NULL) {
1729 	gtk_action_set_sensitive(action, s);
1730     }
1731 }
1732