1 /*
2     roxterm - VTE/GTK terminal emulator with tabs
3     Copyright (C) 2004-2015 Tony Houghton <h@realh.co.uk>
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 2 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, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 #ifndef DEFNS_H
21 #include "defns.h"
22 #endif
23 
24 #include <ctype.h>
25 #include <errno.h>
26 
27 #include "dlg.h"
28 #include "globalopts.h"
29 #include "menutree.h"
30 #include "multitab.h"
31 #include "multitab-close-button.h"
32 #include "multitab-label.h"
33 #include "session-file.h"
34 #include "shortcuts.h"
35 
36 #define HORIZ_TAB_WIDTH_CHARS 16
37 
38 struct MultiTab {
39     MultiWin *parent;
40     GtkWidget *widget;            /* Top-level widget in notebook */
41     char *window_title;
42     char *window_title_template;
43     GtkWidget *popup_menu_item, *menu_bar_item;
44     GtkWidget *active_widget;
45     gpointer user_data;
46     GtkWidget *label;
47     GtkAdjustment *adjustment;
48     double scroll_step;
49     GtkWidget *close_button;
50     GtkWidget *label_box;
51     const char *status_icon_name;
52     GtkWidget *rename_dialog;
53     gboolean postponed_free;
54     gboolean old_win_destroyed;
55     gboolean title_template_locked;
56     int middle_click_action;
57     gboolean restore_pending;
58     int restore_rows, restore_columns;
59 };
60 
61 struct MultiWin {
62     GtkWidget *gtkwin;        /* Top-level window */
63     GtkWidget *vbox;          /* Container for menu bar, tabs and vte widget */
64     GtkWidget *notebook;
65     MenuTree *menu_bar;
66     MenuTree *popup_menu;
67     MenuTree *short_popup;
68     guint ntabs;
69     GList *tabs;
70     MultiTab *current_tab;
71     GtkPositionType tab_pos;
72     gboolean always_show_tabs;
73     gpointer user_data_template;
74     gulong destroy_handler;
75     gboolean ignore_tab_selections;
76     gboolean ignore_toggles;
77     gboolean ignore_tabs_moving;
78     gboolean show_menu_bar;
79     Options *shortcuts;
80     GtkAccelGroup *accel_group;
81     MultiTabSelectionHandler tab_selection_handler;
82     gboolean menu_bar_set;        /* Menu bar can be configured either from
83                                    profile when opening a window, or when user
84                                    clicks on menu. Former case doesn't resize
85                                    window and should only be done once; this
86                                    flag ensures attempts for subsequent tabs
87                                    are ignored */
88     MultiWinScrollBar_Position scroll_bar_pos;
89     gboolean wrap_switch_tab;
90     int zoom_index;
91     gboolean borderless;
92     gboolean fullscreen;
93     char *title_template;
94     char *child_title;
95     gboolean composite;
96     gboolean title_template_locked;
97     int best_tab_width;
98     GtkWidget *add_tab_button;
99     gboolean show_add_tab_button;
100     GdkGeometry geom_hints;
101     GdkWindowHints geom_hint_mask;
102     gboolean has_geometry;
103 };
104 
105 static double multi_win_zoom_factors[] = {
106     PANGO_SCALE_XX_SMALL / (1.2 *1.2 * 1.2 * 1.2),
107     PANGO_SCALE_XX_SMALL / (1.2 * 1.2 * 1.2),
108     PANGO_SCALE_XX_SMALL / (1.2 * 1.2),
109     PANGO_SCALE_XX_SMALL / 1.2,
110     PANGO_SCALE_XX_SMALL,
111     PANGO_SCALE_X_SMALL,
112     PANGO_SCALE_SMALL,
113     PANGO_SCALE_MEDIUM,
114     PANGO_SCALE_LARGE,
115     PANGO_SCALE_X_LARGE,
116     PANGO_SCALE_XX_LARGE,
117     PANGO_SCALE_XX_LARGE * 1.2,
118     PANGO_SCALE_XX_LARGE * 1.2 * 1.2,
119     PANGO_SCALE_XX_LARGE * 1.2 * 1.2 * 1.2,
120     PANGO_SCALE_XX_LARGE * 1.2 * 1.2 * 1.2 * 1.2
121 };
122 #define MULTI_WIN_NORMAL_ZOOM_INDEX 7
123 #define MULTI_WIN_N_ZOOM_FACTORS 15
124 
125 static char *multi_win_role_prefix = NULL;
126 static int multi_win_role_index = 0;
127 
128 static MultiTabFiller multi_tab_filler;
129 static MultiTabDestructor multi_tab_destructor;
130 static MultiWinMenuSignalConnector multi_win_menu_signal_connector;
131 static MultiWinGeometryFunc multi_win_geometry_func;
132 static MultiWinSizeFunc multi_win_size_func;
133 static MultiWinDefaultSizeFunc multi_win_default_size_func;
134 static MultiTabToNewWindowHandler multi_tab_to_new_window_handler;
135 static MultiWinZoomHandler multi_win_zoom_handler;
136 MultiWinGetDisableMenuShortcuts multi_win_get_disable_menu_shortcuts;
137 static MultiWinGetTabPos multi_win_get_config_tab_pos;
138 static MultiWinDeleteHandler multi_win_delete_handler;
139 static MultiTabGetShowCloseButton multi_tab_get_show_close_button;
140 static MultiTabGetNewTabAdjacent multi_tab_get_new_tab_adjacent;
141 static MultiTabConnectMiscSignals multi_tab_connect_misc_signals;
142 
143 static gboolean multi_win_notify_tab_removed(MultiWin *, MultiTab *);
144 
145 static void multi_win_add_tab(MultiWin *, MultiTab *, int position,
146         gboolean notify_only);
147 
148 static void multi_tab_add_menutree_items(MultiWin * win, MultiTab * tab,
149         int position);
150 
151 static void multi_win_select_tab_action(GtkCheckMenuItem *widget,
152         MultiTab * tab);
153 
154 static void multi_win_destructor(MultiWin *win, gboolean destroy_widgets);
155 
156 static void multi_win_close_tab_clicked(GtkWidget *widget, MultiTab *tab);
157 
158 static gboolean multi_win_process_geometry(MultiWin *win,
159         MultiTab *tab, int columns, int rows, int *width, int *height);
160 
161 static char *multi_tab_get_full_window_title(MultiTab * tab);
162 
multi_tab_do_restore_size(MultiTab * tab)163 static gboolean multi_tab_do_restore_size(MultiTab *tab)
164 {
165     int width, height;
166 
167     multi_win_process_geometry(tab->parent, tab,
168             tab->restore_columns, tab->restore_rows, &width, &height);
169     gtk_window_resize(GTK_WINDOW(tab->parent->gtkwin), width, height);
170     return FALSE;
171 }
172 
multi_tab_size_allocate(GtkWidget * widget,GdkRectangle * alloc,MultiTab * tab)173 static void multi_tab_size_allocate(GtkWidget *widget, GdkRectangle *alloc,
174         MultiTab *tab)
175 {
176     (void) widget;
177     (void) alloc;
178 
179     if (tab->restore_pending)
180     {
181         tab->restore_pending = FALSE;
182         /* Calling gtk_window_resize from a size-allocate handler doesn't seem
183          * to work, which is probably quite a sensible precaution by GTK, but it
184          * means we have to make another deferment.
185          */
186         g_idle_add((GSourceFunc) multi_tab_do_restore_size, tab);
187     }
188 }
189 
190 /* After adding/removing menu bars etc we want to resize the window to preserve
191  * the terminal's geometry. But when the packing change unwantedly resizes the
192  * terminal widget the only way to reliably read its new size is during/after a
193  * size-allocate. This function caches the current size of the terminal's
194  * viewport (which we want to keep). The size-allocate handler then works out
195  * the difference between the window size and the new viewport size, then adds
196  * this to the old viewport size to get the new window size.
197  */
multi_win_request_size_restore(MultiWin * win)198 static void multi_win_request_size_restore(MultiWin *win)
199 {
200     MultiTab *tab = win->current_tab;
201 
202     if (!tab || !tab->active_widget ||
203             !gtk_widget_get_realized(tab->active_widget))
204     {
205         return;
206     }
207     if (multi_win_is_maximised(win) || multi_win_is_fullscreen(win))
208         return;
209     tab->restore_rows = -1;
210     tab->restore_columns = -1;
211     multi_win_size_func(tab->user_data, FALSE,
212             &tab->restore_columns, &tab->restore_rows);
213     tab->restore_pending = TRUE;
214 }
215 
multi_tab_get_from_widget(GtkWidget * widget)216 MultiTab *multi_tab_get_from_widget(GtkWidget *widget)
217 {
218     return g_object_get_data(G_OBJECT(widget), "roxterm_tab");
219 }
220 
multi_tab_popup_menu_at_pointer(MultiTab * tab)221 void multi_tab_popup_menu_at_pointer(MultiTab * tab)
222 {
223     GtkMenu *menu = GTK_MENU(menutree_get_top_level_widget
224             (tab->parent->show_menu_bar ? tab->parent->short_popup :
225             tab->parent->popup_menu));
226 
227     gtk_menu_set_screen(menu, gtk_widget_get_screen(tab->widget));
228     gtk_menu_popup_at_pointer(menu, NULL);
229 }
230 
231 /*
232 void multi_tab_popup_menu_over_widget(MultiTab * tab)
233 {
234     GtkMenu *menu = GTK_MENU(menutree_get_top_level_widget
235             (tab->parent->show_menu_bar ? tab->parent->short_popup :
236             tab->parent->popup_menu));
237 
238     gtk_menu_set_screen(menu, gtk_widget_get_screen(tab->widget));
239     gtk_menu_popup_at_widget(menu, tab->active_widget,
240             GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL);
241 }
242 */
243 
multi_tab_init(MultiTabFiller filler,MultiTabDestructor destructor,MultiWinMenuSignalConnector menu_signal_connector,MultiWinGeometryFunc geometry_func,MultiWinSizeFunc size_func,MultiWinDefaultSizeFunc default_size_func,MultiTabToNewWindowHandler tab_to_new_window_handler,MultiWinZoomHandler zoom_handler,MultiWinGetDisableMenuShortcuts get_disable_menu_shortcuts,MultiWinGetTabPos tab_pos,MultiWinDeleteHandler delete_handler,MultiTabGetShowCloseButton get_show_close_button,MultiTabGetNewTabAdjacent get_new_tab_adjacent,MultiTabConnectMiscSignals connect_misc_signals)244 void multi_tab_init(MultiTabFiller filler, MultiTabDestructor destructor,
245     MultiWinMenuSignalConnector menu_signal_connector,
246     MultiWinGeometryFunc geometry_func, MultiWinSizeFunc size_func,
247     MultiWinDefaultSizeFunc default_size_func,
248     MultiTabToNewWindowHandler tab_to_new_window_handler,
249     MultiWinZoomHandler zoom_handler,
250     MultiWinGetDisableMenuShortcuts get_disable_menu_shortcuts,
251     MultiWinGetTabPos tab_pos,
252     MultiWinDeleteHandler delete_handler,
253     MultiTabGetShowCloseButton get_show_close_button,
254     MultiTabGetNewTabAdjacent get_new_tab_adjacent,
255     MultiTabConnectMiscSignals connect_misc_signals
256     )
257 {
258     multi_tab_filler = filler;
259     multi_tab_destructor = destructor;
260     multi_win_menu_signal_connector = menu_signal_connector;
261     multi_win_geometry_func = geometry_func;
262     multi_win_size_func = size_func;
263     multi_win_default_size_func = default_size_func;
264     multi_tab_to_new_window_handler = tab_to_new_window_handler;
265     multi_win_zoom_handler = zoom_handler;
266     multi_win_get_disable_menu_shortcuts = get_disable_menu_shortcuts;
267     multi_win_get_config_tab_pos = tab_pos;
268     multi_win_delete_handler = delete_handler;
269     multi_tab_get_show_close_button = get_show_close_button;
270     multi_tab_get_new_tab_adjacent = get_new_tab_adjacent;
271     multi_tab_connect_misc_signals = connect_misc_signals;
272 }
273 
274 // Doesn't connect client's signal handlers
multi_tab_new_defer_connect(MultiWin * parent,gpointer user_data_template)275 MultiTab *multi_tab_new_defer_connect(MultiWin * parent,
276         gpointer user_data_template)
277 {
278     MultiTab *tab = g_new0(MultiTab, 1);
279     guint pos;
280 
281     tab->parent = parent;
282     tab->status_icon_name = NULL;
283     tab->widget = multi_tab_filler(parent, tab, user_data_template,
284         &tab->user_data, &tab->active_widget, &tab->adjustment);
285     g_signal_connect(tab->widget, "size-allocate",
286             G_CALLBACK(multi_tab_size_allocate), tab);
287     g_object_set_data(G_OBJECT(tab->widget), "roxterm_tab", tab);
288 
289     if (parent->current_tab &&
290             multi_tab_get_new_tab_adjacent(user_data_template))
291     {
292         pos = multi_tab_get_page_num(parent->current_tab) + 1;
293         if (pos == parent->ntabs)
294             pos = -1;
295     }
296     else
297     {
298         pos = -1;
299     }
300     multi_win_add_tab(parent, tab, pos, FALSE);
301     multi_win_select_tab(parent, tab);
302 
303     //g_debug("call roxterm_force_resize_now from line %d", __LINE__);
304     //roxterm_force_resize_now(parent);
305 
306     return tab;
307 }
308 
multi_tab_new(MultiWin * parent,gpointer user_data_template)309 MultiTab *multi_tab_new(MultiWin * parent, gpointer user_data_template)
310 {
311     MultiTab *tab = multi_tab_new_defer_connect(parent, user_data_template);
312     //g_debug("multi_tab_new connecting signals for %p", tab->user_data);
313     multi_tab_connect_misc_signals(tab->user_data);
314     return tab;
315 }
316 
317 /* For setting a breakpoint */
multi_tab_free(MultiTab * tab)318 static void multi_tab_free(MultiTab *tab)
319 {
320     g_free(tab);
321 }
322 
multi_tab_delete_without_notifying_parent(MultiTab * tab,gboolean destroy_widgets)323 static void multi_tab_delete_without_notifying_parent(MultiTab * tab,
324         gboolean destroy_widgets)
325 {
326     if (tab->rename_dialog)
327     {
328         gtk_widget_destroy(tab->rename_dialog);
329         tab->postponed_free = TRUE;
330     }
331     else
332     {
333         tab->postponed_free = FALSE;
334     }
335     //g_debug("Deleting tab with roxterm %p", tab->user_data);
336     (*multi_tab_destructor) (tab->user_data);
337     tab->user_data = NULL;
338 
339     g_free(tab->window_title);
340     tab->window_title = NULL;
341     g_free(tab->window_title_template);
342     tab->window_title_template = NULL;
343     if (destroy_widgets && tab->widget)
344     {
345         gtk_widget_destroy(tab->widget);
346         tab->widget = NULL;
347     }
348     /* if !destroy_widgets menutrees are being destroyed anyway,
349      * so no need to update */
350     if (destroy_widgets && tab->popup_menu_item)
351     {
352         menutree_remove_tab(tab->parent->popup_menu, tab->popup_menu_item);
353         tab->popup_menu_item = NULL;
354     }
355     if (destroy_widgets && tab->menu_bar_item)
356     {
357         menutree_remove_tab(tab->parent->menu_bar, tab->menu_bar_item);
358         tab->menu_bar_item = NULL;
359     }
360     if (!tab->postponed_free)
361         multi_tab_free(tab);
362 }
363 
multi_tab_delete(MultiTab * tab)364 void multi_tab_delete(MultiTab * tab)
365 {
366     MultiWin *win = tab->parent;
367 
368     win->ignore_tabs_moving = TRUE;
369     multi_tab_delete_without_notifying_parent(tab, TRUE);
370     if (!multi_win_notify_tab_removed(win, tab))
371     {
372         win->ignore_tabs_moving = FALSE;
373     }
374 }
375 
multi_tab_get_widget(MultiTab * tab)376 GtkWidget *multi_tab_get_widget(MultiTab * tab)
377 {
378     return tab->widget;
379 }
380 
381 /*
382 static void multi_win_set_geometry_hints_for_tab(MultiWin * win, MultiTab * tab)
383 {
384     GdkGeometry geom;
385     GdkWindowHints hints;
386 
387     if (multi_win_geometry_func)
388     {
389         (*multi_win_geometry_func) (tab->user_data, &geom, &hints);
390         multi_win_set_geometry_hints(win, tab->active_widget, &geom, hints);
391     }
392 }
393 */
394 
make_title(const char * template,const char * title,int tab_num,int tab_count)395 static char *make_title(const char *template, const char *title,
396         int tab_num, int tab_count)
397 {
398     GString *subbed;
399     size_t n, l;
400     if (!template || !template[0])
401         return g_new0(char, 1);
402     l = strlen(template);
403     subbed = g_string_sized_new(strlen(template));
404     for (n = 0; n < l; ++n)
405     {
406         if (template[n] == '%')
407         {
408             switch (template[++n])
409             {
410                 case 's':
411                     if (title)
412                         g_string_append(subbed, title);
413                     break;
414                 case 't':
415                     g_string_append_printf(subbed, "%d", tab_num);
416                     break;
417                 case 'n':
418                     g_string_append_printf(subbed, "%d", tab_count);
419                     break;
420                 case 0:
421                     --n;    /* Make sure next iteration sees terminator */
422                     // Fall-through
423                 case '%':
424                     g_string_append_c(subbed, '%');
425                     break;
426                 default:
427                     g_string_append_c(subbed, '%');
428                     g_string_append_c(subbed, template[n]);
429                     break;
430             }
431         }
432         else
433         {
434             g_string_append_c(subbed, template[n]);
435         }
436     }
437     return g_string_free(subbed, FALSE);
438 }
439 
440 
multi_tab_set_full_window_title(MultiTab * tab)441 static void multi_tab_set_full_window_title(MultiTab * tab)
442 {
443     MultiWin *win = tab->parent;
444     char *tab_label;
445 
446     tab_label = multi_tab_get_full_window_title(tab);
447     if (tab->label)
448     {
449         multitab_label_set_text(MULTITAB_LABEL(tab->label), tab_label);
450     }
451     if (win)
452     {
453         win->ignore_toggles = TRUE;
454         if (win->current_tab == tab)
455             multi_win_set_title(win, tab->window_title);
456         if (tab->popup_menu_item)
457         {
458             tab->popup_menu_item = menutree_change_tab_title
459                 (win->popup_menu, tab->popup_menu_item, tab_label);
460             g_signal_connect(tab->popup_menu_item, "toggled",
461                 G_CALLBACK(multi_win_select_tab_action), tab);
462         }
463         if (tab->menu_bar_item)
464         {
465             tab->menu_bar_item = menutree_change_tab_title
466                 (win->menu_bar, tab->menu_bar_item, tab_label);
467             g_signal_connect(tab->menu_bar_item, "toggled",
468                 G_CALLBACK(multi_win_select_tab_action), tab);
469         }
470         win->ignore_toggles = FALSE;
471     }
472     g_free(tab_label);
473 }
474 
multi_tab_set_window_title(MultiTab * tab,const char * title)475 void multi_tab_set_window_title(MultiTab * tab, const char *title)
476 {
477     g_free(tab->window_title);
478     tab->window_title = title ? g_strdup(title) : NULL;
479     multi_tab_set_full_window_title(tab);
480 }
481 
multi_tab_set_window_title_template(MultiTab * tab,const char * template)482 void multi_tab_set_window_title_template(MultiTab * tab, const char *template)
483 {
484     if (tab->title_template_locked)
485         return;
486     g_free(tab->window_title_template);
487     tab->window_title_template = template ? g_strdup(template) : NULL;
488     multi_tab_set_full_window_title(tab);
489 }
490 
multi_tab_get_title_template_locked(MultiTab * tab)491 gboolean multi_tab_get_title_template_locked(MultiTab *tab)
492 {
493     return tab->title_template_locked;
494 }
495 
multi_tab_set_title_template_locked(MultiTab * tab,gboolean locked)496 void multi_tab_set_title_template_locked(MultiTab *tab, gboolean locked)
497 {
498     tab->title_template_locked = locked;
499 }
500 
multi_tab_get_window_title(MultiTab * tab)501 const char *multi_tab_get_window_title(MultiTab * tab)
502 {
503     return tab->window_title;
504 }
505 
multi_tab_get_window_title_template(MultiTab * tab)506 const char *multi_tab_get_window_title_template(MultiTab * tab)
507 {
508     return tab->window_title_template;
509 }
510 
multi_tab_get_full_window_title(MultiTab * tab)511 static char *multi_tab_get_full_window_title(MultiTab * tab)
512 {
513     int num = tab->parent->ntabs;
514     int pos = multi_tab_get_page_num(tab) + 1;
515     return make_title(tab->window_title_template, tab->window_title, pos, num);
516 }
517 
multi_tab_get_user_data(MultiTab * tab)518 gpointer multi_tab_get_user_data(MultiTab * tab)
519 {
520     return tab->user_data;
521 }
522 
multi_tab_connect_tab_selection_handler(MultiWin * win,MultiTabSelectionHandler handler)523 void multi_tab_connect_tab_selection_handler(MultiWin * win,
524     MultiTabSelectionHandler handler)
525 {
526     win->tab_selection_handler = handler;
527 }
528 
multi_tab_get_page_num(MultiTab * tab)529 int multi_tab_get_page_num(MultiTab *tab)
530 {
531     return gtk_notebook_page_num(GTK_NOTEBOOK(tab->parent->notebook),
532             tab->widget);
533 }
534 
multi_tab_get_parent(MultiTab * tab)535 MultiWin *multi_tab_get_parent(MultiTab *tab)
536 {
537     return tab->parent;
538 }
539 
540 inline static void
multi_win_shade_item_in_both_menus(MultiWin * win,MenuTreeID id,gboolean shade)541 multi_win_shade_item_in_both_menus(MultiWin * win, MenuTreeID id,
542     gboolean shade)
543 {
544     gtk_widget_set_sensitive(menutree_get_widget_for_id(win->menu_bar, id),
545         !shade);
546     gtk_widget_set_sensitive(menutree_get_widget_for_id(win->popup_menu, id),
547         !shade);
548 }
549 
multi_win_at_first_tab(MultiWin * win)550 inline static gboolean multi_win_at_first_tab(MultiWin *win)
551 {
552     return g_list_previous(g_list_find(win->tabs, win->current_tab)) == NULL;
553 }
554 
multi_win_at_last_tab(MultiWin * win)555 inline static gboolean multi_win_at_last_tab(MultiWin *win)
556 {
557     return g_list_next(g_list_find(win->tabs, win->current_tab)) == NULL;
558 }
559 
560 /* Returns TRUE if menu items should be shaded based on wrap_switch_tab flag
561  * and on whether there's only one tab */
multi_win_override_shade_next_prev_tab(MultiWin * win)562 inline static gboolean multi_win_override_shade_next_prev_tab(MultiWin *win)
563 {
564     return win->ntabs == 1 || !win->wrap_switch_tab;
565 }
566 
multi_win_shade_for_previous_tab(MultiWin * win)567 static void multi_win_shade_for_previous_tab(MultiWin *win)
568 {
569     gboolean shade = multi_win_override_shade_next_prev_tab(win) &&
570             multi_win_at_first_tab(win);
571 
572     multi_win_shade_item_in_both_menus(win, MENUTREE_TABS_PREVIOUS_TAB,
573             shade);
574     multi_win_shade_item_in_both_menus(win, MENUTREE_TABS_MOVE_TAB_LEFT,
575             shade);
576 }
577 
multi_win_shade_for_next_tab(MultiWin * win)578 static void multi_win_shade_for_next_tab(MultiWin * win)
579 {
580     gboolean shade = multi_win_override_shade_next_prev_tab(win) &&
581             multi_win_at_last_tab(win);
582 
583     multi_win_shade_item_in_both_menus(win, MENUTREE_TABS_NEXT_TAB,
584             shade);
585     multi_win_shade_item_in_both_menus(win, MENUTREE_TABS_MOVE_TAB_RIGHT,
586             shade);
587 }
588 
multi_win_shade_for_next_and_previous_tab(MultiWin * win)589 inline static void multi_win_shade_for_next_and_previous_tab(MultiWin * win)
590 {
591     multi_win_shade_for_previous_tab(win);
592     multi_win_shade_for_next_tab(win);
593 }
594 
multi_win_shade_for_single_tab(MultiWin * win)595 static void multi_win_shade_for_single_tab(MultiWin * win)
596 {
597     multi_win_shade_item_in_both_menus(win, MENUTREE_TABS_CLOSE_OTHER_TABS,
598             win->ntabs <= 1);
599     multi_win_shade_item_in_both_menus(win, MENUTREE_TABS_DETACH_TAB,
600             win->ntabs <= 1);
601 }
602 
multi_win_shade_menus_for_tabs(MultiWin * win)603 static void multi_win_shade_menus_for_tabs(MultiWin * win)
604 {
605     multi_win_shade_for_next_and_previous_tab(win);
606     multi_win_shade_for_single_tab(win);
607 }
608 
multi_tab_remove_menutree_items(MultiWin * win,MultiTab * tab)609 static void multi_tab_remove_menutree_items(MultiWin * win, MultiTab * tab)
610 {
611     if (tab->menu_bar_item)
612     {
613         menutree_remove_tab(win->menu_bar, tab->menu_bar_item);
614         tab->menu_bar_item = NULL;
615     }
616     if (tab->popup_menu_item)
617     {
618         menutree_remove_tab(win->popup_menu, tab->popup_menu_item);
619         tab->popup_menu_item = NULL;
620     }
621 }
622 
renumber_tabs(MultiWin * win)623 static void renumber_tabs(MultiWin *win)
624 {
625     GList *link;
626     int n = 0;
627 
628     for (link = win->tabs; link; link = g_list_next(link))
629     {
630         MultiTab *tab = link->data;
631 
632         multi_tab_set_full_window_title(tab);
633         ++n;
634     }
635 }
636 
multi_tab_move_to_position(MultiTab * tab,int position,gboolean reorder)637 void multi_tab_move_to_position(MultiTab *tab, int position, gboolean reorder)
638 {
639     MultiWin *win = tab->parent;
640 
641     if (reorder)
642     {
643         gtk_notebook_reorder_child(GTK_NOTEBOOK(win->notebook),
644                 tab->widget, position);
645     }
646     win->tabs = g_list_remove(win->tabs, tab);
647     win->tabs = g_list_insert(win->tabs, tab, position);
648     renumber_tabs(win);
649     multi_win_shade_menus_for_tabs(win);
650     multi_tab_remove_menutree_items(win, tab);
651     multi_tab_add_menutree_items(win, tab, position);
652     multi_tab_set_full_window_title(tab);
653 }
654 
multi_tab_remove_from_parent(MultiTab * tab,gboolean notify_only)655 gboolean multi_tab_remove_from_parent(MultiTab *tab, gboolean notify_only)
656 {
657     MultiWin *win = tab->parent;
658     gboolean destroyed;
659 
660     g_return_val_if_fail(win, TRUE);
661     win->ignore_tabs_moving = TRUE;
662     if (!notify_only)
663     {
664         g_object_ref(tab->widget);
665         tab->label = NULL;
666         gtk_notebook_detach_tab(GTK_NOTEBOOK(win->notebook), tab->widget);
667         tab->close_button = NULL;
668     }
669     multi_tab_remove_menutree_items(win, tab);
670     destroyed = multi_win_notify_tab_removed(win, tab);
671     if (!destroyed)
672     {
673         win->ignore_tabs_moving = FALSE;
674     }
675     tab->parent = NULL;
676     return destroyed;
677 }
678 
multi_tab_move_to_new_window(MultiWin * win,MultiTab * tab,int position)679 void multi_tab_move_to_new_window(MultiWin *win, MultiTab *tab, int position)
680 {
681     MultiWin *old_win = tab->parent;
682     gboolean old_win_destroyed;
683 
684     multi_win_set_always_show_tabs(win, old_win->always_show_tabs);
685     multi_win_set_show_menu_bar(win, old_win->show_menu_bar);
686     if (multi_win_is_borderless(old_win))
687         multi_win_set_borderless(win, TRUE);
688     if (multi_win_is_fullscreen(old_win))
689         multi_win_set_fullscreen(win, TRUE);
690     else if (multi_win_is_maximised(old_win))
691         gtk_window_maximize(GTK_WINDOW(win->gtkwin));
692     if (!win->tabs)
693     {
694         win->title_template = old_win->title_template ?
695                 g_strdup(old_win->title_template) : NULL;
696     }
697     old_win_destroyed = old_win && old_win->ntabs <= 1;
698     multi_tab_remove_from_parent(tab, FALSE);
699     multi_win_add_tab(win, tab, position, FALSE);
700     /* multi_win_set_geometry_hints_for_tab(win, tab); */
701     if (multi_tab_to_new_window_handler)
702     {
703         multi_tab_to_new_window_handler(win, tab,
704                 old_win_destroyed ? NULL : old_win);
705     }
706     /* Remove extra references added by multi_tab_remove_from_parent */
707     g_object_unref(tab->widget);
708 }
709 
multi_tab_draw_attention(MultiTab * tab)710 void multi_tab_draw_attention(MultiTab *tab)
711 {
712     multitab_label_draw_attention(MULTITAB_LABEL(tab->label));
713 }
714 
multi_tab_cancel_attention(MultiTab * tab)715 void multi_tab_cancel_attention(MultiTab *tab)
716 {
717     multitab_label_cancel_attention(MULTITAB_LABEL(tab->label));
718 }
719 
multi_tab_pack_for_horizontal(MultiTab * tab,GtkContainer * nb)720 static void multi_tab_pack_for_horizontal(MultiTab *tab, GtkContainer *nb)
721 {
722     gtk_container_child_set(nb, tab->widget,
723             "tab-expand", FALSE, "tab-fill", TRUE, NULL);
724     multitab_label_set_single(MULTITAB_LABEL(tab->label), TRUE);
725 }
726 
multi_tab_pack_for_single(MultiTab * tab,GtkContainer * nb)727 static void multi_tab_pack_for_single(MultiTab *tab, GtkContainer *nb)
728 {
729     gtk_container_child_set(nb, tab->widget,
730             "tab-expand", FALSE, "tab-fill", FALSE, NULL);
731     multitab_label_set_single(MULTITAB_LABEL(tab->label), TRUE);
732 }
733 
multi_win_pack_for_single_tab(MultiWin * win)734 static void multi_win_pack_for_single_tab(MultiWin *win)
735 {
736     MultiTab *tab = win->tabs->data;
737 
738     multi_tab_pack_for_single(tab, GTK_CONTAINER(win->notebook));
739 }
740 
multi_tab_pack_for_multiple(MultiTab * tab,GtkContainer * nb)741 static void multi_tab_pack_for_multiple(MultiTab *tab, GtkContainer *nb)
742 {
743     gtk_container_child_set(nb, tab->widget,
744             "tab-expand", TRUE, "tab-fill", TRUE, NULL);
745     multitab_label_set_single(MULTITAB_LABEL(tab->label), FALSE);
746 }
747 
multi_win_pack_for_multiple_tabs(MultiWin * win)748 static void multi_win_pack_for_multiple_tabs(MultiWin *win)
749 {
750     GList *link;
751     GtkContainer *nb = GTK_CONTAINER(win->notebook);
752 
753     for (link = win->tabs; link; link = g_list_next(link))
754     {
755         MultiTab *tab = link->data;
756 
757         multi_tab_pack_for_multiple(tab, nb);
758     }
759 }
760 
multi_win_set_full_title(MultiWin * win)761 static void multi_win_set_full_title(MultiWin *win)
762 {
763     if (win->gtkwin)
764     {
765         int pos = win && win->current_tab ?
766                   multi_tab_get_page_num(win->current_tab) + 1 : 1;
767         char *title0 = make_title(win->title_template, win->child_title,
768                 pos, win->ntabs);
769 
770         gtk_window_set_title(GTK_WINDOW(win->gtkwin), title0);
771         g_free(title0);
772     }
773 }
774 
multi_win_set_title(MultiWin * win,const char * title)775 void multi_win_set_title(MultiWin *win, const char *title)
776 {
777     if (win->child_title != title)
778     {
779         g_free(win->child_title);
780         win->child_title = title ? g_strdup(title) : NULL;
781     }
782     multi_win_set_full_title(win);
783 }
784 
multi_win_highlight_selected_tab(MultiWin * win)785 static void multi_win_highlight_selected_tab(MultiWin *win)
786 {
787     GList *link;
788     for (link = win->tabs; link; link = g_list_next(link))
789     {
790         MultiTab *tab = link->data;
791         /* If this is called while a tab is being moved to another window its
792          * label may be NULL
793          */
794         if (tab->label)
795         {
796             multitab_label_set_current(MULTITAB_LABEL(tab->label),
797                     tab == win->current_tab);
798         }
799     }
800 }
801 
multi_win_select_tab(MultiWin * win,MultiTab * tab)802 void multi_win_select_tab(MultiWin * win, MultiTab * tab)
803 {
804     /* This is called by anything that causes a change of tab to keep things
805      * consistent; doing this will cause extra tab change related events to be
806      * generated so we have to ignore them for the duration */
807     if (win->ignore_tab_selections || tab == win->current_tab)
808     {
809         return;
810     }
811     win->ignore_tab_selections = TRUE;
812     win->ignore_toggles = TRUE;
813     win->current_tab = tab;
814     if (tab)
815     {
816         char *title = multi_tab_get_full_window_title(tab);
817 
818         if (tab->label)
819             multitab_label_cancel_attention(MULTITAB_LABEL(tab->label));
820         multi_tab_set_status_icon_name(tab, NULL);
821         win->user_data_template = tab->user_data;
822         gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook),
823             multi_tab_get_page_num(tab));
824         gtk_widget_grab_focus(tab->active_widget);
825         multi_win_set_title(win, tab->window_title);
826         g_free(title);
827         menutree_select_tab(win->popup_menu, tab->popup_menu_item);
828         menutree_select_tab(win->menu_bar, tab->menu_bar_item);
829         if (gtk_widget_get_realized(tab->active_widget))
830         {
831             if (win->tab_selection_handler)
832                 win->tab_selection_handler(tab->user_data, tab);
833         }
834     }
835     multi_win_shade_for_next_and_previous_tab(win);
836     multi_win_highlight_selected_tab(win);
837     /* FIXME: Ideally we should shade scroll up/down menu items if tab doesn't
838      * have an adjustment, but in this implementation they always do
839      */
840     win->ignore_tab_selections = FALSE;
841     win->ignore_toggles = FALSE;
842 }
843 
844 /* The top-level needs to be transparent as a whole to support the transparency
845  * option, but the menu bar is also transparent, so we need to manually fill in
846  * its background.
847  */
848 static gboolean
multi_win_draw_chrome_bg(GtkWidget * widget,cairo_t * cr,MultiWin * win)849 multi_win_draw_chrome_bg(GtkWidget *widget, cairo_t *cr, MultiWin *win)
850 {
851     GtkStyleContext *context = gtk_widget_get_style_context(widget);
852     GtkAllocation alloc;
853 
854     gtk_widget_get_allocation(win->vbox, &alloc);
855     gtk_render_background(context, cr,
856             alloc.x, alloc.y, alloc.width, alloc.height);
857     return FALSE;
858 }
859 
add_menu_bar(MultiWin * win)860 static void add_menu_bar(MultiWin *win)
861 {
862     GtkWidget *menu_bar;
863 
864     if (win->show_menu_bar)
865         return;
866     menu_bar = menutree_get_top_level_widget(win->menu_bar);
867     gtk_box_pack_start(GTK_BOX(win->vbox), menu_bar, FALSE, FALSE, 0);
868     gtk_box_reorder_child(GTK_BOX(win->vbox), menu_bar, 0);
869     gtk_widget_show(menu_bar);
870     win->show_menu_bar = TRUE;
871 }
872 
remove_menu_bar(MultiWin * win)873 static void remove_menu_bar(MultiWin *win)
874 {
875     GtkWidget *menu_bar;
876 
877     if (!win->show_menu_bar)
878         return;
879     menu_bar = menutree_get_top_level_widget(win->menu_bar);
880     gtk_widget_hide(menu_bar);
881     gtk_container_remove(GTK_CONTAINER(win->vbox), menu_bar);
882     win->show_menu_bar = FALSE;
883 }
884 
multi_win_set_show_menu_bar(MultiWin * win,gboolean show)885 void multi_win_set_show_menu_bar(MultiWin * win, gboolean show)
886 {
887     if (win->menu_bar_set == show)
888         return;
889     multi_win_request_size_restore(win);
890     win->menu_bar_set = show;
891     if (show)
892         add_menu_bar(win);
893     else
894         remove_menu_bar(win);
895     menutree_set_show_menu_bar_active(win->menu_bar, show);
896     menutree_set_show_menu_bar_active(win->popup_menu, show);
897     menutree_set_show_menu_bar_active(win->short_popup, show);
898 }
899 
multi_win_get_show_menu_bar(MultiWin * win)900 gboolean multi_win_get_show_menu_bar(MultiWin * win)
901 {
902     return win->show_menu_bar;
903 }
904 
multi_win_set_borderless(MultiWin * win,gboolean borderless)905 void multi_win_set_borderless(MultiWin *win, gboolean borderless)
906 {
907     if (borderless != win->borderless)
908     {
909         gtk_window_set_decorated(GTK_WINDOW(win->gtkwin), !borderless);
910         win->borderless = borderless;
911         menutree_set_borderless_active(win->menu_bar, borderless);
912         menutree_set_borderless_active(win->popup_menu, borderless);
913     }
914 }
915 
multi_win_set_fullscreen(MultiWin * win,gboolean fullscreen)916 void multi_win_set_fullscreen(MultiWin *win, gboolean fullscreen)
917 {
918     if (fullscreen == win->fullscreen)
919         return;
920     if (fullscreen)
921     {
922         gtk_window_fullscreen(GTK_WINDOW(win->gtkwin));
923     }
924     else
925     {
926         gtk_window_unfullscreen(GTK_WINDOW(win->gtkwin));
927     }
928     win->fullscreen = fullscreen;
929 }
930 
931 #if GTK_CHECK_VERSION(3,22,23)
932 #define WIN_STATE_TILED \
933             (GDK_WINDOW_STATE_TOP_TILED | GDK_WINDOW_STATE_BOTTOM_TILED | \
934             GDK_WINDOW_STATE_LEFT_TILED | GDK_WINDOW_STATE_RIGHT_TILED)
935 #else
936 #define WIN_STATE_TILED GDK_WINDOW_STATE_TILED
937 #endif
938 
939 #define WIN_STATE_SNAPPED (GDK_WINDOW_STATE_MAXIMIZED | \
940         GDK_WINDOW_STATE_FULLSCREEN | WIN_STATE_TILED)
941 
multi_win_apply_geometry_hints(MultiWin * win)942 inline static void multi_win_apply_geometry_hints(MultiWin *win)
943 {
944     gtk_window_set_geometry_hints(GTK_WINDOW(win->gtkwin), NULL,
945             &win->geom_hints, win->geom_hint_mask);
946 }
947 
multi_win_state_event_handler(GtkWidget * widget,GdkEventWindowState * event,MultiWin * win)948 static gboolean multi_win_state_event_handler(GtkWidget *widget,
949         GdkEventWindowState *event, MultiWin *win)
950 {
951     /* Having geometry hints set causes window to shrink by one
952      * character width and height when unmaximizing.
953      * <https://gitlab.gnome.org/GNOME/gtk/issues/34>
954      * This is a workaround.
955      */
956     if (event->changed_mask & WIN_STATE_SNAPPED)
957     {
958         if (event->new_window_state & WIN_STATE_SNAPPED)
959         {
960             gtk_window_set_geometry_hints(GTK_WINDOW(widget), NULL, NULL, 0);
961         }
962         else if (win->has_geometry)
963         {
964             multi_win_apply_geometry_hints(win);
965         }
966     }
967 
968     if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
969     {
970         win->fullscreen =
971                 (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
972         menutree_set_fullscreen_active(win->menu_bar, win->fullscreen);
973         menutree_set_fullscreen_active(win->popup_menu, win->fullscreen);
974     }
975     return FALSE;
976 }
977 
multi_win_realize_handler(GtkWidget * win,gpointer data)978 static void multi_win_realize_handler(GtkWidget *win, gpointer data)
979 {
980     GdkWindow *w = gtk_widget_get_window(win);
981 
982     gdk_window_set_group(w, w);
983     (void) data;
984 }
985 
multi_win_destroy_handler(GObject * obj,MultiWin * win)986 static void multi_win_destroy_handler(GObject * obj, MultiWin * win)
987 {
988     (void) obj;
989 
990     multi_win_destructor(win, FALSE);
991 }
992 
multi_win_menutree_deleted_handler(MenuTree * tree,gpointer data)993 static void multi_win_menutree_deleted_handler(MenuTree * tree, gpointer data)
994 {
995     MultiWin *win = data;
996 
997     if (win->menu_bar == tree)
998         win->menu_bar = NULL;
999     else if (win->popup_menu == tree)
1000         win->popup_menu = NULL;
1001     else if (win->short_popup == tree)
1002         win->short_popup = NULL;
1003     else
1004     {
1005         g_warning("Unknown MenuTree deleted");
1006     }
1007 }
1008 
1009 static void
multi_win_page_switched(GtkNotebook * notebook,GtkWidget * page,guint page_num,MultiWin * win)1010 multi_win_page_switched(GtkNotebook * notebook, GtkWidget *page,
1011     guint page_num, MultiWin * win)
1012 {
1013     MultiTab *tab = multi_tab_get_from_widget(
1014             gtk_notebook_get_nth_page(GTK_NOTEBOOK(win->notebook), page_num));
1015     (void) notebook;
1016     (void) page;
1017 
1018     if (win->current_tab == tab)
1019         return;
1020     multi_win_select_tab(win, tab);
1021 }
1022 
tab_set_name_from_clipboard_callback(GtkClipboard * clp,const gchar * text,gpointer data)1023 static void tab_set_name_from_clipboard_callback(GtkClipboard *clp,
1024         const gchar *text, gpointer data)
1025 {
1026     MultiTab *tab = (MultiTab *) data;
1027     (void) clp;
1028 
1029     if (text == NULL || tab == NULL)
1030         return;
1031     multi_tab_set_window_title_template(tab, text);
1032 }
1033 
multi_win_focus_in(GtkWidget * widget,GdkEventFocus * event,MultiWin * win)1034 static gboolean multi_win_focus_in(GtkWidget *widget, GdkEventFocus *event,
1035         MultiWin *win)
1036 {
1037     (void) widget;
1038     (void) event;
1039     multi_win_all = g_list_remove(multi_win_all, win);
1040     multi_win_all = g_list_prepend(multi_win_all, win);
1041     gtk_window_set_urgency_hint(GTK_WINDOW(win->gtkwin), FALSE);
1042     return FALSE;
1043 }
1044 
popup_tabs_menu(MultiWin * win,GdkEvent * event)1045 static void popup_tabs_menu(MultiWin *win, GdkEvent *event)
1046 {
1047     GtkMenuItem *mparent;
1048 
1049     g_return_if_fail(win->popup_menu != NULL);
1050     mparent = GTK_MENU_ITEM(
1051             menutree_get_widget_for_id(win->popup_menu, MENUTREE_TABS));
1052     gtk_menu_popup_at_pointer(GTK_MENU(gtk_menu_item_get_submenu(mparent)),
1053             event);
1054 }
1055 
tab_clicked_handler(GtkWidget * widget,GdkEventButton * event,MultiTab * tab)1056 static gboolean tab_clicked_handler(GtkWidget *widget,
1057         GdkEventButton *event, MultiTab *tab)
1058 {
1059     (void) widget;
1060     if (event->type != GDK_BUTTON_PRESS)
1061         return FALSE;
1062 
1063     switch (event->button)
1064     {
1065         case 2:
1066             switch (tab->middle_click_action)
1067             {
1068                 case 1:
1069                     multi_win_close_tab_clicked(NULL, tab);
1070                     break;
1071                 case 2:
1072                     gtk_clipboard_request_text(
1073                             gtk_clipboard_get(GDK_SELECTION_PRIMARY),
1074                             tab_set_name_from_clipboard_callback, tab);
1075                     break;
1076                 default:
1077                     break;
1078             }
1079             return TRUE;
1080         case 3:
1081             multi_win_select_tab(tab->parent, tab);
1082             popup_tabs_menu(tab->parent, (GdkEvent *) event);
1083             return TRUE;
1084         default:
1085             break;
1086     }
1087     return FALSE;
1088 }
1089 
1090 /* Creates the label widget for a tab. tab->label is the GtkLabel containing
1091  * the text; the return value is the top-level container. */
make_tab_label(MultiTab * tab,GtkPositionType tab_pos)1092 static GtkWidget *make_tab_label(MultiTab *tab, GtkPositionType tab_pos)
1093 {
1094     (void) tab_pos;
1095     tab->label = multitab_label_new(tab->parent->notebook, NULL,
1096             &tab->parent->best_tab_width);
1097     multi_tab_set_full_window_title(tab);
1098     tab->label_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1099     gtk_box_set_homogeneous(GTK_BOX(tab->label_box), FALSE);
1100     gtk_box_pack_start(GTK_BOX(tab->label_box), tab->label, TRUE, TRUE, 0);
1101     g_signal_connect(tab->label, "button-press-event",
1102             G_CALLBACK(tab_clicked_handler), tab);
1103     if (multi_tab_get_show_close_button(tab->user_data))
1104     {
1105         multi_tab_add_close_button(tab);
1106     }
1107     gtk_widget_show_all(tab->label_box);
1108 
1109     return tab->label_box;
1110 }
1111 
page_reordered_callback(GtkNotebook * notebook,GtkWidget * child,guint page_num,MultiWin * win)1112 static void page_reordered_callback(GtkNotebook *notebook, GtkWidget *child,
1113         guint page_num, MultiWin *win)
1114 {
1115     MultiTab *tab = multi_tab_get_from_widget(child);
1116     (void) notebook;
1117     (void) win;
1118 
1119     g_return_if_fail(tab);
1120     multi_tab_move_to_position(tab, page_num, FALSE);
1121 }
1122 
page_added_callback(GtkNotebook * notebook,GtkWidget * child,guint page_num,MultiWin * win)1123 static void page_added_callback(GtkNotebook *notebook, GtkWidget *child,
1124         guint page_num, MultiWin *win)
1125 {
1126     MultiTab *tab = multi_tab_get_from_widget(child);
1127     gboolean old_win_destroyed;
1128     MultiWin *old_win = tab->parent;
1129     gboolean vert_tabs;
1130 
1131     g_return_if_fail(tab);
1132     if (win->ignore_tabs_moving)
1133         return;
1134     if (tab->parent == win)
1135     {
1136         /* Dragged in from same window so do nothing */
1137         return;
1138     }
1139     vert_tabs = win->tab_pos == GTK_POS_LEFT || win->tab_pos == GTK_POS_RIGHT;
1140     if (tab->parent)
1141     {
1142         old_win_destroyed = multi_tab_remove_from_parent(tab, TRUE);
1143     }
1144     else
1145     {
1146         old_win_destroyed = TRUE;
1147         g_warning("Page added had no previous parent");
1148     }
1149     multi_win_add_tab(win, tab, page_num, TRUE);
1150     multi_tab_to_new_window_handler(win, tab,
1151             old_win_destroyed ? NULL : old_win);
1152     if (vert_tabs)
1153     {
1154         multi_tab_pack_for_horizontal(tab, GTK_CONTAINER(notebook));
1155     }
1156     else
1157     {
1158         if (win->ntabs == 1)
1159         {
1160             multi_win_pack_for_single_tab(win);
1161             gtk_widget_queue_resize(win->notebook);
1162             multi_win_show(win);
1163         }
1164         else if (win->ntabs == 2)
1165         {
1166             multi_win_pack_for_multiple_tabs(win);
1167         }
1168         else
1169         {
1170             multi_tab_pack_for_multiple(tab, GTK_CONTAINER(notebook));
1171         }
1172 #if 0
1173         if (old_win_destroyed && !vert_tabs)
1174         {
1175             /* The dragged tab's label will now be stupidly small,
1176              * but there's absolutely nothing we can do about it.
1177              */
1178         }
1179 #endif
1180     }
1181 }
1182 
multi_win_notebook_creation_hook(GtkNotebook * source,GtkWidget * page,gint x,gint y,gpointer data)1183 static GtkNotebook *multi_win_notebook_creation_hook(GtkNotebook *source,
1184         GtkWidget *page, gint x, gint y, gpointer data)
1185 {
1186     (void) source;
1187     (void) data;
1188     MultiTab *tab = multi_tab_get_from_widget(page);
1189     MultiWin *win;
1190 
1191     g_return_val_if_fail(tab, NULL);
1192     /* Need to set !expand in advance */
1193     multi_tab_pack_for_single(tab, GTK_CONTAINER(tab->parent->notebook));
1194     win = multi_win_new_for_tab(x, y, tab);
1195     /* Defer showing till page_added_callback because we can't set size
1196      * correctly until the tab has been added to the notebook */
1197     return GTK_NOTEBOOK(win->notebook);
1198 }
1199 
multi_win_new_for_tab(int x,int y,MultiTab * tab)1200 MultiWin *multi_win_new_for_tab(int x, int y, MultiTab *tab)
1201 {
1202     gboolean disable_menu_shortcuts, disable_tab_shortcuts;
1203     MultiWin *win = tab->parent;
1204     int w, h;
1205     GtkWindow *gwin = GTK_WINDOW(win->gtkwin);
1206     const char *title_template = win->title_template;
1207     gboolean show_menubar = win->show_menu_bar;
1208     gboolean borderless = win->borderless;
1209 
1210     gtk_window_get_size(gwin, &w, &h);
1211     multi_win_get_disable_menu_shortcuts(tab->user_data,
1212             &disable_menu_shortcuts, &disable_tab_shortcuts);
1213     win = multi_win_new_blank(multi_win_get_shortcut_scheme(win),
1214             win->zoom_index, disable_menu_shortcuts, disable_tab_shortcuts,
1215                 win->tab_pos, win->always_show_tabs,
1216                 win->show_add_tab_button);
1217     multi_win_set_show_menu_bar(win, show_menubar);
1218     multi_win_set_title_template(win, title_template);
1219     multi_win_set_borderless(win, borderless);
1220     gwin = GTK_WINDOW(win->gtkwin);
1221     gtk_window_set_default_size(gwin, w, h);
1222     if (x != -1 && y != -1)
1223         gtk_window_move(gwin, MAX(x - 20, 0), MAX(y - 8, 0));
1224     gtk_widget_show_all(win->notebook);
1225     return win;
1226 }
1227 
multi_win_close_tab_clicked(GtkWidget * widget,MultiTab * tab)1228 static void multi_win_close_tab_clicked(GtkWidget *widget, MultiTab *tab)
1229 {
1230     (void) widget;
1231     if (!multi_win_delete_handler ||
1232             !multi_win_delete_handler(tab->parent->gtkwin, NULL,
1233                     tab->user_data))
1234     {
1235         multi_tab_delete(tab);
1236     }
1237 }
1238 
multi_win_clone(MultiWin * old,gpointer user_data_template,gboolean always_show_tabs)1239 MultiWin *multi_win_clone(MultiWin *old,
1240         gpointer user_data_template, gboolean always_show_tabs)
1241 {
1242     char *geom;
1243     int width, height;
1244     MultiWin *result;
1245     GtkPositionType tab_pos = multi_win_get_config_tab_pos(user_data_template);
1246     gboolean borderless = multi_win_is_borderless(old);
1247 
1248     multi_win_default_size_func(multi_tab_get_user_data(old->current_tab),
1249             &width, &height);
1250     geom = g_strdup_printf("%dx%d", width, height);
1251     result = multi_win_new_with_geom(old->shortcuts,
1252             old->zoom_index, user_data_template, geom,
1253             tab_pos, borderless, always_show_tabs, old->show_add_tab_button);
1254     g_free(geom);
1255     return result;
1256 }
1257 
multi_win_new_window_action(MultiWin * win)1258 static void multi_win_new_window_action(MultiWin * win)
1259 {
1260     multi_win_clone(win,
1261             win->current_tab ?
1262                     win->current_tab->user_data : win->user_data_template,
1263             win->always_show_tabs);
1264 }
1265 
multi_win_new_tab_action(MultiWin * win)1266 static void multi_win_new_tab_action(MultiWin * win)
1267 {
1268     multi_tab_new(win, win->current_tab ?
1269             win->current_tab->user_data : win->user_data_template);
1270 }
1271 
multi_win_detach_tab_action(MultiWin * win)1272 static void multi_win_detach_tab_action(MultiWin * win)
1273 {
1274     MultiTab *tab = win->current_tab;
1275 
1276     win = multi_win_new_for_tab(-1, -1, tab);
1277     multi_tab_move_to_new_window(win, tab, -1);
1278     gtk_widget_show_all(win->gtkwin);
1279 }
1280 
multi_win_close_tab_action(MultiWin * win)1281 static void multi_win_close_tab_action(MultiWin * win)
1282 {
1283     multi_win_close_tab_clicked(NULL, win->current_tab);
1284 }
1285 
multi_win_close_other_tabs_action(MultiWin * win)1286 static void multi_win_close_other_tabs_action(MultiWin * win)
1287 {
1288     GList *link;
1289 
1290     while ((link = win->tabs) != NULL)
1291     {
1292         if (link->data == win->current_tab)
1293             link = g_list_next(link);
1294         if (link)
1295             multi_tab_delete(link->data);
1296         else
1297             break;
1298     }
1299 }
1300 
multi_win_name_tab_action(MultiWin * win)1301 static void multi_win_name_tab_action(MultiWin * win)
1302 {
1303     MultiTab *tab = win->current_tab;
1304     g_return_if_fail(tab);
1305     GtkWidget *dialog_w = gtk_dialog_new_with_buttons(_("Name Tab"),
1306             GTK_WINDOW(win->gtkwin),
1307             GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1308             _("_Cancel"), GTK_RESPONSE_CANCEL,
1309             _("_Apply"), GTK_RESPONSE_APPLY,
1310             NULL);
1311     GtkDialog *dialog = GTK_DIALOG(dialog_w);
1312     GtkWidget *name_w = gtk_entry_new();
1313     GtkEntry *name_e = GTK_ENTRY(name_w);
1314     const char *name = tab->window_title_template;
1315     int response;
1316 
1317     tab->rename_dialog = dialog_w;
1318     gtk_dialog_set_default_response(dialog, GTK_RESPONSE_APPLY);
1319     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(dialog)),
1320             name_w, TRUE, TRUE, 8);
1321     gtk_entry_set_activates_default(name_e, TRUE);
1322     gtk_entry_set_text(name_e, name ? name : "");
1323     gtk_widget_show_all(dialog_w);
1324     response = gtk_dialog_run(dialog);
1325     switch (response)
1326     {
1327         case GTK_RESPONSE_NONE:
1328         case GTK_RESPONSE_DELETE_EVENT:
1329             /* Don't destroy */
1330             break;
1331         case GTK_RESPONSE_APPLY:
1332             name = gtk_entry_get_text(name_e);
1333             tab->title_template_locked = FALSE;
1334             if (name && !name[0])
1335                 name = NULL;
1336             multi_tab_set_window_title_template(tab, name);
1337             tab->title_template_locked = (name != NULL);
1338             /* Fall through to destroy */
1339         default:
1340             gtk_widget_destroy(dialog_w);
1341             break;
1342     }
1343     tab->rename_dialog = NULL;
1344     if (tab->postponed_free)
1345         multi_tab_free(tab);
1346 }
multi_win_save_session_action(MultiWin * win)1347 static void multi_win_save_session_action(MultiWin * win)
1348 {
1349     GtkWidget *dialog_w = gtk_dialog_new_with_buttons(_("Save Session"),
1350             GTK_WINDOW(win->gtkwin),
1351             GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1352             _("_Cancel"), GTK_RESPONSE_CANCEL,
1353             _("_Save"), GTK_RESPONSE_OK,
1354             NULL);
1355     GtkDialog *dialog = GTK_DIALOG(dialog_w);
1356     GtkWidget *title_w = gtk_entry_new();
1357     GtkEntry *title_e = GTK_ENTRY(title_w);
1358     GtkWidget *img = gtk_image_new_from_icon_name("dialog-information",
1359             GTK_ICON_SIZE_DIALOG);
1360     GtkWidget *tip_label = gtk_label_new(_("The current session will be saved "
1361                 "in $XDG_CONFIG_HOME/roxterm.sourceforge.net/UserSessions/ "
1362                 "with the given leafname (XDG_CONFIG_HOME defaults to "
1363                 "~/.config). A session saves the state of current windows "
1364                 "and tabs but not the terminals' text content. A session "
1365                 "can be restored with roxterm's --session command-line "
1366                 "option, or will be restored automatically if named "
1367                 "'Default'. Leaving the field blank is equivalent to "
1368                 "'Default'."));
1369     int response;
1370     const char *name;
1371     char *filename;
1372     GtkWidget *content_area = gtk_dialog_get_content_area(dialog);
1373     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1374 
1375     gtk_dialog_set_default_response(dialog, GTK_RESPONSE_APPLY);
1376     gtk_box_pack_start(GTK_BOX(content_area), title_w, FALSE, FALSE, 8);
1377     gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 8);
1378     gtk_label_set_line_wrap(GTK_LABEL(tip_label), TRUE);
1379     gtk_label_set_width_chars(GTK_LABEL(tip_label), 30);
1380     gtk_label_set_max_width_chars(GTK_LABEL(tip_label), 50);
1381     gtk_box_pack_start(GTK_BOX(hbox), tip_label, FALSE, FALSE, 8);
1382     gtk_box_pack_start(GTK_BOX(content_area), hbox, FALSE, FALSE, 8);
1383     gtk_entry_set_activates_default(title_e, TRUE);
1384     gtk_entry_set_text(title_e, "Default");
1385     gtk_widget_show_all(dialog_w);
1386     response = gtk_dialog_run(dialog);
1387     switch (response)
1388     {
1389         case GTK_RESPONSE_DELETE_EVENT:
1390         case GTK_RESPONSE_NONE:
1391             /* Don't destroy */
1392             break;
1393         case GTK_RESPONSE_OK:
1394             name = gtk_entry_get_text(title_e);
1395             if (!name || !name[0])
1396                 name = "Default";
1397             filename = session_get_filename(name, "UserSessions", TRUE);
1398             if (!save_session_to_file(filename, name))
1399             {
1400                 dlg_critical(GTK_WINDOW(win->gtkwin),
1401                         _("Unable to save session to file '%s': %s"),
1402                         filename, strerror(errno));
1403             }
1404             /* Fall through to destroy */
1405         default:
1406             gtk_widget_destroy(dialog_w);
1407             break;
1408     }
1409 }
1410 
multi_win_set_window_title_action(MultiWin * win)1411 static void multi_win_set_window_title_action(MultiWin * win)
1412 {
1413     MultiTab *tab = win->current_tab;
1414     g_return_if_fail(tab);
1415     GtkWidget *dialog_w = gtk_dialog_new_with_buttons(_("Set Window Title"),
1416             GTK_WINDOW(win->gtkwin),
1417             GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1418             _("_Cancel"), GTK_RESPONSE_CANCEL,
1419             _("_Apply"), GTK_RESPONSE_APPLY,
1420             NULL);
1421     GtkDialog *dialog = GTK_DIALOG(dialog_w);
1422     GtkWidget *title_w = gtk_entry_new();
1423     GtkEntry *title_e = GTK_ENTRY(title_w);
1424     GtkWidget *img = gtk_image_new_from_icon_name("dialog-information",
1425             GTK_ICON_SIZE_DIALOG);
1426     GtkWidget *tip_label = gtk_label_new(_("The title string may include '%s' "
1427            "which is substituted with the active tab's name/window title, "
1428            "'%t' for the active tab's number, '%n' for the number of tabs "
1429            "and or '%%' for a literal '%'. Other '%' sequences are passed "
1430            "through. Apply an empty string here to use the profile's title "
1431            "string."));
1432     int response;
1433     const char *title;
1434     GtkWidget *content_area = gtk_dialog_get_content_area(dialog);
1435     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
1436 
1437     gtk_dialog_set_default_response(dialog, GTK_RESPONSE_APPLY);
1438     gtk_box_pack_start(GTK_BOX(content_area), title_w, FALSE, FALSE, 8);
1439     gtk_box_pack_start(GTK_BOX(hbox), img, FALSE, FALSE, 8);
1440     gtk_label_set_line_wrap(GTK_LABEL(tip_label), TRUE);
1441     gtk_label_set_width_chars(GTK_LABEL(tip_label), 30);
1442     gtk_label_set_max_width_chars(GTK_LABEL(tip_label), 50);
1443     gtk_box_pack_start(GTK_BOX(hbox), tip_label, FALSE, FALSE, 8);
1444     gtk_box_pack_start(GTK_BOX(content_area), hbox, FALSE, FALSE, 8);
1445     gtk_entry_set_activates_default(title_e, TRUE);
1446     gtk_entry_set_text(title_e, win->title_template ? win->title_template : "");
1447     gtk_widget_show_all(dialog_w);
1448     response = gtk_dialog_run(dialog);
1449     switch (response)
1450     {
1451         case GTK_RESPONSE_DELETE_EVENT:
1452         case GTK_RESPONSE_NONE:
1453             /* Don't destroy */
1454             break;
1455         case GTK_RESPONSE_APPLY:
1456             title = gtk_entry_get_text(title_e);
1457             if (title && !title[0])
1458                 title = NULL;
1459             win->title_template_locked = FALSE;
1460             multi_win_set_title_template(win, title);
1461             win->title_template_locked = (title != NULL);
1462             /* Fall through to destroy */
1463         default:
1464             gtk_widget_destroy(dialog_w);
1465             break;
1466     }
1467 }
1468 
multi_win_next_tab_action(MultiWin * win)1469 static void multi_win_next_tab_action(MultiWin * win)
1470 {
1471     if (win->wrap_switch_tab && multi_win_at_last_tab(win))
1472         gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
1473     else
1474         gtk_notebook_next_page(GTK_NOTEBOOK(win->notebook));
1475 }
1476 
multi_win_previous_tab_action(MultiWin * win)1477 static void multi_win_previous_tab_action(MultiWin * win)
1478 {
1479     if (win->wrap_switch_tab && multi_win_at_first_tab(win))
1480     {
1481         gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook),
1482                 win->ntabs - 1);
1483     }
1484     else
1485     {
1486         gtk_notebook_prev_page(GTK_NOTEBOOK(win->notebook));
1487     }
1488 }
1489 
multi_win_select_tab_action(GtkCheckMenuItem * widget,MultiTab * tab)1490 static void multi_win_select_tab_action(GtkCheckMenuItem *widget,
1491         MultiTab * tab)
1492 {
1493     MultiWin *win = tab->parent;
1494 
1495     if (win->ignore_toggles)
1496         return;
1497     if (gtk_check_menu_item_get_active(widget))
1498         multi_win_select_tab(win, tab);
1499 }
1500 
1501 static void
multi_win_toggle_show_tab_bar_action(GtkCheckMenuItem * item,MultiWin * win)1502 multi_win_toggle_show_tab_bar_action(GtkCheckMenuItem * item, MultiWin * win)
1503 {
1504     gboolean show = gtk_check_menu_item_get_active(item);
1505 
1506     if (show != win->always_show_tabs)
1507         multi_win_set_always_show_tabs(win, show);
1508 }
1509 
1510 static void
multi_win_toggle_show_menubar_action(GtkCheckMenuItem * item,MultiWin * win)1511 multi_win_toggle_show_menubar_action(GtkCheckMenuItem * item, MultiWin * win)
1512 {
1513     gboolean show = gtk_check_menu_item_get_active(item);
1514 
1515     if (show != win->show_menu_bar)
1516         multi_win_set_show_menu_bar(win, show);
1517 }
1518 
1519 static void
multi_win_toggle_fullscreen_action(GtkCheckMenuItem * item,MultiWin * win)1520 multi_win_toggle_fullscreen_action(GtkCheckMenuItem * item, MultiWin * win)
1521 {
1522     gboolean fs = gtk_check_menu_item_get_active(item);
1523 
1524     if (fs != win->fullscreen)
1525         multi_win_set_fullscreen(win, fs);
1526 }
1527 
1528 static void
multi_win_toggle_borderless_action(GtkCheckMenuItem * item,MultiWin * win)1529 multi_win_toggle_borderless_action(GtkCheckMenuItem * item, MultiWin * win)
1530 {
1531     gboolean fs = gtk_check_menu_item_get_active(item);
1532 
1533     if (fs != win->borderless)
1534         multi_win_set_borderless(win, fs);
1535 }
1536 
multi_win_zoom_changed(MultiWin * win)1537 static void multi_win_zoom_changed(MultiWin *win)
1538 {
1539     double zf = multi_win_zoom_factors[win->zoom_index];
1540     GList *link;
1541 
1542     if (!multi_win_zoom_handler)
1543         return;
1544     for (link = win->tabs; link; link = g_list_next(link))
1545     {
1546         multi_win_zoom_handler(((MultiTab *) link->data)->user_data, zf,
1547                 win->zoom_index);
1548     }
1549 }
1550 
multi_win_zoom_in_action(MultiWin * win)1551 static void multi_win_zoom_in_action(MultiWin *win)
1552 {
1553     if (win->zoom_index < MULTI_WIN_N_ZOOM_FACTORS - 1)
1554     {
1555         ++win->zoom_index;
1556         multi_win_zoom_changed(win);
1557     }
1558 }
1559 
multi_win_zoom_out_action(MultiWin * win)1560 static void multi_win_zoom_out_action(MultiWin *win)
1561 {
1562     if (win->zoom_index > 0)
1563     {
1564         --win->zoom_index;
1565         multi_win_zoom_changed(win);
1566     }
1567 }
1568 
multi_win_zoom_norm_action(MultiWin * win)1569 static void multi_win_zoom_norm_action(MultiWin *win)
1570 {
1571     if (win->zoom_index != MULTI_WIN_NORMAL_ZOOM_INDEX)
1572     {
1573         win->zoom_index = MULTI_WIN_NORMAL_ZOOM_INDEX;
1574         multi_win_zoom_changed(win);
1575     }
1576 }
1577 
1578 typedef enum {
1579     MULTI_TAB_SCROLL_STEP_LINE,
1580     MULTI_TAB_SCROLL_STEP_PAGE,
1581     MULTI_TAB_SCROLL_STEP_END
1582 } MultiTabScrollStep;
1583 
multi_win_vscroll_by(MultiWin * win,double multiplier,MultiTabScrollStep step_type)1584 static void multi_win_vscroll_by(MultiWin *win, double multiplier,
1585         MultiTabScrollStep step_type)
1586 {
1587     GtkAdjustment *adj;
1588     double newval;
1589     double bottom;
1590 
1591     adj = win->current_tab->adjustment;
1592     if (!adj)
1593         return;
1594     bottom = gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
1595     if (step_type == MULTI_TAB_SCROLL_STEP_END)
1596     {
1597         if (multiplier < 0)
1598             newval = 0;
1599         else
1600             newval = bottom;
1601     }
1602     else
1603     {
1604         double step;
1605 
1606         if (step_type == MULTI_TAB_SCROLL_STEP_PAGE)
1607             step = gtk_adjustment_get_page_increment(adj);
1608         else
1609             step = gtk_adjustment_get_step_increment(adj);
1610         newval = gtk_adjustment_get_value(adj) + multiplier * step;
1611         newval = CLAMP(newval, 0, bottom);
1612     }
1613     gtk_adjustment_set_value(adj, newval);
1614 }
1615 
multi_win_scroll_up_action(MultiWin * win)1616 static void multi_win_scroll_up_action(MultiWin *win)
1617 {
1618     g_return_if_fail(win->current_tab != NULL);
1619     multi_win_vscroll_by(win, -1, MULTI_TAB_SCROLL_STEP_LINE);
1620 }
1621 
multi_win_scroll_down_action(MultiWin * win)1622 static void multi_win_scroll_down_action(MultiWin *win)
1623 {
1624     g_return_if_fail(win->current_tab != NULL);
1625     multi_win_vscroll_by(win, 1, MULTI_TAB_SCROLL_STEP_LINE);
1626 }
1627 
multi_win_scroll_page_up_action(MultiWin * win)1628 static void multi_win_scroll_page_up_action(MultiWin *win)
1629 {
1630     g_return_if_fail(win->current_tab != NULL);
1631     multi_win_vscroll_by(win, -1, MULTI_TAB_SCROLL_STEP_PAGE);
1632 }
1633 
multi_win_scroll_page_down_action(MultiWin * win)1634 static void multi_win_scroll_page_down_action(MultiWin *win)
1635 {
1636     g_return_if_fail(win->current_tab != NULL);
1637     multi_win_vscroll_by(win, 1, MULTI_TAB_SCROLL_STEP_PAGE);
1638 }
1639 
multi_win_scroll_half_page_up_action(MultiWin * win)1640 static void multi_win_scroll_half_page_up_action(MultiWin *win)
1641 {
1642     g_return_if_fail(win->current_tab != NULL);
1643     multi_win_vscroll_by(win, -0.5, MULTI_TAB_SCROLL_STEP_PAGE);
1644 }
1645 
multi_win_scroll_half_page_down_action(MultiWin * win)1646 static void multi_win_scroll_half_page_down_action(MultiWin *win)
1647 {
1648     g_return_if_fail(win->current_tab != NULL);
1649     multi_win_vscroll_by(win, 0.5, MULTI_TAB_SCROLL_STEP_PAGE);
1650 }
1651 
multi_win_scroll_to_top_action(MultiWin * win)1652 static void multi_win_scroll_to_top_action(MultiWin *win)
1653 {
1654     g_return_if_fail(win->current_tab != NULL);
1655     multi_win_vscroll_by(win, -1, MULTI_TAB_SCROLL_STEP_END);
1656 }
1657 
multi_win_scroll_to_bottom_action(MultiWin * win)1658 static void multi_win_scroll_to_bottom_action(MultiWin *win)
1659 {
1660     g_return_if_fail(win->current_tab != NULL);
1661     multi_win_vscroll_by(win, 1, MULTI_TAB_SCROLL_STEP_END);
1662 }
1663 
multi_win_move_tab_by_one(MultiWin * win,int dir)1664 static void multi_win_move_tab_by_one(MultiWin *win, int dir)
1665 {
1666     MultiTab *tab = win->current_tab;
1667     int pos;
1668 
1669     g_return_if_fail(win->current_tab);
1670     pos = multi_tab_get_page_num(tab) + dir;
1671     if (win->wrap_switch_tab)
1672     {
1673         if (pos < 0)
1674             pos = win->ntabs - 1;
1675         else if (pos >= (int) win->ntabs)
1676             pos = 0;
1677     }
1678     g_return_if_fail(pos >= 0 && pos < (int) win->ntabs);
1679     multi_tab_move_to_position(tab, pos, TRUE);
1680 }
1681 
multi_win_move_tab_left_action(MultiWin * win)1682 static void multi_win_move_tab_left_action(MultiWin *win)
1683 {
1684     multi_win_move_tab_by_one(win, -1);
1685 }
1686 
multi_win_move_tab_right_action(MultiWin * win)1687 static void multi_win_move_tab_right_action(MultiWin *win)
1688 {
1689     multi_win_move_tab_by_one(win, 1);
1690 }
1691 
multi_win_close_window_action(MultiWin * win)1692 static void multi_win_close_window_action(MultiWin *win)
1693 {
1694     if (!multi_win_delete_handler ||
1695             !multi_win_delete_handler(win->gtkwin, (GdkEvent *) win, win))
1696     {
1697         multi_win_destructor(win, TRUE);
1698     }
1699 }
1700 
multi_win_popup_new_term_with_profile(GtkMenu * menu)1701 static void multi_win_popup_new_term_with_profile(GtkMenu *menu)
1702 {
1703     gtk_menu_popup_at_pointer(menu, NULL);
1704 }
1705 
multi_win_connect_actions(MultiWin * win)1706 static void multi_win_connect_actions(MultiWin * win)
1707 {
1708     multi_win_menu_connect_swapped(win, MENUTREE_FILE_NEW_WINDOW, G_CALLBACK
1709         (multi_win_new_window_action), win, NULL, NULL, NULL);
1710     multi_win_menu_connect_swapped(win, MENUTREE_FILE_CLOSE_WINDOW, G_CALLBACK
1711         (multi_win_close_window_action), win, NULL, NULL, NULL);
1712     multi_win_menu_connect_swapped(win, MENUTREE_FILE_NEW_TAB, G_CALLBACK
1713         (multi_win_new_tab_action), win, NULL, NULL, NULL);
1714     multi_win_menu_connect_swapped(win, MENUTREE_FILE_CLOSE_TAB, G_CALLBACK
1715         (multi_win_close_tab_action), win, NULL, NULL, NULL);
1716     multi_win_menu_connect_swapped(win, MENUTREE_FILE_SAVE_SESSION, G_CALLBACK
1717         (multi_win_save_session_action), win, NULL, NULL, NULL);
1718     multi_win_menu_connect_swapped(win, MENUTREE_TABS_DETACH_TAB, G_CALLBACK
1719         (multi_win_detach_tab_action), win, NULL, NULL, NULL);
1720     menutree_signal_connect_swapped(win->popup_menu,
1721             MENUTREE_FILE_NEW_WINDOW_WITH_PROFILE_HEADER,
1722             G_CALLBACK(multi_win_popup_new_term_with_profile),
1723             win->popup_menu->new_win_profiles_menu);
1724     menutree_signal_connect_swapped(win->popup_menu,
1725             MENUTREE_FILE_NEW_TAB_WITH_PROFILE_HEADER,
1726             G_CALLBACK(multi_win_popup_new_term_with_profile),
1727             win->popup_menu->new_tab_profiles_menu);
1728     multi_win_menu_connect_swapped(win, MENUTREE_TABS_PREVIOUS_TAB, G_CALLBACK
1729         (multi_win_previous_tab_action), win, NULL, NULL, NULL);
1730     multi_win_menu_connect_swapped(win, MENUTREE_TABS_NEXT_TAB, G_CALLBACK
1731         (multi_win_next_tab_action), win, NULL, NULL, NULL);
1732     multi_win_menu_connect_swapped(win, MENUTREE_TABS_MOVE_TAB_LEFT, G_CALLBACK
1733         (multi_win_move_tab_left_action), win, NULL, NULL, NULL);
1734     multi_win_menu_connect_swapped(win, MENUTREE_TABS_MOVE_TAB_RIGHT, G_CALLBACK
1735         (multi_win_move_tab_right_action), win, NULL, NULL, NULL);
1736     multi_win_menu_connect_swapped(win, MENUTREE_TABS_NAME_TAB, G_CALLBACK
1737         (multi_win_name_tab_action), win, NULL, NULL, NULL);
1738     multi_win_menu_connect_swapped(win, MENUTREE_TABS_CLOSE_TAB, G_CALLBACK
1739         (multi_win_close_tab_action), win, NULL, NULL, NULL);
1740     multi_win_menu_connect_swapped(win, MENUTREE_TABS_CLOSE_OTHER_TABS,
1741         G_CALLBACK(multi_win_close_other_tabs_action), win, NULL, NULL, NULL);
1742     multi_win_menu_connect_swapped(win, MENUTREE_EDIT_SET_WINDOW_TITLE,
1743         G_CALLBACK(multi_win_set_window_title_action), win, NULL, NULL, NULL);
1744     multi_win_menu_connect(win, MENUTREE_VIEW_SHOW_TAB_BAR, G_CALLBACK
1745         (multi_win_toggle_show_tab_bar_action), win, NULL, NULL, NULL);
1746     multi_win_menu_connect(win, MENUTREE_VIEW_SHOW_MENUBAR, G_CALLBACK
1747         (multi_win_toggle_show_menubar_action), win, NULL, NULL, NULL);
1748     multi_win_menu_connect(win, MENUTREE_VIEW_FULLSCREEN, G_CALLBACK
1749         (multi_win_toggle_fullscreen_action), win, NULL, NULL, NULL);
1750     multi_win_menu_connect(win, MENUTREE_VIEW_BORDERLESS, G_CALLBACK
1751         (multi_win_toggle_borderless_action), win, NULL, NULL, NULL);
1752     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_ZOOM_IN, G_CALLBACK
1753         (multi_win_zoom_in_action), win, NULL, NULL, NULL);
1754     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_ZOOM_OUT, G_CALLBACK
1755         (multi_win_zoom_out_action), win, NULL, NULL, NULL);
1756     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_ZOOM_NORM, G_CALLBACK
1757         (multi_win_zoom_norm_action), win, NULL, NULL, NULL);
1758     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_UP,
1759         G_CALLBACK(multi_win_scroll_up_action), win, NULL, NULL, NULL);
1760     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_DOWN,
1761         G_CALLBACK(multi_win_scroll_down_action), win, NULL, NULL, NULL);
1762     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_PAGE_UP,
1763         G_CALLBACK(multi_win_scroll_page_up_action), win, NULL, NULL, NULL);
1764     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_PAGE_DOWN,
1765         G_CALLBACK(multi_win_scroll_page_down_action), win, NULL, NULL, NULL);
1766     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_HALF_PAGE_UP,
1767         G_CALLBACK(multi_win_scroll_half_page_up_action), win,
1768         NULL, NULL, NULL);
1769     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_HALF_PAGE_DOWN,
1770         G_CALLBACK(multi_win_scroll_half_page_down_action), win,
1771         NULL, NULL, NULL);
1772     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_TO_TOP,
1773         G_CALLBACK(multi_win_scroll_to_top_action), win, NULL, NULL, NULL);
1774     multi_win_menu_connect_swapped(win, MENUTREE_VIEW_SCROLL_TO_BOTTOM,
1775         G_CALLBACK(multi_win_scroll_to_bottom_action), win, NULL, NULL, NULL);
1776 }
1777 
multi_win_set_show_tabs_menu_items(MultiWin * win,gboolean active)1778 static void multi_win_set_show_tabs_menu_items(MultiWin *win, gboolean active)
1779 {
1780     menutree_set_show_tab_bar_active(win->menu_bar, active);
1781     menutree_set_show_tab_bar_active(win->popup_menu, active);
1782 }
1783 
multi_win_show_tabs(MultiWin * win)1784 static void multi_win_show_tabs(MultiWin * win)
1785 {
1786     if ((int) win->tab_pos != -1)
1787     {
1788         gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), TRUE);
1789         gtk_notebook_set_show_border(GTK_NOTEBOOK(win->notebook), TRUE);
1790     }
1791 }
1792 
multi_win_hide_tabs(MultiWin * win)1793 static void multi_win_hide_tabs(MultiWin * win)
1794 {
1795     gtk_notebook_set_show_tabs(GTK_NOTEBOOK(win->notebook), FALSE);
1796     gtk_notebook_set_show_border(GTK_NOTEBOOK(win->notebook), FALSE);
1797 }
1798 
multi_win_show(MultiWin * win)1799 void multi_win_show(MultiWin *win)
1800 {
1801     gtk_widget_show(win->notebook);
1802     gtk_widget_show(win->vbox);
1803     gtk_widget_show(win->gtkwin);
1804     g_object_set_data(G_OBJECT(gtk_widget_get_window(win->gtkwin)),
1805             "ROXTermWin", win);
1806 
1807     //g_debug("call roxterm_force_resize_now from line %d", __LINE__);
1808     //roxterm_force_resize_now(win);
1809 }
1810 
multi_win_set_colormap(MultiWin * win)1811 void multi_win_set_colormap(MultiWin *win)
1812 {
1813     GdkScreen *screen = gtk_widget_get_screen(win->gtkwin);
1814     GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
1815 
1816     if (gdk_screen_is_composited(screen) && visual)
1817     {
1818         gtk_widget_set_visual(win->gtkwin, visual);
1819         win->composite = TRUE;
1820     }
1821     else
1822     {
1823         gtk_widget_set_visual(win->gtkwin,
1824                 gdk_screen_get_system_visual(screen));
1825         win->composite = FALSE;
1826     }
1827 }
1828 
1829 /* Close window explicitly instead of allowing system to close it by returning
1830  * FALSE from delete-event handler. This cures
1831  * https://sourceforge.net/p/roxterm/bugs/89/
1832  * but I don't really know why.
1833  */
multi_win_delete_event_cb(GtkWidget * widget,GdkEvent * event,MultiWin * win)1834 static gboolean multi_win_delete_event_cb(GtkWidget *widget, GdkEvent *event,
1835         MultiWin *win)
1836 {
1837     if (!multi_win_delete_handler(widget, event, win))
1838     {
1839         multi_win_destructor(win, TRUE);
1840     }
1841     return TRUE;
1842 }
1843 
multi_win_size_allocate(GtkWidget * widget,GdkRectangle * alloc,MultiWin * win)1844 static void multi_win_size_allocate(GtkWidget *widget, GdkRectangle *alloc,
1845         MultiWin *win)
1846 {
1847     (void) widget;
1848     (void) alloc;
1849     if (!win->has_geometry)
1850     {
1851         MultiTab *tab = win->current_tab;
1852         if (!tab)
1853         {
1854             g_warning("Size allocated to window with no current tab");
1855             return;
1856         }
1857         int columns, rows, width, height;
1858         (*multi_win_size_func)(tab->user_data,
1859                 FALSE, &columns, &rows);
1860         multi_win_process_geometry(win, tab, columns, rows, &width, &height);
1861     }
1862 }
1863 
multi_win_new_blank(Options * shortcuts,int zoom_index,gboolean disable_menu_shortcuts,gboolean disable_tab_shortcuts,GtkPositionType tab_pos,gboolean always_show_tabs,gboolean add_tab_button)1864 MultiWin *multi_win_new_blank(Options *shortcuts,
1865         int zoom_index,
1866         gboolean disable_menu_shortcuts, gboolean disable_tab_shortcuts,
1867         GtkPositionType tab_pos, gboolean always_show_tabs,
1868         gboolean add_tab_button)
1869 {
1870     MultiWin *win = g_new0(MultiWin, 1);
1871     GtkNotebook *notebook;
1872     char *role = NULL;
1873 
1874     win->best_tab_width = G_MAXINT;
1875     win->tab_pos = tab_pos;
1876     win->always_show_tabs = always_show_tabs;
1877     win->scroll_bar_pos = MultiWinScrollBar_Query;
1878     if (zoom_index < 0)
1879         win->zoom_index = MULTI_WIN_NORMAL_ZOOM_INDEX;
1880     else if (zoom_index >= MULTI_WIN_N_ZOOM_FACTORS)
1881         win->zoom_index = multi_win_zoom_factors[MULTI_WIN_N_ZOOM_FACTORS - 1];
1882     else
1883         win->zoom_index = zoom_index;
1884 
1885     win->gtkwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1886 
1887     role = global_options_lookup_string("role");
1888     if (role)
1889     {
1890         global_options_reset_string("role");
1891     }
1892     else if (multi_win_role_prefix)
1893     {
1894         role = g_strdup_printf("%s-%d-%x-%d", multi_win_role_prefix,
1895                 getpid(), g_random_int(), ++multi_win_role_index);
1896     }
1897     if (role)
1898     {
1899         gtk_window_set_role(GTK_WINDOW(win->gtkwin), role);
1900         g_free(role);
1901     }
1902 
1903     g_signal_connect(win->gtkwin, "realize",
1904             G_CALLBACK(multi_win_realize_handler), win);
1905     win->destroy_handler = g_signal_connect(win->gtkwin, "destroy",
1906             G_CALLBACK(multi_win_destroy_handler), win);
1907     if (multi_win_delete_handler)
1908     {
1909         g_signal_connect(win->gtkwin, "delete-event",
1910                 G_CALLBACK(multi_win_delete_event_cb), win);
1911     }
1912     g_signal_connect(win->gtkwin, "draw",
1913             G_CALLBACK(multi_win_draw_chrome_bg), win);
1914     g_signal_connect(win->gtkwin, "size-allocate",
1915             G_CALLBACK(multi_win_size_allocate), win);
1916 
1917     multi_win_set_colormap(win);
1918 
1919     /* This on its own seems to allow transparency to work correctly in all of
1920      * Classic, Xorg and Wayland GNOME variants. The "draw" signal handler
1921      * broke the shadows outside the window in Wayland.
1922      */
1923     gtk_widget_set_app_paintable(win->gtkwin, TRUE);
1924 
1925     win->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1926     gtk_container_add(GTK_CONTAINER(win->gtkwin), win->vbox);
1927 
1928     options_ref(shortcuts);
1929     win->shortcuts = shortcuts;
1930     win->accel_group = gtk_accel_group_new();
1931     gtk_window_add_accel_group(GTK_WINDOW(win->gtkwin), win->accel_group);
1932 
1933     win->popup_menu = menutree_new(shortcuts, win->accel_group,
1934         GTK_TYPE_MENU, TRUE, disable_tab_shortcuts, win);
1935     //g_debug("Created popup menu %p", win->popup_menu);
1936     menutree_connect_destroyed(win->popup_menu,
1937         G_CALLBACK(multi_win_menutree_deleted_handler), win);
1938 
1939     win->short_popup = menutree_new_short_popup(shortcuts, win->accel_group,
1940         TRUE, win);
1941     menutree_connect_destroyed(win->short_popup,
1942         G_CALLBACK(multi_win_menutree_deleted_handler), win);
1943     //g_debug("Created short popup menu %p", win->short_popup);
1944 
1945     win->menu_bar = menutree_new(shortcuts, win->accel_group,
1946         GTK_TYPE_MENU_BAR, disable_menu_shortcuts, disable_tab_shortcuts,
1947         win);
1948     //g_debug("Created menu bar %p", win->menu_bar);
1949     /* Make sure menu widgets aren't destroyed when removed from window */
1950     g_object_ref(menutree_get_top_level_widget(win->menu_bar));
1951     menutree_connect_destroyed(win->menu_bar,
1952         G_CALLBACK(multi_win_menutree_deleted_handler), win);
1953     win->show_menu_bar = FALSE;
1954 
1955     if (win->tab_pos == GTK_POS_LEFT || win->tab_pos == GTK_POS_RIGHT)
1956     {
1957         menutree_change_move_tab_labels(win->popup_menu);
1958         menutree_change_move_tab_labels(win->menu_bar);
1959     }
1960 
1961     multi_win_connect_actions(win);
1962     (*multi_win_menu_signal_connector) (win);
1963     g_signal_connect(win->gtkwin, "window-state-event",
1964         G_CALLBACK(multi_win_state_event_handler), win);
1965 
1966     win->notebook = gtk_notebook_new();
1967     notebook = GTK_NOTEBOOK(win->notebook);
1968     g_signal_connect(notebook, "create-window",
1969             G_CALLBACK(multi_win_notebook_creation_hook), win);
1970     gtk_notebook_set_scrollable(notebook, TRUE);
1971     if ((int) tab_pos == -1)
1972     {
1973         gtk_notebook_set_show_tabs(notebook, always_show_tabs);
1974         gtk_notebook_set_show_border(notebook, always_show_tabs);
1975     }
1976     else
1977     {
1978         gtk_notebook_set_tab_pos(notebook, tab_pos);
1979     }
1980     gtk_notebook_popup_disable(notebook);
1981     if (always_show_tabs)
1982     {
1983         multi_win_set_show_tabs_menu_items(win, TRUE);
1984         multi_win_show_tabs(win);
1985     }
1986     else
1987     {
1988         multi_win_set_show_tabs_menu_items(win, FALSE);
1989         multi_win_hide_tabs(win);
1990     }
1991     win->add_tab_button = multitab_close_button_new("tab-new-symbolic");
1992     g_signal_connect_swapped(win->add_tab_button, "clicked",
1993             G_CALLBACK(multi_win_new_tab_action), win);
1994     gtk_notebook_set_action_widget(notebook, win->add_tab_button, GTK_PACK_END);
1995     if (add_tab_button)
1996         gtk_widget_show_all(win->add_tab_button);
1997     win->show_add_tab_button = add_tab_button;
1998     gtk_box_pack_start(GTK_BOX(win->vbox), win->notebook, TRUE, TRUE, 0);
1999     g_signal_connect(win->notebook, "switch-page",
2000         G_CALLBACK(multi_win_page_switched), win);
2001     g_signal_connect(win->gtkwin, "focus-in-event",
2002         G_CALLBACK(multi_win_focus_in), win);
2003     gtk_notebook_set_group_name(notebook, "ROXTerm");
2004     g_signal_connect(win->notebook, "page-reordered",
2005         G_CALLBACK(page_reordered_callback), win);
2006     g_signal_connect(win->notebook, "page-added",
2007         G_CALLBACK(page_added_callback), win);
2008     /* VTE widgets handle these events, so we only get them if they occur over
2009      * the actual sticky out bit */
2010 
2011     multi_win_all = g_list_append(multi_win_all, win);
2012 
2013     return win;
2014 }
2015 
multi_win_set_role_prefix(const char * role_prefix)2016 void multi_win_set_role_prefix(const char *role_prefix)
2017 {
2018     g_free(multi_win_role_prefix);
2019     multi_win_role_prefix = g_strdup(role_prefix);
2020 }
2021 
multi_win_new_full(Options * shortcuts,int zoom_index,gpointer user_data_template,const char * geom,MultiWinSizing sizing,GtkPositionType tab_pos,gboolean borderless,gboolean always_show_tabs,gboolean add_tab_button)2022 MultiWin *multi_win_new_full(Options *shortcuts,
2023         int zoom_index, gpointer user_data_template, const char *geom,
2024         MultiWinSizing sizing, GtkPositionType tab_pos, gboolean borderless,
2025         gboolean always_show_tabs, gboolean add_tab_button)
2026 {
2027     gboolean disable_menu_shortcuts, disable_tab_shortcuts;
2028     MultiWin *win;
2029     MultiTab *tab;
2030 
2031     multi_win_get_disable_menu_shortcuts(user_data_template,
2032             &disable_menu_shortcuts, &disable_tab_shortcuts);
2033     win = multi_win_new_blank(shortcuts, zoom_index,
2034             disable_menu_shortcuts, disable_tab_shortcuts,
2035             tab_pos, always_show_tabs, add_tab_button);
2036     win->user_data_template = user_data_template;
2037     win->tab_pos = tab_pos;
2038     tab = multi_tab_new_defer_connect(win, user_data_template);
2039 
2040     multi_win_shade_menus_for_tabs(win);
2041     multi_win_set_borderless(win, borderless);
2042 
2043     /* Showing everything except top-level, then realizing top-level, seems to
2044      * be the key to getting some sensible size allocations so we can work out
2045      * how much size the "chrome" needs.
2046      */
2047     gtk_widget_show_all(win->vbox);
2048     gtk_widget_realize(win->gtkwin);
2049     if (geom)
2050     {
2051         multi_win_set_initial_geometry(win, geom, tab);
2052     }
2053     if (sizing == MULTI_WIN_FULL_SCREEN)
2054     {
2055         multi_win_set_fullscreen(win, TRUE);
2056     }
2057     else if (sizing == MULTI_WIN_MAXIMISED)
2058     {
2059         gtk_window_maximize(GTK_WINDOW(win->gtkwin));
2060     }
2061     multi_win_show(win);
2062 
2063     /* Need to do this after multi_tab_new's initial call of
2064      * multi_win_select_tab to ensure child widgets are realized
2065      * and tab selection handler is activated */
2066     gtk_notebook_set_current_page(GTK_NOTEBOOK(win->notebook), 0);
2067     tab = win->tabs->data;
2068     win->tab_selection_handler(tab->user_data, tab);
2069     multi_tab_connect_misc_signals(tab->user_data);
2070     return win;
2071 }
2072 
multi_win_destructor(MultiWin * win,gboolean destroy_widgets)2073 static void multi_win_destructor(MultiWin *win, gboolean destroy_widgets)
2074 {
2075     GList *link;
2076 
2077     g_return_if_fail(win);
2078 
2079     win->ignore_tab_selections = TRUE;
2080     if (win->accel_group)
2081     {
2082         UNREF_LOG(g_object_unref(win->accel_group));
2083         win->accel_group = NULL;
2084     }
2085     if (destroy_widgets && win->gtkwin)
2086     {
2087         g_signal_handler_disconnect(win->gtkwin, win->destroy_handler);
2088     }
2089 
2090     if (!destroy_widgets)
2091     {
2092         win->gtkwin = NULL;
2093     }
2094     for (link = win->tabs; link; link = g_list_next(link))
2095     {
2096         multi_tab_delete_without_notifying_parent(link->data, destroy_widgets);
2097     }
2098     if (win->menu_bar)
2099     {
2100         /* Remember we added an extra reference to this when we created window,
2101          * so it will now be removed from window but not destroyed. Have to
2102          * destroy it explicitly.
2103          */
2104         UNREF_LOG(menutree_delete(win->menu_bar));
2105         win->menu_bar = NULL;
2106     }
2107     if (win->popup_menu)
2108     {
2109         UNREF_LOG(menutree_delete(win->popup_menu));
2110         win->popup_menu = NULL;
2111     }
2112     if (win->short_popup)
2113     {
2114         UNREF_LOG(menutree_delete(win->short_popup));
2115         win->short_popup = NULL;
2116     }
2117     if (destroy_widgets && win->gtkwin)
2118     {
2119         gtk_widget_destroy(win->gtkwin);
2120         win->gtkwin = NULL;
2121     }
2122     UNREF_LOG(options_unref(win->shortcuts));
2123     g_free(win->title_template);
2124     g_free(win->child_title);
2125     g_free(win);
2126     multi_win_all = g_list_remove(multi_win_all, win);
2127     if (!multi_win_all)
2128     {
2129         gtk_main_quit();
2130     }
2131 }
2132 
multi_win_delete(MultiWin * win)2133 void multi_win_delete(MultiWin *win)
2134 {
2135     multi_win_destructor(win, TRUE);
2136 }
2137 
2138 /* Returns TRUE if window destroyed */
multi_win_notify_tab_removed(MultiWin * win,MultiTab * tab)2139 static gboolean multi_win_notify_tab_removed(MultiWin * win, MultiTab * tab)
2140 {
2141     GList *link = g_list_find(win->tabs, tab);
2142 
2143     g_return_val_if_fail(link, FALSE);
2144     /* GtkNotebook event will have dealt with new tab selection for us but we
2145      * need to ensure we don't respond to spurious events from deleting menu
2146      * items */
2147     if (win->current_tab == tab)
2148     {
2149         win->current_tab = NULL;
2150     }
2151     /*win->ignore_tab_selections = TRUE;*/
2152     win->tabs = g_list_delete_link(win->tabs, link);
2153     /*win->ignore_tab_selections = FALSE;*/
2154     if (!--win->ntabs)
2155     {
2156         multi_win_delete(win);
2157         return TRUE;
2158     }
2159     else
2160     {
2161         renumber_tabs(win);
2162         if (win->ntabs == 1)
2163         {
2164             tab = win->tabs->data;
2165             if (win->tab_pos == GTK_POS_TOP || win->tab_pos == GTK_POS_BOTTOM)
2166                 multi_win_pack_for_single_tab(win);
2167             if (!win->always_show_tabs)
2168             {
2169                 multi_win_request_size_restore(win);
2170                 multi_win_hide_tabs(win);
2171             }
2172         }
2173     }
2174     multi_win_shade_menus_for_tabs(win);
2175     return FALSE;
2176 }
2177 
2178 /* Adding or removeing close buttons could cause unwanted resizes, but might
2179  * not, safer to not try to do anything about it.
2180  */
2181 
multi_tab_add_close_button(MultiTab * tab)2182 void multi_tab_add_close_button(MultiTab *tab)
2183 {
2184     if (tab->close_button)
2185         return;
2186 
2187     tab->close_button = multitab_close_button_new(tab->status_icon_name);
2188     gtk_box_pack_start(GTK_BOX(tab->label_box), tab->close_button,
2189             FALSE, FALSE, 0);
2190     g_signal_connect(tab->close_button, "clicked",
2191             G_CALLBACK(multi_win_close_tab_clicked), tab);
2192     gtk_widget_show(tab->close_button);
2193 }
2194 
multi_tab_remove_close_button(MultiTab * tab)2195 void multi_tab_remove_close_button(MultiTab *tab)
2196 {
2197     if (tab->close_button)
2198     {
2199         gtk_widget_destroy(tab->close_button);
2200         tab->close_button = NULL;
2201     }
2202 }
2203 
multi_tab_set_status_icon_name(MultiTab * tab,const char * name)2204 void multi_tab_set_status_icon_name(MultiTab *tab, const char *name)
2205 {
2206     if (!strcmp(tab->status_icon_name ? tab->status_icon_name : "window-close",
2207             name ? name : "window-close"))
2208     {
2209         return;
2210     }
2211     tab->status_icon_name = name;
2212     if (tab->close_button)
2213     {
2214         multitab_close_button_set_image(
2215                 MULTITAB_CLOSE_BUTTON(tab->close_button), name);
2216     }
2217 }
2218 
multi_tab_set_middle_click_tab_action(MultiTab * tab,int action)2219 void multi_tab_set_middle_click_tab_action(MultiTab *tab, int action)
2220 {
2221     tab->middle_click_action = action;
2222 }
2223 
multi_tab_add_menutree_items(MultiWin * win,MultiTab * tab,int position)2224 static void multi_tab_add_menutree_items(MultiWin * win, MultiTab * tab,
2225         int position)
2226 {
2227     char *title = multi_tab_get_full_window_title(tab);
2228     gboolean has_num = g_str_has_prefix(tab->window_title_template, "%t. ");
2229     char *n_and_title = has_num ?
2230             g_strdup_printf("%d. %s", multi_tab_get_page_num(tab), title) :
2231             title;
2232 
2233     if (has_num)
2234         g_free(title);
2235     tab->popup_menu_item = menutree_add_tab_at_position(win->popup_menu,
2236             n_and_title, position);
2237     tab->menu_bar_item = menutree_add_tab_at_position(win->menu_bar,
2238             n_and_title, position);
2239     g_free(n_and_title);
2240     g_signal_connect(tab->popup_menu_item, "toggled",
2241         G_CALLBACK(multi_win_select_tab_action), tab);
2242     g_signal_connect(tab->menu_bar_item, "toggled",
2243         G_CALLBACK(multi_win_select_tab_action), tab);
2244     if (win->current_tab == tab)
2245     {
2246         menutree_select_tab(win->menu_bar, tab->menu_bar_item);
2247         menutree_select_tab(win->popup_menu, tab->popup_menu_item);
2248     }
2249 }
2250 
multi_win_add_tab_to_notebook(MultiWin * win,MultiTab * tab,int position)2251 static void multi_win_add_tab_to_notebook(MultiWin * win, MultiTab * tab,
2252         int position)
2253 {
2254     GtkWidget *label = make_tab_label(tab, win->tab_pos);
2255     GtkNotebook *notebook = GTK_NOTEBOOK(win->notebook);
2256 
2257     if (position == -1)
2258     {
2259         gtk_notebook_append_page(notebook, tab->widget, label);
2260     }
2261     else
2262     {
2263         gtk_notebook_insert_page(notebook, tab->widget, label, position);
2264     }
2265     gtk_notebook_set_tab_reorderable(notebook, tab->widget, TRUE);
2266     gtk_notebook_set_tab_detachable(notebook, tab->widget, TRUE);
2267     g_object_set(gtk_widget_get_parent(label), "can-focus", FALSE, NULL);
2268     /* Note at this point ntabs is how many tabs there are about to be,
2269      * not how many there were before adding.
2270      */
2271     if (win->tab_pos == GTK_POS_TOP || win->tab_pos == GTK_POS_BOTTOM)
2272     {
2273         if (win->ntabs == 1)
2274             multi_win_pack_for_single_tab(win);
2275         else if (win->ntabs == 2)
2276             multi_win_pack_for_multiple_tabs(win);
2277         else
2278             multi_tab_pack_for_multiple(tab, GTK_CONTAINER(notebook));
2279     }
2280     else
2281     {
2282         multi_tab_pack_for_horizontal(tab, GTK_CONTAINER(notebook));
2283     }
2284 }
2285 
2286 /* Keep track of a tab after it's been created; if notify_only is set, it's
2287  * assumed the tab has already been added by GTK and this is being called
2288  * just for notification */
multi_win_add_tab(MultiWin * win,MultiTab * tab,int position,gboolean notify_only)2289 static void multi_win_add_tab(MultiWin * win, MultiTab * tab, int position,
2290         gboolean notify_only)
2291 {
2292     tab->parent = win;
2293     if (tab->label)
2294     {
2295         multitab_label_set_parent(MULTITAB_LABEL(tab->label),
2296                 tab->parent->notebook, &tab->parent->best_tab_width);
2297         multi_tab_set_full_window_title(tab);
2298     }
2299     win->ignore_tabs_moving = TRUE;
2300     if (position == -1)
2301         win->tabs = g_list_append(win->tabs, tab);
2302     else
2303         win->tabs = g_list_insert(win->tabs, tab, position);
2304     ++win->ntabs;
2305     if (win->ntabs == 2 && !win->always_show_tabs)
2306     {
2307         multi_win_request_size_restore(win);
2308         multi_win_show_tabs(win);
2309     }
2310     multi_tab_add_menutree_items(win, tab, position);
2311     if (!notify_only)
2312     {
2313         multi_win_add_tab_to_notebook(win, tab, position);
2314         gtk_widget_show(tab->widget);
2315         if (win->ntabs > 1 && gtk_widget_get_visible(win->gtkwin))
2316             gtk_window_present(GTK_WINDOW(win->gtkwin));
2317     }
2318     renumber_tabs(win);
2319     multi_win_shade_menus_for_tabs(win);
2320     multi_win_select_tab(win, tab);
2321     win->ignore_tabs_moving = FALSE;
2322 }
2323 
multi_win_get_widget(MultiWin * win)2324 GtkWidget *multi_win_get_widget(MultiWin * win)
2325 {
2326     return win->gtkwin;
2327 }
2328 
multi_win_is_fullscreen(MultiWin * win)2329 gboolean multi_win_is_fullscreen(MultiWin *win)
2330 {
2331     return win->fullscreen;
2332 }
2333 
multi_win_is_maximised(MultiWin * win)2334 gboolean multi_win_is_maximised(MultiWin *win)
2335 {
2336     GdkWindow *w = gtk_widget_get_window(win->gtkwin);
2337 
2338     return (w && (gdk_window_get_state(w) & GDK_WINDOW_STATE_MAXIMIZED) != 0);
2339 }
2340 
multi_win_is_borderless(MultiWin * win)2341 gboolean multi_win_is_borderless(MultiWin *win)
2342 {
2343     return win->borderless;
2344 }
2345 
multi_win_get_zoom_index(MultiWin * win)2346 int multi_win_get_zoom_index(MultiWin *win)
2347 {
2348     return win->zoom_index;
2349 }
2350 
multi_win_get_zoom_factor(MultiWin * win)2351 double multi_win_get_zoom_factor(MultiWin *win)
2352 {
2353     int i = win->zoom_index;
2354 
2355     if (i == -1)
2356         i = MULTI_WIN_NORMAL_ZOOM_INDEX;
2357     return multi_win_zoom_factors[i];
2358 }
2359 
multi_win_get_nearest_index_for_zoom(double factor)2360 int multi_win_get_nearest_index_for_zoom(double factor)
2361 {
2362     int i;
2363 
2364     for (i = 0; i < MULTI_WIN_N_ZOOM_FACTORS; ++i)
2365     {
2366         if (multi_win_zoom_factors[i] >= factor)
2367             return i;
2368     }
2369     return i - 1;
2370 }
2371 
2372 void
multi_win_menu_connect_data(MultiWin * win,MenuTreeID id,GCallback handler,gpointer user_data,GConnectFlags flags,gulong * popup_id,gulong * bar_id,gulong * short_popup_id)2373 multi_win_menu_connect_data(MultiWin *win, MenuTreeID id,
2374     GCallback handler, gpointer user_data, GConnectFlags flags,
2375     gulong *popup_id, gulong *bar_id, gulong *short_popup_id)
2376 {
2377     int handler_id;
2378 
2379     g_return_if_fail(win);
2380     handler_id = menutree_signal_connect_data(win->popup_menu, id, handler,
2381         user_data, flags);
2382     if (popup_id)
2383         *popup_id = handler_id;
2384     handler_id = menutree_signal_connect_data(win->menu_bar, id, handler,
2385         user_data, flags);
2386     if (bar_id)
2387         *bar_id = handler_id;
2388     handler_id = menutree_signal_connect_data(win->short_popup, id, handler,
2389         user_data, flags);
2390     if (short_popup_id)
2391         *short_popup_id = handler_id;
2392 }
2393 
multi_win_get_current_tab(MultiWin * win)2394 MultiTab *multi_win_get_current_tab(MultiWin * win)
2395 {
2396     return win->current_tab;
2397 }
2398 
multi_win_get_ntabs(MultiWin * win)2399 guint multi_win_get_ntabs(MultiWin * win)
2400 {
2401     return win->ntabs;
2402 }
2403 
multi_win_get_user_data_for_current_tab(MultiWin * win)2404 gpointer multi_win_get_user_data_for_current_tab(MultiWin * win)
2405 {
2406     if (!win->current_tab)
2407         return NULL;
2408     return win->current_tab->user_data;
2409 }
2410 
multi_win_get_menu_bar(MultiWin * win)2411 MenuTree *multi_win_get_menu_bar(MultiWin * win)
2412 {
2413     return win->menu_bar;
2414 }
2415 
multi_win_get_popup_menu(MultiWin * win)2416 MenuTree *multi_win_get_popup_menu(MultiWin * win)
2417 {
2418     return win->popup_menu;
2419 }
2420 
multi_win_get_short_popup_menu(MultiWin * win)2421 MenuTree *multi_win_get_short_popup_menu(MultiWin * win)
2422 {
2423     return win->short_popup;
2424 }
2425 
multi_win_get_shortcut_scheme(MultiWin * win)2426 Options *multi_win_get_shortcut_scheme(MultiWin * win)
2427 {
2428     return win->shortcuts;
2429 }
2430 
multi_win_set_shortcut_scheme(MultiWin * win,Options * shortcuts)2431 void multi_win_set_shortcut_scheme(MultiWin *win, Options *shortcuts)
2432 {
2433     if (win->shortcuts && win->shortcuts != shortcuts)
2434     {
2435         UNREF_LOG(shortcuts_unref(win->shortcuts));
2436     }
2437     if (win->shortcuts != shortcuts)
2438     {
2439         options_ref(shortcuts);
2440         win->shortcuts = shortcuts;
2441     }
2442     if (win->menu_bar)
2443         menutree_apply_shortcuts(win->menu_bar, shortcuts);
2444     if (win->popup_menu)
2445         menutree_apply_shortcuts(win->popup_menu, shortcuts);
2446     if (win->short_popup)
2447         menutree_apply_shortcuts(win->short_popup, shortcuts);
2448 }
2449 
multi_win_get_accel_group(MultiWin * win)2450 GtkAccelGroup *multi_win_get_accel_group(MultiWin * win)
2451 {
2452     return win->accel_group;
2453 }
2454 
multi_win_set_scroll_bar_position(MultiWin * win,MultiWinScrollBar_Position new_pos)2455 MultiWinScrollBar_Position multi_win_set_scroll_bar_position(MultiWin * win,
2456     MultiWinScrollBar_Position new_pos)
2457 {
2458     if (win->scroll_bar_pos == MultiWinScrollBar_Query)
2459         win->scroll_bar_pos = new_pos;
2460     return win->scroll_bar_pos;
2461 }
2462 
multi_win_get_notebook(MultiWin * win)2463 GtkNotebook *multi_win_get_notebook(MultiWin *win)
2464 {
2465     return GTK_NOTEBOOK(win->notebook);
2466 }
2467 
multi_win_restore_focus(MultiWin * win)2468 void multi_win_restore_focus(MultiWin *win)
2469 {
2470     if (win->current_tab)
2471         gtk_widget_grab_focus(win->current_tab->active_widget);
2472 }
2473 
multi_win_set_ignore_toggles(MultiWin * win,gboolean ignore)2474 void multi_win_set_ignore_toggles(MultiWin *win, gboolean ignore)
2475 {
2476     win->ignore_toggles = ignore;
2477 }
2478 
multi_win_get_ignore_toggles(MultiWin * win)2479 gboolean multi_win_get_ignore_toggles(MultiWin *win)
2480 {
2481     return win->ignore_toggles;
2482 }
2483 
multi_win_set_wrap_switch_tab(MultiWin * win,gboolean wrap)2484 void multi_win_set_wrap_switch_tab(MultiWin *win, gboolean wrap)
2485 {
2486     win->wrap_switch_tab = wrap;
2487     multi_win_shade_for_next_and_previous_tab(win);
2488 }
2489 
multi_win_foreach_tab(MultiWin * win,MultiWinForEachTabFunc func,gpointer user_data)2490 void multi_win_foreach_tab(MultiWin *win, MultiWinForEachTabFunc func,
2491         gpointer user_data)
2492 {
2493     GList *link;
2494 
2495     for (link = win->tabs; link; link = g_list_next(link))
2496     {
2497         (*func)(link->data, user_data);
2498     }
2499 }
2500 
multi_win_get_tab_pos(MultiWin * win)2501 GtkPositionType multi_win_get_tab_pos(MultiWin *win)
2502 {
2503     return win->tab_pos;
2504 }
2505 
multi_win_get_always_show_tabs(MultiWin * win)2506 gboolean multi_win_get_always_show_tabs(MultiWin *win)
2507 {
2508     return win->always_show_tabs;
2509 }
2510 
multi_win_set_always_show_tabs(MultiWin * win,gboolean show)2511 void multi_win_set_always_show_tabs(MultiWin *win, gboolean show)
2512 {
2513     gboolean old_show = win->always_show_tabs;
2514 
2515     win->always_show_tabs = show;
2516     multi_win_set_show_tabs_menu_items(win, show);
2517     if (win->ntabs == 1 && old_show != show)
2518     {
2519         MultiTab *tab = win->current_tab;
2520 
2521         g_return_if_fail(tab != NULL);
2522         multi_win_request_size_restore(win);
2523         if (show)
2524             multi_win_show_tabs(win);
2525         else
2526             multi_win_hide_tabs(win);
2527     }
2528 }
2529 
multi_win_get_show_add_tab_button(MultiWin * win)2530 gboolean multi_win_get_show_add_tab_button(MultiWin *win)
2531 {
2532     return win->show_add_tab_button;
2533 }
2534 
multi_win_set_show_add_tab_button(MultiWin * win,gboolean show)2535 void multi_win_set_show_add_tab_button(MultiWin *win, gboolean show)
2536 {
2537     win->show_add_tab_button = show;
2538     if (show)
2539         gtk_widget_show_all(win->add_tab_button);
2540     else
2541         gtk_widget_hide(win->add_tab_button);
2542 }
2543 
multi_win_set_title_template(MultiWin * win,const char * tt)2544 void multi_win_set_title_template(MultiWin *win, const char *tt)
2545 {
2546     if (win->title_template_locked)
2547         return;
2548     g_free(win->title_template);
2549     win->title_template = tt ? g_strdup(tt) : NULL;
2550     multi_win_set_full_title(win);
2551 }
2552 
multi_win_set_title_template_locked(MultiWin * win,gboolean locked)2553 void multi_win_set_title_template_locked(MultiWin *win, gboolean locked)
2554 {
2555     win->title_template_locked = locked;
2556 }
2557 
multi_win_get_title_template_locked(MultiWin * win)2558 gboolean multi_win_get_title_template_locked(MultiWin *win)
2559 {
2560     return win->title_template_locked;
2561 }
2562 
multi_win_get_title_template(MultiWin * win)2563 const char *multi_win_get_title_template(MultiWin *win)
2564 {
2565     return win->title_template;
2566 }
2567 
multi_win_get_title(MultiWin * win)2568 const char *multi_win_get_title(MultiWin *win)
2569 {
2570     return win->child_title;
2571 }
2572 
multi_win_composite(MultiWin * win)2573 gboolean multi_win_composite(MultiWin *win)
2574 {
2575     return gdk_screen_is_composited(gtk_widget_get_screen(win->gtkwin));
2576 }
2577 
multi_win_get_shortcuts_scheme_name(MultiWin * win)2578 const char *multi_win_get_shortcuts_scheme_name(MultiWin *win)
2579 {
2580     char *slash = strrchr(win->shortcuts->name, G_DIR_SEPARATOR);
2581 
2582     return slash ? slash + 1 : win->shortcuts->name;
2583 }
2584 
multi_win_get_num_tabs(MultiWin * win)2585 guint multi_win_get_num_tabs(MultiWin *win)
2586 {
2587     return g_list_length(win->tabs);
2588 }
2589 
2590 /* Parse a sequence of digits from a geometry string.
2591  * Return pointer to first non-digit or NULL if invalid. */
parse_digits(const char * geom,int * num)2592 static const char *parse_digits(const char *geom, int *num)
2593 {
2594     unsigned long ul = 0;
2595     size_t len = geom ? strspn(geom, "0123456789") : 0;
2596     if (0 < len && 1 == sscanf(geom, "%lu", &ul) && ul <= G_MAXINT)
2597     {
2598         *num = (int) ul;
2599         return geom + len;
2600     }
2601     return NULL;
2602 }
2603 
2604 /* Parse a signed integer, which might be preceded by a plus or minus.
2605  * Return NULL if invalid, or a pointer to where the parsing ended. */
parse_signed(const char * geom,int * num)2606 static const char *parse_signed(const char *geom, int *num)
2607 {
2608     char c = *geom;
2609     if (c == '+' || c == '-')
2610         geom++;
2611     geom = parse_digits(geom, num);
2612     if (geom && c == '-')
2613     {
2614         *num = (0 - *num);
2615     }
2616     return geom;
2617 }
2618 
2619 /* Parse a pair of signed integers, each must be preceded by a plus-sign.
2620  * Return NULL if invalid, or a pointer to where the parsing ended. */
parse_geom_offsets(const char * g,int * x,int * y)2621 static const char *parse_geom_offsets(const char *g, int *x, int *y)
2622 {
2623     if (g && *g == '+')
2624     {
2625         g = parse_signed(g + 1, x);
2626         if (g && *g == '+')
2627         {
2628             return parse_signed(g + 1, y);
2629         }
2630     }
2631     return NULL;
2632 }
2633 
2634 /* Parse a geometry string WxH[+X+Y]. If pxy is non-null, then also
2635  * parse a position +X+Y. Return TRUE for WxH and set pxy for +X+Y. */
multi_win_parse_geometry(const char * geom,int * width,int * height,int * x,int * y,gboolean * pxy)2636 gboolean multi_win_parse_geometry(const char *geom,
2637         int *width, int *height, int *x, int *y, gboolean *pxy)
2638 {
2639     geom = parse_digits(geom, width);
2640     if (geom && strchr("xX", *geom))
2641     {
2642         geom = parse_digits(geom + 1, height);
2643         if (geom && (!*geom || strchr("+-", *geom)))
2644         {
2645             if (pxy)
2646             {
2647                 *pxy = (parse_geom_offsets(geom, x, y) != NULL);
2648             }
2649             return TRUE;
2650         }
2651     }
2652     return FALSE;
2653 }
2654 
2655 /* Returns TRUE if the geometry hints have changed. */
multi_win_process_geometry(MultiWin * win,MultiTab * tab,int columns,int rows,int * width,int * height)2656 static gboolean multi_win_process_geometry(MultiWin *win,
2657         MultiTab *tab, int columns, int rows, int *width, int *height)
2658 {
2659     GdkGeometry geom;
2660     GdkWindowHints hint_mask;
2661     int bw, bh;
2662     int gw, gh;
2663     int ww, wh;
2664 
2665     //g_debug("Processing geometry %dx%d", columns, rows);
2666     if (!tab)
2667         tab = win->current_tab;
2668     multi_win_geometry_func(tab->user_data, &geom, &hint_mask);
2669     g_debug("VTE cell size %dx%d, padding %dx%d",
2670             geom.width_inc, geom.height_inc,
2671             geom.base_width, geom.base_height);
2672 
2673     /* Get difference in size between toplevel window and the geometry
2674      * widget.
2675      */
2676     gw = gtk_widget_get_allocated_width(tab->active_widget);
2677     gh = gtk_widget_get_allocated_height(tab->active_widget);
2678     /* Setting the size of a window excludes its decorations, so get the
2679      * "chrome" size from the vbox.
2680      */
2681     bw = gtk_widget_get_allocated_width(win->vbox);
2682     bh = gtk_widget_get_allocated_height(win->vbox);
2683     //g_debug("Terminal allocation %dx%d, undecorated window allocation %dx%d",
2684     //        gw, gh, bw, bh);
2685     if (gw <= 1 || gh <= 1)
2686     {
2687         *width = 0;
2688         *height = 0;
2689         return FALSE;
2690     }
2691     //g_debug("Want to allocate %dx%d to terminal",
2692     //        columns * geom.width_inc + geom.base_width,
2693     //        rows * geom.height_inc + geom.base_height);
2694 
2695     geom.base_width += bw - gw;
2696     geom.base_height += bh - gh;
2697     //g_debug("Additional padding %dx%d, total %dx%d", bw - gw, bh - gh,
2698     //        geom.base_width, geom.base_height);
2699     *width = columns * geom.width_inc + geom.base_width;
2700     *height = rows * geom.height_inc + geom.base_height;
2701     //g_debug("Desired size including chrome but not CSD: %dx%d",
2702     //        *width, *height);
2703     /* From gnome-terminal's code I deduced that gtk_window_set_default_size
2704      * etc should exclude the window decorations, but the geometry hints should
2705      * include them. This seems to fix roxterm's sizing :).
2706      */
2707     ww = gtk_widget_get_allocated_width(win->gtkwin);
2708     wh = gtk_widget_get_allocated_height(win->gtkwin);
2709     //g_debug("Window allocation %dx%d, CSD %dx%d", ww, wh, ww - bw, wh - bh);
2710     geom.min_width += ww - gw;
2711     geom.min_height += wh - gh;
2712     geom.base_width += ww - bw;
2713     geom.base_height += wh - bh;
2714     if (!win->has_geometry || geom.min_width != win->geom_hints.min_width ||
2715         geom.min_height != win->geom_hints.min_height ||
2716         geom.base_width != win->geom_hints.base_width ||
2717         geom.base_height != win->geom_hints.base_height ||
2718         geom.width_inc != win->geom_hints.width_inc ||
2719         geom.height_inc != win->geom_hints.height_inc)
2720     {
2721         //g_debug("Updating geometry");
2722         win->geom_hints = geom;
2723         win->geom_hint_mask = hint_mask;
2724         win->has_geometry = TRUE;
2725         return TRUE;
2726     }
2727     //g_debug("Geometry unchanged");
2728     return FALSE;
2729 }
2730 
multi_win_set_initial_geometry(MultiWin * win,const char * geom,MultiTab * tab)2731 void multi_win_set_initial_geometry(MultiWin *win, const char *geom,
2732         MultiTab *tab)
2733 {
2734     int columns, rows, x, y, width, height;
2735     gboolean xy;
2736 
2737     if (multi_win_parse_geometry(geom, &columns, &rows, &x, &y, &xy))
2738     {
2739         //g_debug("multi_win_set_initial_geometry: %d columns, %d rows",
2740         //        columns, rows);
2741         multi_win_process_geometry(win, tab, columns, rows, &width, &height);
2742         //g_debug("processed geometry to : %dx%d", width, height);
2743         if (width > 1 && height > 1)
2744         {
2745             multi_win_apply_geometry_hints(win);
2746             gtk_window_set_default_size(GTK_WINDOW(win->gtkwin), width, height);
2747         }
2748         else
2749         {
2750             g_warning("Unable to determine geometry");
2751         }
2752         /* Honor initial position on launch: it is indispensable and ubiquitous */
2753         if (xy)
2754         {
2755             gtk_window_move(GTK_WINDOW(win->gtkwin), x, y);
2756         }
2757     }
2758 }
2759 
multi_win_apply_new_geometry(MultiWin * win,int columns,int rows,MultiTab * tab)2760 void multi_win_apply_new_geometry(MultiWin *win, int columns, int rows,
2761         MultiTab *tab)
2762 {
2763     int width, height;
2764     int old_width, old_height;
2765     GdkWindowState state = gdk_window_get_state(
2766             gtk_widget_get_window(win->gtkwin));
2767 
2768     if (state & WIN_STATE_SNAPPED)
2769     {
2770         //g_debug("Ignoring geometry change for maximized or similar state");
2771         return;
2772     }
2773 
2774     //g_debug("Processing new geometry for %dx%d columns and rows",
2775     //        columns, rows);
2776     multi_win_process_geometry(win, tab, columns, rows, &width, &height);
2777     gtk_window_get_size(GTK_WINDOW(win->gtkwin), &old_width, &old_height);
2778     //g_debug("Desired window size %dx%d, currently %dx%d",
2779     //        width, height, old_width, old_height);
2780     if (width != old_width || height != old_height)
2781     {
2782         gtk_window_set_geometry_hints(GTK_WINDOW(win->gtkwin), NULL, NULL, 0);
2783         gtk_window_resize(GTK_WINDOW(win->gtkwin), width, height);
2784         multi_win_apply_geometry_hints(win);
2785     }
2786 }
2787 
2788 /* This is maintained in order of most recently focused */
2789 GList *multi_win_all = NULL;
2790 
2791 /* vi:set sw=4 ts=4 et cindent cino= */
2792