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