1 /*
2  * Copyright © 2001 Havoc Pennington
3  * Copyright © 2002 Red Hat, Inc.
4  * Copyright © 2007, 2008, 2009 Christian Persch
5  * Copyright (C) 2012-2021 MATE Developers
6  *
7  * Mate-terminal is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Mate-terminal is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include <config.h>
22 
23 #include <string.h>
24 #include <stdlib.h>
25 #include <gtk/gtk.h>
26 #include <gdk/gdk.h>
27 #ifdef GDK_WINDOWING_X11
28 #include <gdk/gdkx.h>
29 #endif
30 #include <gdk/gdkkeysyms.h>
31 
32 #include "terminal-accels.h"
33 #include "terminal-app.h"
34 #include "terminal-debug.h"
35 #include "terminal-encoding.h"
36 #include "terminal-intl.h"
37 #include "terminal-screen-container.h"
38 #include "terminal-search-dialog.h"
39 #include "terminal-tab-label.h"
40 #include "terminal-tabs-menu.h"
41 #include "terminal-util.h"
42 #include "terminal-window.h"
43 
44 #ifdef ENABLE_SKEY
45 #include "skey-popup.h"
46 #endif
47 
48 static gboolean detach_tab = FALSE;
49 
50 struct _TerminalWindowPrivate
51 {
52     GtkActionGroup *action_group;
53     GtkUIManager *ui_manager;
54     guint ui_id;
55 
56     GtkActionGroup *profiles_action_group;
57     guint profiles_ui_id;
58 
59     GtkActionGroup *encodings_action_group;
60     guint encodings_ui_id;
61 
62     TerminalTabsMenu *tabs_menu;
63 
64     TerminalScreenPopupInfo *popup_info;
65     guint remove_popup_info_idle;
66 
67     GtkActionGroup *new_terminal_action_group;
68     guint new_terminal_ui_id;
69 
70     GtkWidget *menubar;
71     GtkWidget *notebook;
72     GtkWidget *main_vbox;
73     TerminalScreen *active_screen;
74 
75     /* Size of a character cell in pixels */
76     int old_char_width;
77     int old_char_height;
78 
79     /* Width and height added to the actual terminal grid by "chrome" inside
80      * what was traditionally the X11 window: menu bar, title bar,
81      * style-provided padding. This must be included when resizing the window
82      * and also included in geometry hints. */
83     int old_chrome_width;
84     int old_chrome_height;
85 
86     /* Width and height of the padding around the geometry widget. */
87     int old_padding_width;
88     int old_padding_height;
89 
90     void *old_geometry_widget; /* only used for pointer value as it may be freed */
91 
92     GtkWidget *confirm_close_dialog;
93     GtkWidget *search_find_dialog;
94 
95     guint menubar_visible : 1;
96     guint use_default_menubar_visibility : 1;
97 
98     /* Compositing manager integration */
99     guint have_argb_visual : 1;
100 
101     /* Used to clear stray "demands attention" flashing on our window when we
102      * unmap and map it to switch to an ARGB visual.
103      */
104     guint clear_demands_attention : 1;
105 
106     guint disposed : 1;
107     guint present_on_insert : 1;
108 
109     /* Workaround until gtk+ bug #535557 is fixed */
110     guint icon_title_set : 1;
111     time_t focus_time;
112 
113     /* should we copy selection to clibpoard */
114     int copy_selection;
115 };
116 
117 #define PROFILE_DATA_KEY "GT::Profile"
118 
119 #define FILE_NEW_TERMINAL_TAB_UI_PATH     "/menubar/File/FileNewTabProfiles"
120 #define FILE_NEW_TERMINAL_WINDOW_UI_PATH  "/menubar/File/FileNewWindowProfiles"
121 #define SET_ENCODING_UI_PATH              "/menubar/Terminal/TerminalSetEncoding/EncodingsPH"
122 #define SET_ENCODING_ACTION_NAME_PREFIX   "TerminalSetEncoding"
123 
124 #define PROFILES_UI_PATH        "/menubar/Terminal/TerminalProfiles/ProfilesPH"
125 #define PROFILES_POPUP_UI_PATH  "/Popup/PopupTerminalProfiles/ProfilesPH"
126 
127 #define SIZE_TO_UI_PATH            "/menubar/Terminal/TerminalSizeToPH"
128 #define SIZE_TO_ACTION_NAME_PREFIX "TerminalSizeTo"
129 
130 #define STOCK_NEW_TAB     "tab-new"
131 
132 #define ENCODING_DATA_KEY "encoding"
133 
134 #if 1
135 /*
136  * We don't want to enable content saving until vte supports it async.
137  * So we disable this code for stable versions.
138  */
139 #include "terminal-version.h"
140 
141 #if (TERMINAL_MINOR_VERSION & 1) != 0
142 #define ENABLE_SAVE
143 #else
144 #undef ENABLE_SAVE
145 #endif
146 #endif
147 
148 static void terminal_window_dispose     (GObject             *object);
149 static void terminal_window_finalize    (GObject             *object);
150 static gboolean terminal_window_state_event (GtkWidget            *widget,
151         GdkEventWindowState  *event);
152 
153 static gboolean terminal_window_delete_event (GtkWidget *widget,
154         GdkEvent *event,
155         gpointer data);
156 static gboolean terminal_window_focus_in_event (GtkWidget *widget,
157                                                 GdkEventFocus *event,
158                                                 gpointer data);
159 
160 static gboolean notebook_button_press_cb     (GtkWidget *notebook,
161         GdkEventButton *event,
162         GSettings *settings);
163 static gboolean window_key_press_cb     (GtkWidget *notebook,
164         GdkEventKey *event,
165         GSettings *settings);
166 static gboolean notebook_popup_menu_cb       (GtkWidget *notebook,
167         TerminalWindow *window);
168 static void notebook_page_selected_callback  (GtkWidget       *notebook,
169         GtkWidget       *page,
170         guint            page_num,
171         TerminalWindow  *window);
172 static void notebook_page_added_callback     (GtkWidget       *notebook,
173         GtkWidget       *container,
174         guint            page_num,
175         TerminalWindow  *window);
176 static void notebook_page_removed_callback   (GtkWidget       *notebook,
177         GtkWidget       *container,
178         guint            page_num,
179         TerminalWindow  *window);
180 static gboolean notebook_scroll_event_cb     (GtkWidget      *notebook,
181                                               GdkEventScroll *event,
182                                               TerminalWindow *window);
183 
184 /* Menu action callbacks */
185 static void file_new_window_callback          (GtkAction *action,
186         TerminalWindow *window);
187 static void file_new_tab_callback             (GtkAction *action,
188         TerminalWindow *window);
189 static void file_new_profile_callback         (GtkAction *action,
190         TerminalWindow *window);
191 static void file_close_window_callback        (GtkAction *action,
192         TerminalWindow *window);
193 static void file_save_contents_callback       (GtkAction *action,
194         TerminalWindow *window);
195 static void file_close_tab_callback           (GtkAction *action,
196         TerminalWindow *window);
197 static void edit_copy_callback                (GtkAction *action,
198         TerminalWindow *window);
199 static void edit_paste_callback               (GtkAction *action,
200         TerminalWindow *window);
201 static void edit_select_all_callback          (GtkAction *action,
202         TerminalWindow *window);
203 static void edit_keybindings_callback         (GtkAction *action,
204         TerminalWindow *window);
205 static void edit_profiles_callback            (GtkAction *action,
206         TerminalWindow *window);
207 static void edit_current_profile_callback     (GtkAction *action,
208         TerminalWindow *window);
209 static void view_menubar_toggled_callback     (GtkToggleAction *action,
210         TerminalWindow *window);
211 static void view_fullscreen_toggled_callback  (GtkToggleAction *action,
212         TerminalWindow *window);
213 static void view_zoom_in_callback             (GtkAction *action,
214         TerminalWindow *window);
215 static void view_zoom_out_callback            (GtkAction *action,
216         TerminalWindow *window);
217 static void view_zoom_normal_callback         (GtkAction *action,
218         TerminalWindow *window);
219 static void search_find_callback              (GtkAction *action,
220         TerminalWindow *window);
221 static void search_find_next_callback         (GtkAction *action,
222         TerminalWindow *window);
223 static void search_find_prev_callback         (GtkAction *action,
224         TerminalWindow *window);
225 static void search_clear_highlight_callback   (GtkAction *action,
226         TerminalWindow *window);
227 static void terminal_next_or_previous_profile_cb (GtkAction *action,
228         TerminalWindow *window);
229 static void terminal_set_title_callback       (GtkAction *action,
230         TerminalWindow *window);
231 static void terminal_add_encoding_callback    (GtkAction *action,
232         TerminalWindow *window);
233 static void terminal_reset_callback           (GtkAction *action,
234         TerminalWindow *window);
235 static void terminal_reset_clear_callback     (GtkAction *action,
236         TerminalWindow *window);
237 static void tabs_next_or_previous_tab_cb      (GtkAction *action,
238         TerminalWindow *window);
239 static void tabs_move_left_callback           (GtkAction *action,
240         TerminalWindow *window);
241 static void tabs_move_right_callback          (GtkAction *action,
242         TerminalWindow *window);
243 static void tabs_detach_tab_callback          (GtkAction *action,
244         TerminalWindow *window);
245 static void help_contents_callback        (GtkAction *action,
246         TerminalWindow *window);
247 static void help_about_callback           (GtkAction *action,
248         TerminalWindow *window);
249 
250 static gboolean find_larger_zoom_factor  (double  current,
251         double *found);
252 static gboolean find_smaller_zoom_factor (double  current,
253         double *found);
254 
255 static void terminal_window_show (GtkWidget *widget);
256 
257 static gboolean confirm_close_window_or_tab (TerminalWindow *window,
258         TerminalScreen *screen);
259 
260 static void
261 profile_set_callback (TerminalScreen *screen,
262                       TerminalProfile *old_profile,
263                       TerminalWindow *window);
264 static void
265 sync_screen_icon_title (TerminalScreen *screen,
266                         GParamSpec *psepc,
267                         TerminalWindow *window);
268 
G_DEFINE_TYPE_WITH_PRIVATE(TerminalWindow,terminal_window,GTK_TYPE_WINDOW)269 G_DEFINE_TYPE_WITH_PRIVATE (TerminalWindow, terminal_window, GTK_TYPE_WINDOW)
270 
271 /* Menubar mnemonics & accel settings handling */
272 
273 static void
274 app_setting_notify_cb (TerminalApp *app,
275                        GParamSpec *pspec,
276                        GdkScreen *screen)
277 {
278     GtkSettings *settings;
279     const char *prop_name;
280 
281     if (pspec)
282         prop_name = pspec->name;
283     else
284         prop_name = NULL;
285 
286     settings = gtk_settings_get_for_screen (screen);
287 
288     if (!prop_name || prop_name == I_(TERMINAL_APP_ENABLE_MNEMONICS))
289     {
290         gboolean enable_mnemonics;
291 
292         g_object_get (app, TERMINAL_APP_ENABLE_MNEMONICS, &enable_mnemonics, NULL);
293         g_object_set (settings, "gtk-enable-mnemonics", enable_mnemonics, NULL);
294     }
295 
296     if (!prop_name || prop_name == I_(TERMINAL_APP_ENABLE_MENU_BAR_ACCEL))
297     {
298         /* const */ char *saved_menubar_accel;
299         gboolean enable_menubar_accel;
300 
301         /* FIXME: Once gtk+ bug 507398 is fixed, use that to reset the property instead */
302         /* Now this is a bad hack on so many levels. */
303         saved_menubar_accel = g_object_get_data (G_OBJECT (settings), "GT::gtk-menu-bar-accel");
304         if (!saved_menubar_accel)
305         {
306             g_object_get (settings, "gtk-menu-bar-accel", &saved_menubar_accel, NULL);
307             g_object_set_data_full (G_OBJECT (settings), "GT::gtk-menu-bar-accel",
308                                     saved_menubar_accel, (GDestroyNotify) g_free);
309         }
310 
311         g_object_get (app, TERMINAL_APP_ENABLE_MENU_BAR_ACCEL, &enable_menubar_accel, NULL);
312         if (enable_menubar_accel)
313             g_object_set (settings, "gtk-menu-bar-accel", saved_menubar_accel, NULL);
314         else
315             g_object_set (settings, "gtk-menu-bar-accel", NULL, NULL);
316     }
317 }
318 
319 static void
app_setting_notify_destroy_cb(GdkScreen * screen)320 app_setting_notify_destroy_cb (GdkScreen *screen)
321 {
322     g_signal_handlers_disconnect_by_func (terminal_app_get (),
323                                           G_CALLBACK (app_setting_notify_cb),
324                                           screen);
325 }
326 
327 /* utility functions */
328 
329 /*
330   Derived from XParseGeometry() in X.org
331 
332   Copyright 1985, 1986, 1987, 1998  The Open Group
333 
334   All Rights Reserved.
335 
336   The above copyright notice and this permission notice shall be included
337   in all copies or substantial portions of the Software.
338 
339   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
340   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
341   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
342   IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
343   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
344   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
345   OTHER DEALINGS IN THE SOFTWARE.
346 
347   Except as contained in this notice, the name of The Open Group shall
348   not be used in advertising or otherwise to promote the sale, use or
349   other dealings in this Software without prior written authorization
350   from The Open Group.
351 */
352 
353 /*
354  *    XParseGeometry parses strings of the form
355  *   "=<width>x<height>{+-}<xoffset>{+-}<yoffset>", where
356  *   width, height, xoffset, and yoffset are unsigned integers.
357  *   Example: "=80x24+300-49"
358  *   The equal sign is optional.
359  *   It returns a bitmask that indicates which of the four values
360  *   were actually found in the string. For each value found,
361  *   the corresponding argument is updated; for each value
362  *   not found, the corresponding argument is left unchanged.
363  */
364 
365 /* The following code is from Xlib, and is minimally modified, so we
366  * can track any upstream changes if required. Don’t change this
367  * code. Or if you do, put in a huge comment marking which thing
368  * changed.
369  */
370 
371 static int
terminal_window_ReadInteger(char * string,char ** NextString)372 terminal_window_ReadInteger (char  *string,
373                              char **NextString)
374 {
375     register int Result = 0;
376     int Sign = 1;
377 
378     if (*string == '+')
379 	string++;
380     else if (*string == '-')
381     {
382 	string++;
383 	Sign = -1;
384     }
385     for (; (*string >= '0') && (*string <= '9'); string++)
386     {
387 	Result = (Result * 10) + (*string - '0');
388     }
389     *NextString = string;
390     if (Sign >= 0)
391 	return (Result);
392     else
393 	return (-Result);
394 }
395 
396 /*
397  * Bitmask returned by XParseGeometry(). Each bit tells if the corresponding
398  * value (x, y, width, height) was found in the parsed string.
399  */
400 #define NoValue         0x0000
401 #define XValue          0x0001
402 #define YValue          0x0002
403 #define WidthValue      0x0004
404 #define HeightValue     0x0008
405 #define XNegative       0x0010
406 #define YNegative       0x0020
407 
408 static int
terminal_window_XParseGeometry(const char * string,int * x,int * y,unsigned int * width,unsigned int * height)409 terminal_window_XParseGeometry (const char *string,
410                                 int *x, int *y,
411                                 unsigned int *width,
412                                 unsigned int *height)
413 {
414 	int mask = NoValue;
415 	register char *strind;
416 	unsigned int tempWidth = 0, tempHeight = 0;
417 	int tempX = 0, tempY = 0;
418 	char *nextCharacter;
419 
420 	if ( (string == NULL) || (*string == '\0')) return(mask);
421 	if (*string == '=')
422 		string++;  /* ignore possible '=' at beg of geometry spec */
423 
424 	strind = (char *)string;
425 	if (*strind != '+' && *strind != '-' && *strind != 'x') {
426 		tempWidth = terminal_window_ReadInteger(strind, &nextCharacter);
427 		if (strind == nextCharacter)
428 		    return (0);
429 		strind = nextCharacter;
430 		mask |= WidthValue;
431 	}
432 
433 	if (*strind == 'x' || *strind == 'X') {
434 		strind++;
435 		tempHeight = terminal_window_ReadInteger(strind, &nextCharacter);
436 		if (strind == nextCharacter)
437 		    return (0);
438 		strind = nextCharacter;
439 		mask |= HeightValue;
440 	}
441 
442 	if ((*strind == '+') || (*strind == '-')) {
443 		if (*strind == '-') {
444 			strind++;
445 			tempX = -terminal_window_ReadInteger(strind, &nextCharacter);
446 			if (strind == nextCharacter)
447 			    return (0);
448 			strind = nextCharacter;
449 			mask |= XNegative;
450 
451 		}
452 		else
453 		{	strind++;
454 			tempX = terminal_window_ReadInteger(strind, &nextCharacter);
455 			if (strind == nextCharacter)
456 			    return(0);
457 			strind = nextCharacter;
458 		}
459 		mask |= XValue;
460 		if ((*strind == '+') || (*strind == '-')) {
461 			if (*strind == '-') {
462 				strind++;
463 				tempY = -terminal_window_ReadInteger(strind, &nextCharacter);
464 				if (strind == nextCharacter)
465 				    return(0);
466 				strind = nextCharacter;
467 				mask |= YNegative;
468 
469 			}
470 			else
471 			{
472 				strind++;
473 				tempY = terminal_window_ReadInteger(strind, &nextCharacter);
474 				if (strind == nextCharacter)
475 				    return(0);
476 				strind = nextCharacter;
477 			}
478 			mask |= YValue;
479 		}
480 	}
481 
482 	/* If strind isn't at the end of the string the it's an invalid
483 		geometry specification. */
484 
485 	if (*strind != '\0') return (0);
486 
487 	if (mask & XValue)
488 	    *x = tempX;
489 	if (mask & YValue)
490 	    *y = tempY;
491 	if (mask & WidthValue)
492             *width = tempWidth;
493 	if (mask & HeightValue)
494             *height = tempHeight;
495 	return (mask);
496 }
497 
498 static char *
escape_underscores(const char * name)499 escape_underscores (const char *name)
500 {
501     GString *escaped_name;
502 
503     g_assert (name != NULL);
504 
505     /* Who'd use more that 4 underscores in a profile name... */
506     escaped_name = g_string_sized_new (strlen (name) + 4 + 1);
507 
508     while (*name)
509     {
510         if (*name == '_')
511             g_string_append (escaped_name, "__");
512         else
513             g_string_append_c (escaped_name, *name);
514         name++;
515     }
516 
517     return g_string_free (escaped_name, FALSE);
518 }
519 
520 static int
find_tab_num_at_pos(GtkNotebook * notebook,int screen_x,int screen_y)521 find_tab_num_at_pos (GtkNotebook *notebook,
522                      int screen_x,
523                      int screen_y)
524 {
525     GtkPositionType tab_pos;
526     int page_num = 0;
527     GtkNotebook *nb = GTK_NOTEBOOK (notebook);
528     GtkWidget *page;
529     GtkAllocation tab_allocation;
530 
531     tab_pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (notebook));
532 
533     while ((page = gtk_notebook_get_nth_page (nb, page_num)))
534     {
535         GtkWidget *tab;
536         int max_x, max_y, x_root, y_root;
537 
538         tab = gtk_notebook_get_tab_label (nb, page);
539         g_return_val_if_fail (tab != NULL, -1);
540 
541         if (!gtk_widget_get_mapped (GTK_WIDGET (tab)))
542         {
543             page_num++;
544             continue;
545         }
546 
547         gdk_window_get_origin (gtk_widget_get_window (tab), &x_root, &y_root);
548 
549         gtk_widget_get_allocation (tab, &tab_allocation);
550         max_x = x_root + tab_allocation.x + tab_allocation.width;
551         max_y = y_root + tab_allocation.y + tab_allocation.height;
552 
553         if ((tab_pos == GTK_POS_TOP || tab_pos == GTK_POS_BOTTOM) && screen_x <= max_x)
554             return page_num;
555 
556         if ((tab_pos == GTK_POS_LEFT || tab_pos == GTK_POS_RIGHT) && screen_y <= max_y)
557             return page_num;
558 
559         page_num++;
560     }
561 
562     return -1;
563 }
564 
565 static void
terminal_set_profile_toggled_callback(GtkToggleAction * action,TerminalWindow * window)566 terminal_set_profile_toggled_callback (GtkToggleAction *action,
567                                        TerminalWindow *window)
568 {
569     TerminalWindowPrivate *priv = window->priv;
570     TerminalProfile *profile;
571 
572     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
573     if (!gtk_toggle_action_get_active (action))
574         return;
575     G_GNUC_END_IGNORE_DEPRECATIONS;
576 
577     if (priv->active_screen == NULL)
578         return;
579 
580     profile = g_object_get_data (G_OBJECT (action), PROFILE_DATA_KEY);
581     g_assert (profile);
582 
583     if (_terminal_profile_get_forgotten (profile))
584         return;
585 
586     g_signal_handlers_block_by_func (priv->active_screen, G_CALLBACK (profile_set_callback), window);
587     terminal_screen_set_profile (priv->active_screen, profile);
588     g_signal_handlers_unblock_by_func (priv->active_screen, G_CALLBACK (profile_set_callback), window);
589 }
590 
591 static void
profile_visible_name_notify_cb(TerminalProfile * profile,GParamSpec * pspec,GtkAction * action)592 profile_visible_name_notify_cb (TerminalProfile *profile,
593                                 GParamSpec *pspec,
594                                 GtkAction *action)
595 {
596     const char *visible_name;
597     char *dot, *display_name;
598     guint num;
599 
600     visible_name = terminal_profile_get_property_string (profile, TERMINAL_PROFILE_VISIBLE_NAME);
601     display_name = escape_underscores (visible_name);
602 
603     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
604     dot = strchr (gtk_action_get_name (action), '.');
605     G_GNUC_END_IGNORE_DEPRECATIONS;
606     if (dot != NULL)
607     {
608         char *free_me;
609 
610         num = g_ascii_strtoll (dot + 1, NULL, 10);
611 
612         free_me = display_name;
613         if (num < 10)
614             /* Translators: This is the label of a menu item to choose a profile.
615              * _%d is used as the accelerator (with d between 1 and 9), and
616              * the %s is the name of the terminal profile.
617              */
618             display_name = g_strdup_printf (_("_%d. %s"), num, display_name);
619         else if (num < 36)
620             /* Translators: This is the label of a menu item to choose a profile.
621              * _%c is used as the accelerator (it will be a character between A and Z),
622              * and the %s is the name of the terminal profile.
623              */
624             display_name = g_strdup_printf (_("_%c. %s"), ('A' + num - 10), display_name);
625         else
626             free_me = NULL;
627 
628         g_free (free_me);
629     }
630 
631     g_object_set (action, "label", display_name, NULL);
632     g_free (display_name);
633 }
634 
635 static void
disconnect_profiles_from_actions_in_group(GtkActionGroup * action_group)636 disconnect_profiles_from_actions_in_group (GtkActionGroup *action_group)
637 {
638     GList *actions, *l;
639 
640     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
641     actions = gtk_action_group_list_actions (action_group);
642     G_GNUC_END_IGNORE_DEPRECATIONS;
643     for (l = actions; l != NULL; l = l->next)
644     {
645         GObject *action = G_OBJECT (l->data);
646         TerminalProfile *profile;
647 
648         profile = g_object_get_data (action, PROFILE_DATA_KEY);
649         if (!profile)
650             continue;
651 
652         g_signal_handlers_disconnect_by_func (profile, G_CALLBACK (profile_visible_name_notify_cb), action);
653     }
654     g_list_free (actions);
655 }
656 
657 static void
terminal_window_update_set_profile_menu_active_profile(TerminalWindow * window)658 terminal_window_update_set_profile_menu_active_profile (TerminalWindow *window)
659 {
660     TerminalWindowPrivate *priv = window->priv;
661     TerminalProfile *new_active_profile;
662     GList *actions, *l;
663 
664     if (!priv->profiles_action_group)
665         return;
666 
667     if (!priv->active_screen)
668         return;
669 
670     new_active_profile = terminal_screen_get_profile (priv->active_screen);
671 
672     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
673     actions = gtk_action_group_list_actions (priv->profiles_action_group);
674     G_GNUC_END_IGNORE_DEPRECATIONS;
675     for (l = actions; l != NULL; l = l->next)
676     {
677         GObject *action = G_OBJECT (l->data);
678         TerminalProfile *profile;
679 
680         profile = g_object_get_data (action, PROFILE_DATA_KEY);
681         if (profile != new_active_profile)
682             continue;
683 
684         g_signal_handlers_block_by_func (action, G_CALLBACK (terminal_set_profile_toggled_callback), window);
685         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
686         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
687         G_GNUC_END_IGNORE_DEPRECATIONS;
688         g_signal_handlers_unblock_by_func (action, G_CALLBACK (terminal_set_profile_toggled_callback), window);
689 
690         break;
691     }
692     g_list_free (actions);
693 }
694 
695 static void
terminal_window_update_set_profile_menu(TerminalWindow * window)696 terminal_window_update_set_profile_menu (TerminalWindow *window)
697 {
698     TerminalWindowPrivate *priv = window->priv;
699     TerminalProfile *active_profile;
700     GtkActionGroup *action_group;
701     GtkAction *action;
702     GList *profiles, *p;
703     GSList *group;
704     guint n;
705     gboolean single_profile;
706 
707     /* Remove the old UI */
708     if (priv->profiles_ui_id != 0)
709     {
710         gtk_ui_manager_remove_ui (priv->ui_manager, priv->profiles_ui_id);
711         priv->profiles_ui_id = 0;
712     }
713 
714     if (priv->profiles_action_group != NULL)
715     {
716         disconnect_profiles_from_actions_in_group (priv->profiles_action_group);
717         gtk_ui_manager_remove_action_group (priv->ui_manager,
718                                             priv->profiles_action_group);
719         priv->profiles_action_group = NULL;
720     }
721 
722     profiles = terminal_app_get_profile_list (terminal_app_get ());
723 
724     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
725     action = gtk_action_group_get_action (priv->action_group, "TerminalProfiles");
726     single_profile = !profiles || profiles->next == NULL; /* list length <= 1 */
727     gtk_action_set_sensitive (action, !single_profile);
728     G_GNUC_END_IGNORE_DEPRECATIONS;
729     if (profiles == NULL)
730         return;
731 
732     if (priv->active_screen)
733         active_profile = terminal_screen_get_profile (priv->active_screen);
734     else
735         active_profile = NULL;
736 
737     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
738     action_group = priv->profiles_action_group = gtk_action_group_new ("Profiles");
739     G_GNUC_END_IGNORE_DEPRECATIONS;
740     gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1);
741     g_object_unref (action_group);
742 
743     priv->profiles_ui_id = gtk_ui_manager_new_merge_id (priv->ui_manager);
744 
745     group = NULL;
746     n = 0;
747     for (p = profiles; p != NULL; p = p->next)
748     {
749         TerminalProfile *profile = (TerminalProfile *) p->data;
750         GtkRadioAction *profile_action;
751         char name[32];
752 
753         g_snprintf (name, sizeof (name), "TerminalSetProfile%u", n++);
754 
755         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
756         profile_action = gtk_radio_action_new (name,
757                                                NULL,
758                                                NULL,
759                                                NULL,
760                                                n);
761 
762         gtk_radio_action_set_group (profile_action, group);
763         group = gtk_radio_action_get_group (profile_action);
764 
765         if (profile == active_profile)
766             gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (profile_action), TRUE);
767         G_GNUC_END_IGNORE_DEPRECATIONS;
768 
769         g_object_set_data_full (G_OBJECT (profile_action),
770                                 PROFILE_DATA_KEY,
771                                 g_object_ref (profile),
772                                 (GDestroyNotify) g_object_unref);
773         profile_visible_name_notify_cb (profile, NULL, GTK_ACTION (profile_action));
774         g_signal_connect (profile, "notify::" TERMINAL_PROFILE_VISIBLE_NAME,
775                           G_CALLBACK (profile_visible_name_notify_cb), profile_action);
776         g_signal_connect (profile_action, "toggled",
777                           G_CALLBACK (terminal_set_profile_toggled_callback), window);
778 
779         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
780         gtk_action_group_add_action (action_group, GTK_ACTION (profile_action));
781         G_GNUC_END_IGNORE_DEPRECATIONS;
782         g_object_unref (profile_action);
783 
784         gtk_ui_manager_add_ui (priv->ui_manager, priv->profiles_ui_id,
785                                PROFILES_UI_PATH,
786                                name, name,
787                                GTK_UI_MANAGER_MENUITEM, FALSE);
788         gtk_ui_manager_add_ui (priv->ui_manager, priv->profiles_ui_id,
789                                PROFILES_POPUP_UI_PATH,
790                                name, name,
791                                GTK_UI_MANAGER_MENUITEM, FALSE);
792     }
793 
794     g_list_free (profiles);
795 }
796 
797 static void
terminal_window_create_new_terminal_action(TerminalWindow * window,TerminalProfile * profile,const char * name,guint num,GCallback callback)798 terminal_window_create_new_terminal_action (TerminalWindow *window,
799         TerminalProfile *profile,
800         const char *name,
801         guint num,
802         GCallback callback)
803 {
804     TerminalWindowPrivate *priv = window->priv;
805     GtkAction *action;
806 
807     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
808     action = gtk_action_new (name, NULL, NULL, NULL);
809     G_GNUC_END_IGNORE_DEPRECATIONS;
810 
811     g_object_set_data_full (G_OBJECT (action),
812                             PROFILE_DATA_KEY,
813                             g_object_ref (profile),
814                             (GDestroyNotify) g_object_unref);
815     profile_visible_name_notify_cb (profile, NULL, action);
816     g_signal_connect (profile, "notify::" TERMINAL_PROFILE_VISIBLE_NAME,
817                       G_CALLBACK (profile_visible_name_notify_cb), action);
818     g_signal_connect (action, "activate", callback, window);
819 
820     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
821     gtk_action_group_add_action (priv->new_terminal_action_group, action);
822     G_GNUC_END_IGNORE_DEPRECATIONS;
823     g_object_unref (action);
824 }
825 
826 static void
terminal_window_update_new_terminal_menus(TerminalWindow * window)827 terminal_window_update_new_terminal_menus (TerminalWindow *window)
828 {
829     TerminalWindowPrivate *priv = window->priv;
830     GtkActionGroup *action_group;
831     GtkAction *action;
832     GList *profiles, *p;
833     guint n;
834     gboolean have_single_profile;
835 
836     /* Remove the old UI */
837     if (priv->new_terminal_ui_id != 0)
838     {
839         gtk_ui_manager_remove_ui (priv->ui_manager, priv->new_terminal_ui_id);
840         priv->new_terminal_ui_id = 0;
841     }
842 
843     if (priv->new_terminal_action_group != NULL)
844     {
845         disconnect_profiles_from_actions_in_group (priv->new_terminal_action_group);
846         gtk_ui_manager_remove_action_group (priv->ui_manager,
847                                             priv->new_terminal_action_group);
848         priv->new_terminal_action_group = NULL;
849     }
850 
851     profiles = terminal_app_get_profile_list (terminal_app_get ());
852     have_single_profile = !profiles || !profiles->next;
853 
854     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
855     action = gtk_action_group_get_action (priv->action_group, "FileNewTab");
856     gtk_action_set_visible (action, have_single_profile);
857     action = gtk_action_group_get_action (priv->action_group, "FileNewWindow");
858     gtk_action_set_visible (action, have_single_profile);
859     G_GNUC_END_IGNORE_DEPRECATIONS;
860 
861     if (have_single_profile)
862     {
863         g_list_free (profiles);
864         return;
865     }
866 
867     /* Now build the submenus */
868 
869     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
870     action_group = priv->new_terminal_action_group = gtk_action_group_new ("NewTerminal");
871     G_GNUC_END_IGNORE_DEPRECATIONS;
872     gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1);
873     g_object_unref (action_group);
874 
875     priv->new_terminal_ui_id = gtk_ui_manager_new_merge_id (priv->ui_manager);
876 
877     n = 0;
878     for (p = profiles; p != NULL; p = p->next)
879     {
880         TerminalProfile *profile = (TerminalProfile *) p->data;
881         char name[32];
882 
883         g_snprintf (name, sizeof (name), "FileNewTab.%u", n);
884         terminal_window_create_new_terminal_action (window,
885                 profile,
886                 name,
887                 n,
888                 G_CALLBACK (file_new_tab_callback));
889 
890         gtk_ui_manager_add_ui (priv->ui_manager, priv->new_terminal_ui_id,
891                                FILE_NEW_TERMINAL_TAB_UI_PATH,
892                                name, name,
893                                GTK_UI_MANAGER_MENUITEM, FALSE);
894 
895         g_snprintf (name, sizeof (name), "FileNewWindow.%u", n);
896         terminal_window_create_new_terminal_action (window,
897                 profile,
898                 name,
899                 n,
900                 G_CALLBACK (file_new_window_callback));
901 
902         gtk_ui_manager_add_ui (priv->ui_manager, priv->new_terminal_ui_id,
903                                FILE_NEW_TERMINAL_WINDOW_UI_PATH,
904                                name, name,
905                                GTK_UI_MANAGER_MENUITEM, FALSE);
906 
907         ++n;
908     }
909 
910     g_list_free (profiles);
911 }
912 
913 static void
terminal_set_encoding_callback(GtkToggleAction * action,TerminalWindow * window)914 terminal_set_encoding_callback (GtkToggleAction *action,
915                                 TerminalWindow *window)
916 {
917     TerminalWindowPrivate *priv = window->priv;
918     TerminalEncoding *encoding;
919 
920     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
921     if (!gtk_toggle_action_get_active (action))
922         return;
923     G_GNUC_END_IGNORE_DEPRECATIONS;
924 
925     if (priv->active_screen == NULL)
926         return;
927 
928     encoding = g_object_get_data (G_OBJECT (action), ENCODING_DATA_KEY);
929     g_assert (encoding);
930 
931     vte_terminal_set_encoding (VTE_TERMINAL (priv->active_screen),
932                                terminal_encoding_get_charset (encoding), NULL);
933 }
934 
935 static void
terminal_window_update_encoding_menu(TerminalWindow * window)936 terminal_window_update_encoding_menu (TerminalWindow *window)
937 {
938     TerminalWindowPrivate *priv = window->priv;
939     TerminalApp *app;
940     GtkActionGroup *action_group;
941     GSList *group;
942     guint n;
943     GSList *encodings, *l;
944     const char *charset;
945     TerminalEncoding *active_encoding;
946 
947     /* Remove the old UI */
948     if (priv->encodings_ui_id != 0)
949     {
950         gtk_ui_manager_remove_ui (priv->ui_manager, priv->encodings_ui_id);
951         priv->encodings_ui_id = 0;
952     }
953 
954     if (priv->encodings_action_group != NULL)
955     {
956         gtk_ui_manager_remove_action_group (priv->ui_manager,
957                                             priv->encodings_action_group);
958         priv->encodings_action_group = NULL;
959     }
960 
961     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
962     action_group = priv->encodings_action_group = gtk_action_group_new ("Encodings");
963     G_GNUC_END_IGNORE_DEPRECATIONS;
964     gtk_ui_manager_insert_action_group (priv->ui_manager, action_group, -1);
965     g_object_unref (action_group);
966 
967     priv->encodings_ui_id = gtk_ui_manager_new_merge_id (priv->ui_manager);
968 
969     if (priv->active_screen)
970         charset = vte_terminal_get_encoding (VTE_TERMINAL (priv->active_screen));
971     else
972         charset = "current";
973 
974     app = terminal_app_get ();
975     active_encoding = terminal_app_ensure_encoding (app, charset);
976 
977     encodings = terminal_app_get_active_encodings (app);
978 
979     if (g_slist_find (encodings, active_encoding) == NULL)
980         encodings = g_slist_append (encodings, terminal_encoding_ref (active_encoding));
981 
982     group = NULL;
983     n = 0;
984     for (l = encodings; l != NULL; l = l->next)
985     {
986         TerminalEncoding *e = (TerminalEncoding *) l->data;
987         GtkRadioAction *encoding_action;
988         char name[128];
989         char *display_name;
990 
991         g_snprintf (name, sizeof (name), SET_ENCODING_ACTION_NAME_PREFIX "%s", terminal_encoding_get_id (e));
992         display_name = g_strdup_printf ("%s (%s)", e->name, terminal_encoding_get_charset (e));
993 
994         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
995         encoding_action = gtk_radio_action_new (name,
996                                                 display_name,
997                                                 NULL,
998                                                 NULL,
999                                                 n);
1000         g_free (display_name);
1001 
1002         gtk_radio_action_set_group (encoding_action, group);
1003         group = gtk_radio_action_get_group (encoding_action);
1004 
1005         if (charset && strcmp (terminal_encoding_get_id (e), charset) == 0)
1006             gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (encoding_action), TRUE);
1007         G_GNUC_END_IGNORE_DEPRECATIONS;
1008 
1009         g_signal_connect (encoding_action, "toggled",
1010                           G_CALLBACK (terminal_set_encoding_callback), window);
1011 
1012         g_object_set_data_full (G_OBJECT (encoding_action), ENCODING_DATA_KEY,
1013                                 terminal_encoding_ref (e),
1014                                 (GDestroyNotify) terminal_encoding_unref);
1015 
1016         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1017         gtk_action_group_add_action (action_group, GTK_ACTION (encoding_action));
1018         G_GNUC_END_IGNORE_DEPRECATIONS;
1019         g_object_unref (encoding_action);
1020 
1021         gtk_ui_manager_add_ui (priv->ui_manager, priv->encodings_ui_id,
1022                                SET_ENCODING_UI_PATH,
1023                                name, name,
1024                                GTK_UI_MANAGER_MENUITEM, FALSE);
1025     }
1026 
1027     g_slist_foreach (encodings, (GFunc) terminal_encoding_unref, NULL);
1028     g_slist_free (encodings);
1029 }
1030 
1031 static void
terminal_window_update_encoding_menu_active_encoding(TerminalWindow * window)1032 terminal_window_update_encoding_menu_active_encoding (TerminalWindow *window)
1033 {
1034     TerminalWindowPrivate *priv = window->priv;
1035     GtkAction *action;
1036     char name[128];
1037 
1038     if (!priv->active_screen)
1039         return;
1040     if (!priv->encodings_action_group)
1041         return;
1042 
1043     g_snprintf (name, sizeof (name), SET_ENCODING_ACTION_NAME_PREFIX "%s",
1044                 vte_terminal_get_encoding (VTE_TERMINAL (priv->active_screen)));
1045     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1046     action = gtk_action_group_get_action (priv->encodings_action_group, name);
1047     G_GNUC_END_IGNORE_DEPRECATIONS;
1048     if (!action)
1049         return;
1050 
1051     g_signal_handlers_block_by_func (action, G_CALLBACK (terminal_set_encoding_callback), window);
1052     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1053     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
1054     G_GNUC_END_IGNORE_DEPRECATIONS;
1055     g_signal_handlers_unblock_by_func (action, G_CALLBACK (terminal_set_encoding_callback), window);
1056 }
1057 
1058 static void
terminal_size_to_cb(GtkAction * action,TerminalWindow * window)1059 terminal_size_to_cb (GtkAction *action,
1060                      TerminalWindow *window)
1061 {
1062     TerminalWindowPrivate *priv = window->priv;
1063     const char *name;
1064     char *end = NULL;
1065     guint width, height;
1066 
1067     if (priv->active_screen == NULL)
1068         return;
1069 
1070     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1071     name = gtk_action_get_name (action) + strlen (SIZE_TO_ACTION_NAME_PREFIX);
1072     G_GNUC_END_IGNORE_DEPRECATIONS;
1073     width = g_ascii_strtoull (name, &end, 10);
1074     g_assert (end && *end == 'x');
1075     height = g_ascii_strtoull (end + 1, &end, 10);
1076     g_assert (end && *end == '\0');
1077 
1078     vte_terminal_set_size (VTE_TERMINAL (priv->active_screen), width, height);
1079 
1080     terminal_window_update_size (window, priv->active_screen, TRUE);
1081 }
1082 
1083 static void
terminal_window_update_size_to_menu(TerminalWindow * window)1084 terminal_window_update_size_to_menu (TerminalWindow *window)
1085 {
1086     static const struct
1087     {
1088         guint grid_width;
1089         guint grid_height;
1090     } predefined_sizes[] =
1091     {
1092         { 80, 24 },
1093         { 80, 43 },
1094         { 132, 24 },
1095         { 132, 43 }
1096     };
1097     TerminalWindowPrivate *priv = window->priv;
1098     guint i;
1099 
1100     /* We only install this once, so there's no need for a separate action group
1101      * and any cleanup + build-new-one action here.
1102      */
1103 
1104     for (i = 0; i < G_N_ELEMENTS (predefined_sizes); ++i)
1105     {
1106         guint grid_width = predefined_sizes[i].grid_width;
1107         guint grid_height = predefined_sizes[i].grid_height;
1108         GtkAction *action;
1109         char name[40];
1110         char *display_name;
1111 
1112         g_snprintf (name, sizeof (name), SIZE_TO_ACTION_NAME_PREFIX "%ux%u",
1113                     grid_width, grid_height);
1114 
1115         /* If there are ever more than 9 of these, extend this to use A..Z as mnemonics,
1116          * like we do for the profiles menu.
1117          */
1118         display_name = g_strdup_printf ("_%u. %ux%u", i + 1, grid_width, grid_height);
1119 
1120         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1121         action = gtk_action_new (name, display_name, NULL, NULL);
1122         G_GNUC_END_IGNORE_DEPRECATIONS;
1123         g_free (display_name);
1124 
1125         g_signal_connect (action, "activate",
1126                           G_CALLBACK (terminal_size_to_cb), window);
1127 
1128         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1129         gtk_action_group_add_action (priv->action_group, action);
1130         G_GNUC_END_IGNORE_DEPRECATIONS;
1131         g_object_unref (action);
1132 
1133         gtk_ui_manager_add_ui (priv->ui_manager, priv->ui_id,
1134                                SIZE_TO_UI_PATH,
1135                                name, name,
1136                                GTK_UI_MANAGER_MENUITEM, FALSE);
1137     }
1138 }
1139 
1140 /* Actions stuff */
1141 
1142 static void
terminal_window_update_copy_sensitivity(TerminalScreen * screen,TerminalWindow * window)1143 terminal_window_update_copy_sensitivity (TerminalScreen *screen,
1144                                          TerminalWindow *window)
1145 {
1146     TerminalWindowPrivate *priv = window->priv;
1147     GtkAction *action;
1148     gboolean can_copy;
1149 
1150     if (screen != priv->active_screen)
1151         return;
1152 
1153     can_copy = vte_terminal_get_has_selection (VTE_TERMINAL (screen));
1154 
1155     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1156     action = gtk_action_group_get_action (priv->action_group, "EditCopy");
1157     gtk_action_set_sensitive (action, can_copy);
1158     G_GNUC_END_IGNORE_DEPRECATIONS;
1159 
1160     if (can_copy && priv->copy_selection)
1161 #if VTE_CHECK_VERSION (0, 50, 0)
1162         vte_terminal_copy_clipboard_format (VTE_TERMINAL(screen), VTE_FORMAT_TEXT);
1163 #else
1164         vte_terminal_copy_clipboard(VTE_TERMINAL(screen));
1165 #endif
1166 }
1167 
1168 static void
terminal_window_update_zoom_sensitivity(TerminalWindow * window)1169 terminal_window_update_zoom_sensitivity (TerminalWindow *window)
1170 {
1171     TerminalWindowPrivate *priv = window->priv;
1172     TerminalScreen *screen;
1173     GtkAction *action;
1174     double current, zoom;
1175 
1176     screen = priv->active_screen;
1177     if (screen == NULL)
1178         return;
1179 
1180     current = terminal_screen_get_font_scale (screen);
1181 
1182     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1183     action = gtk_action_group_get_action (priv->action_group, "ViewZoomOut");
1184     gtk_action_set_sensitive (action, find_smaller_zoom_factor (current, &zoom));
1185     action = gtk_action_group_get_action (priv->action_group, "ViewZoomIn");
1186     gtk_action_set_sensitive (action, find_larger_zoom_factor (current, &zoom));
1187     G_GNUC_END_IGNORE_DEPRECATIONS;
1188 }
1189 
1190 static void
terminal_window_update_search_sensitivity(TerminalScreen * screen,TerminalWindow * window)1191 terminal_window_update_search_sensitivity (TerminalScreen *screen,
1192         TerminalWindow *window)
1193 {
1194     TerminalWindowPrivate *priv = window->priv;
1195     GtkAction *action;
1196     gboolean can_search;
1197 
1198     if (screen != priv->active_screen)
1199         return;
1200 
1201     can_search = vte_terminal_search_get_regex (VTE_TERMINAL (screen)) != NULL;
1202 
1203     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1204     action = gtk_action_group_get_action (priv->action_group, "SearchFindNext");
1205     gtk_action_set_sensitive (action, can_search);
1206     action = gtk_action_group_get_action (priv->action_group, "SearchFindPrevious");
1207     gtk_action_set_sensitive (action, can_search);
1208     action = gtk_action_group_get_action (priv->action_group, "SearchClearHighlight");
1209     gtk_action_set_sensitive (action, can_search);
1210     G_GNUC_END_IGNORE_DEPRECATIONS;
1211 }
1212 
1213 static void
update_edit_menu_cb(GtkClipboard * clipboard,GdkAtom * targets,int n_targets,TerminalWindow * window)1214 update_edit_menu_cb (GtkClipboard *clipboard,
1215                      GdkAtom *targets,
1216                      int n_targets,
1217                      TerminalWindow *window)
1218 {
1219     TerminalWindowPrivate *priv = window->priv;
1220     GtkAction *action;
1221     gboolean can_paste, can_paste_uris;
1222 
1223     can_paste = targets != NULL && gtk_targets_include_text (targets, n_targets);
1224     can_paste_uris = targets != NULL && gtk_targets_include_uri (targets, n_targets);
1225 
1226     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1227     action = gtk_action_group_get_action (priv->action_group, "EditPaste");
1228     gtk_action_set_sensitive (action, can_paste);
1229     action = gtk_action_group_get_action (priv->action_group, "EditPasteURIPaths");
1230     gtk_action_set_visible (action, can_paste_uris);
1231     gtk_action_set_sensitive (action, can_paste_uris);
1232     G_GNUC_END_IGNORE_DEPRECATIONS;
1233 
1234     /* Ref was added in gtk_clipboard_request_targets below */
1235     g_object_unref (window);
1236 }
1237 
1238 static void
update_edit_menu(TerminalWindow * window)1239 update_edit_menu(TerminalWindow *window)
1240 {
1241     GtkClipboard *clipboard;
1242 
1243     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
1244     gtk_clipboard_request_targets (clipboard,
1245                                    (GtkClipboardTargetsReceivedFunc) update_edit_menu_cb,
1246                                    g_object_ref (window));
1247 }
1248 
1249 static void
screen_resize_window_cb(TerminalScreen * screen,guint width,guint height,TerminalWindow * window)1250 screen_resize_window_cb (TerminalScreen *screen,
1251                          guint width,
1252                          guint height,
1253                          TerminalWindow* window)
1254 {
1255     TerminalWindowPrivate *priv = window->priv;
1256     VteTerminal *terminal = VTE_TERMINAL (screen);
1257     GtkWidget *widget = GTK_WIDGET (screen);
1258 
1259     /* Don't do anything if we're maximised or fullscreened */
1260     // FIXME: realized && ... instead?
1261     if (!gtk_widget_get_realized (widget) ||
1262             (gdk_window_get_state (gtk_widget_get_window (widget)) & (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_FULLSCREEN)) != 0)
1263         return;
1264 
1265     vte_terminal_set_size (terminal, width, height);
1266 
1267     if (screen != priv->active_screen)
1268         return;
1269 
1270     terminal_window_update_size (window, screen, TRUE);
1271 }
1272 
1273 static void
terminal_window_update_tabs_menu_sensitivity(TerminalWindow * window)1274 terminal_window_update_tabs_menu_sensitivity (TerminalWindow *window)
1275 {
1276     TerminalWindowPrivate *priv = window->priv;
1277     GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook);
1278     GtkActionGroup *action_group = priv->action_group;
1279     GtkAction *action;
1280     int num_pages, page_num;
1281     gboolean not_first, not_last;
1282 
1283     if (priv->disposed)
1284         return;
1285 
1286     num_pages = gtk_notebook_get_n_pages (notebook);
1287     page_num = gtk_notebook_get_current_page (notebook);
1288     not_first = page_num > 0;
1289     not_last = page_num + 1 < num_pages;
1290 
1291     /* Hide the tabs menu in single-tab windows */
1292     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1293     action = gtk_action_group_get_action (action_group, "Tabs");
1294     gtk_action_set_visible (action, num_pages > 1);
1295 
1296 #if 1
1297     /* NOTE: We always make next/prev actions sensitive except in
1298      * single-tab windows, so the corresponding shortcut key escape code
1299      * isn't sent to the terminal. See bug #453193 and bug #138609.
1300      * This also makes tab cycling work, bug #92139.
1301      * FIXME: Find a better way to do this.
1302      */
1303     action = gtk_action_group_get_action (action_group, "TabsPrevious");
1304     gtk_action_set_sensitive (action, num_pages > 1);
1305     action = gtk_action_group_get_action (action_group, "TabsNext");
1306     gtk_action_set_sensitive (action, num_pages > 1);
1307 #else
1308     /* This would be correct, but see the comment above. */
1309     action = gtk_action_group_get_action (action_group, "TabsPrevious");
1310     gtk_action_set_sensitive (action, not_first);
1311     action = gtk_action_group_get_action (action_group, "TabsNext");
1312     gtk_action_set_sensitive (action, not_last);
1313 #endif
1314 
1315     action = gtk_action_group_get_action (action_group, "TabsMoveLeft");
1316     gtk_action_set_sensitive (action, not_first);
1317     action = gtk_action_group_get_action (action_group, "TabsMoveRight");
1318     gtk_action_set_sensitive (action, not_last);
1319     action = gtk_action_group_get_action (action_group, "TabsDetach");
1320     gtk_action_set_sensitive (action, num_pages > 1);
1321     action = gtk_action_group_get_action (action_group, "FileCloseTab");
1322     gtk_action_set_sensitive (action, num_pages > 1);
1323     G_GNUC_END_IGNORE_DEPRECATIONS;
1324 }
1325 
1326 static void
update_tab_visibility(TerminalWindow * window,int change)1327 update_tab_visibility (TerminalWindow *window,
1328                        int             change)
1329 {
1330     TerminalWindowPrivate *priv = window->priv;
1331     gboolean show_tabs;
1332     guint num;
1333 
1334     num = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1335 
1336     show_tabs = (num + change) > 1;
1337     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), show_tabs);
1338 }
1339 
1340 static GtkNotebook *
handle_tab_droped_on_desktop(GtkNotebook * source_notebook,GtkWidget * container,gint x,gint y,gpointer data)1341 handle_tab_droped_on_desktop (GtkNotebook *source_notebook,
1342                               GtkWidget   *container,
1343                               gint         x,
1344                               gint         y,
1345                               gpointer     data)
1346 {
1347     TerminalWindow *source_window;
1348     TerminalWindow *new_window;
1349     TerminalWindowPrivate *new_priv;
1350 
1351     source_window = TERMINAL_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (source_notebook)));
1352     g_return_val_if_fail (TERMINAL_IS_WINDOW (source_window), NULL);
1353 
1354     new_window = terminal_app_new_window (terminal_app_get (),
1355                                           gtk_widget_get_screen (GTK_WIDGET (source_window)));
1356     new_priv = new_window->priv;
1357     new_priv->present_on_insert = TRUE;
1358 
1359     update_tab_visibility (source_window, -1);
1360     update_tab_visibility (new_window, +1);
1361 
1362     return GTK_NOTEBOOK (new_priv->notebook);
1363 }
1364 
1365 /* Terminal screen popup menu handling */
1366 
1367 static void
popup_open_url_callback(GtkAction * action,TerminalWindow * window)1368 popup_open_url_callback (GtkAction *action,
1369                          TerminalWindow *window)
1370 {
1371     TerminalWindowPrivate *priv = window->priv;
1372     TerminalScreenPopupInfo *info = priv->popup_info;
1373 
1374     if (info == NULL)
1375         return;
1376 
1377     terminal_util_open_url (GTK_WIDGET (window), info->string, info->flavour,
1378                             gtk_get_current_event_time ());
1379 }
1380 
1381 static void
popup_copy_url_callback(GtkAction * action,TerminalWindow * window)1382 popup_copy_url_callback (GtkAction *action,
1383                          TerminalWindow *window)
1384 {
1385     TerminalWindowPrivate *priv = window->priv;
1386     TerminalScreenPopupInfo *info = priv->popup_info;
1387     GtkClipboard *clipboard;
1388 
1389     if (info == NULL)
1390         return;
1391 
1392     if (info->string == NULL)
1393         return;
1394 
1395     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
1396     gtk_clipboard_set_text (clipboard, info->string, -1);
1397 }
1398 
1399 static void
popup_leave_fullscreen_callback(GtkAction * action,TerminalWindow * window)1400 popup_leave_fullscreen_callback (GtkAction *action,
1401                                  TerminalWindow *window)
1402 {
1403     gtk_window_unfullscreen (GTK_WINDOW (window));
1404 }
1405 
1406 static void
remove_popup_info(TerminalWindow * window)1407 remove_popup_info (TerminalWindow *window)
1408 {
1409     TerminalWindowPrivate *priv = window->priv;
1410 
1411     if (priv->remove_popup_info_idle != 0)
1412     {
1413         g_source_remove (priv->remove_popup_info_idle);
1414         priv->remove_popup_info_idle = 0;
1415     }
1416 
1417     if (priv->popup_info != NULL)
1418     {
1419         terminal_screen_popup_info_unref (priv->popup_info);
1420         priv->popup_info = NULL;
1421     }
1422 }
1423 
1424 static gboolean
idle_remove_popup_info(TerminalWindow * window)1425 idle_remove_popup_info (TerminalWindow *window)
1426 {
1427     TerminalWindowPrivate *priv = window->priv;
1428 
1429     priv->remove_popup_info_idle = 0;
1430     remove_popup_info (window);
1431     return FALSE;
1432 }
1433 
1434 static void
unset_popup_info(TerminalWindow * window)1435 unset_popup_info (TerminalWindow *window)
1436 {
1437     TerminalWindowPrivate *priv = window->priv;
1438 
1439     /* Unref the event from idle since we still need it
1440      * from the action callbacks which will run before idle.
1441      */
1442     if (priv->remove_popup_info_idle == 0 &&
1443             priv->popup_info != NULL)
1444     {
1445         priv->remove_popup_info_idle =
1446             g_idle_add ((GSourceFunc) idle_remove_popup_info, window);
1447     }
1448 }
1449 
1450 static void
popup_menu_deactivate_callback(GtkWidget * popup,TerminalWindow * window)1451 popup_menu_deactivate_callback (GtkWidget *popup,
1452                                 TerminalWindow *window)
1453 {
1454     TerminalWindowPrivate *priv = window->priv;
1455     GtkWidget *im_menu_item;
1456 
1457     g_signal_handlers_disconnect_by_func
1458     (popup, G_CALLBACK (popup_menu_deactivate_callback), window);
1459 
1460     im_menu_item = gtk_ui_manager_get_widget (priv->ui_manager,
1461                    "/Popup/PopupInputMethods");
1462     gtk_menu_item_set_submenu (GTK_MENU_ITEM (im_menu_item), NULL);
1463 
1464     unset_popup_info (window);
1465 }
1466 
1467 static void
popup_clipboard_targets_received_cb(GtkClipboard * clipboard,GdkAtom * targets,int n_targets,TerminalScreenPopupInfo * info)1468 popup_clipboard_targets_received_cb (GtkClipboard *clipboard,
1469                                      GdkAtom *targets,
1470                                      int n_targets,
1471                                      TerminalScreenPopupInfo *info)
1472 {
1473     TerminalWindow *window = info->window;
1474     TerminalWindowPrivate *priv = window->priv;
1475     TerminalScreen *screen = info->screen;
1476     GtkWidget *popup_menu;
1477     GtkAction *action;
1478     gboolean can_paste, can_paste_uris, show_link, show_email_link, show_call_link, show_input_method_menu;
1479     int n_pages;
1480 
1481     if (!gtk_widget_get_realized (GTK_WIDGET (screen)))
1482     {
1483         terminal_screen_popup_info_unref (info);
1484         return;
1485     }
1486 
1487     /* Now we know that the screen is realized, we know that the window is still alive */
1488     remove_popup_info (window);
1489 
1490     priv->popup_info = info; /* adopt the ref added when requesting the clipboard */
1491 
1492     n_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook));
1493 
1494     can_paste = targets != NULL && gtk_targets_include_text (targets, n_targets);
1495     can_paste_uris = targets != NULL && gtk_targets_include_uri (targets, n_targets);
1496     show_link = info->string != NULL && (info->flavour == FLAVOR_AS_IS || info->flavour == FLAVOR_DEFAULT_TO_HTTP);
1497     show_email_link = info->string != NULL && info->flavour == FLAVOR_EMAIL;
1498     show_call_link = info->string != NULL && info->flavour == FLAVOR_VOIP_CALL;
1499 
1500     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1501     action = gtk_action_group_get_action (priv->action_group, "PopupSendEmail");
1502     gtk_action_set_visible (action, show_email_link);
1503     action = gtk_action_group_get_action (priv->action_group, "PopupCopyEmailAddress");
1504     gtk_action_set_visible (action, show_email_link);
1505     action = gtk_action_group_get_action (priv->action_group, "PopupCall");
1506     gtk_action_set_visible (action, show_call_link);
1507     action = gtk_action_group_get_action (priv->action_group, "PopupCopyCallAddress");
1508     gtk_action_set_visible (action, show_call_link);
1509     action = gtk_action_group_get_action (priv->action_group, "PopupOpenLink");
1510     gtk_action_set_visible (action, show_link);
1511     action = gtk_action_group_get_action (priv->action_group, "PopupCopyLinkAddress");
1512     gtk_action_set_visible (action, show_link);
1513 
1514     action = gtk_action_group_get_action (priv->action_group, "PopupCloseWindow");
1515     gtk_action_set_visible (action, n_pages <= 1);
1516     action = gtk_action_group_get_action (priv->action_group, "PopupCloseTab");
1517     gtk_action_set_visible (action, n_pages > 1);
1518 
1519     action = gtk_action_group_get_action (priv->action_group, "PopupCopy");
1520     gtk_action_set_sensitive (action, vte_terminal_get_has_selection (VTE_TERMINAL (screen)));
1521     action = gtk_action_group_get_action (priv->action_group, "PopupPaste");
1522     gtk_action_set_sensitive (action, can_paste);
1523     action = gtk_action_group_get_action (priv->action_group, "PopupPasteURIPaths");
1524     gtk_action_set_visible (action, can_paste_uris);
1525     G_GNUC_END_IGNORE_DEPRECATIONS;
1526 
1527     g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
1528                   "gtk-show-input-method-menu", &show_input_method_menu,
1529                   NULL);
1530 
1531     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1532     action = gtk_action_group_get_action (priv->action_group, "PopupInputMethods");
1533     gtk_action_set_visible (action, show_input_method_menu);
1534     G_GNUC_END_IGNORE_DEPRECATIONS;
1535 
1536     popup_menu = gtk_ui_manager_get_widget (priv->ui_manager, "/Popup");
1537     g_signal_connect (popup_menu, "deactivate",
1538                       G_CALLBACK (popup_menu_deactivate_callback), window);
1539 
1540     /* Pseudo activation of the popup menu's action */
1541     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1542     action = gtk_action_group_get_action (priv->action_group, "Popup");
1543     gtk_action_activate (action);
1544     G_GNUC_END_IGNORE_DEPRECATIONS;
1545 
1546     if (info->button == 0)
1547         gtk_menu_shell_select_first (GTK_MENU_SHELL (popup_menu), FALSE);
1548 
1549     if (!gtk_menu_get_attach_widget (GTK_MENU (popup_menu)))
1550         gtk_menu_attach_to_widget (GTK_MENU (popup_menu),GTK_WIDGET (screen),NULL);
1551 
1552     gtk_menu_popup (GTK_MENU (popup_menu),
1553                     NULL, NULL,
1554                     NULL, NULL,
1555                     info->button,
1556                     info->timestamp);
1557 }
1558 
1559 static void
screen_show_popup_menu_callback(TerminalScreen * screen,TerminalScreenPopupInfo * info,TerminalWindow * window)1560 screen_show_popup_menu_callback (TerminalScreen *screen,
1561                                  TerminalScreenPopupInfo *info,
1562                                  TerminalWindow *window)
1563 {
1564     GtkClipboard *clipboard;
1565 
1566     g_return_if_fail (info->window == window);
1567 
1568     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
1569     gtk_clipboard_request_targets (clipboard,
1570                                    (GtkClipboardTargetsReceivedFunc) popup_clipboard_targets_received_cb,
1571                                    terminal_screen_popup_info_ref (info));
1572 }
1573 
1574 static gboolean
screen_match_clicked_cb(TerminalScreen * screen,const char * match,int flavour,guint state,TerminalWindow * window)1575 screen_match_clicked_cb (TerminalScreen *screen,
1576                          const char *match,
1577                          int flavour,
1578                          guint state,
1579                          TerminalWindow *window)
1580 {
1581     TerminalWindowPrivate *priv = window->priv;
1582 
1583     if (screen != priv->active_screen)
1584         return FALSE;
1585 
1586     switch (flavour)
1587     {
1588 #ifdef ENABLE_SKEY
1589     case FLAVOR_SKEY:
1590         terminal_skey_do_popup (GTK_WINDOW (window), screen, match);
1591         break;
1592 #endif
1593     default:
1594         gtk_widget_grab_focus (GTK_WIDGET (screen));
1595         terminal_util_open_url (GTK_WIDGET (window), match, flavour,
1596                                 gtk_get_current_event_time ());
1597         break;
1598     }
1599 
1600     return TRUE;
1601 }
1602 
1603 static void
screen_close_cb(TerminalScreen * screen,TerminalWindow * window)1604 screen_close_cb (TerminalScreen *screen,
1605                  TerminalWindow *window)
1606 {
1607     terminal_window_remove_screen (window, screen);
1608 }
1609 
1610 static gboolean
terminal_window_accel_activate_cb(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,TerminalWindow * window)1611 terminal_window_accel_activate_cb (GtkAccelGroup  *accel_group,
1612                                    GObject        *acceleratable,
1613                                    guint           keyval,
1614                                    GdkModifierType modifier,
1615                                    TerminalWindow *window)
1616 {
1617     GtkAccelGroupEntry *entries;
1618     guint n_entries;
1619     gboolean retval = FALSE;
1620 
1621     entries = gtk_accel_group_query (accel_group, keyval, modifier, &n_entries);
1622     if (n_entries > 0)
1623     {
1624         const char *accel_path;
1625 
1626         accel_path = g_quark_to_string (entries[0].accel_path_quark);
1627 
1628         if (g_str_has_prefix (accel_path, "<Actions>/Main/"))
1629         {
1630             const char *action_name;
1631 
1632             /* We want to always consume these accelerators, even if the corresponding
1633              * action is insensitive, so the corresponding shortcut key escape code
1634              * isn't sent to the terminal. See bug #453193, bug #138609 and bug #559728.
1635              * This also makes tab cycling work, bug #92139. (NOT!)
1636              */
1637 
1638             action_name = I_(accel_path + strlen ("<Actions>/Main/"));
1639 
1640 #if 0
1641             if (gtk_notebook_get_n_pages (GTK_NOTEBOOK (priv->notebook)) > 1 &&
1642                     (action_name == I_("TabsPrevious") || action_name == I_("TabsNext")))
1643                 retval = TRUE;
1644             else
1645 #endif
1646                 if (action_name == I_("EditCopy") ||
1647                         action_name == I_("PopupCopy") ||
1648                         action_name == I_("EditPaste") ||
1649                         action_name == I_("PopupPaste"))
1650                     retval = TRUE;
1651         }
1652     }
1653 
1654     return retval;
1655 }
1656 
1657 /*****************************************/
1658 
1659 #ifdef MATE_ENABLE_DEBUG
1660 static void
terminal_window_size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation)1661 terminal_window_size_allocate_cb (GtkWidget *widget,
1662                                   GtkAllocation *allocation)
1663 {
1664     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
1665                            "[window %p] size-alloc result %d : %d at (%d, %d)\n",
1666                            widget,
1667                            allocation->width, allocation->height,
1668                            allocation->x, allocation->y);
1669 }
1670 #endif /* MATE_ENABLE_DEBUG */
1671 
1672 static void
terminal_window_realize(GtkWidget * widget)1673 terminal_window_realize (GtkWidget *widget)
1674 {
1675     TerminalWindow *window = TERMINAL_WINDOW (widget);
1676     TerminalWindowPrivate *priv = window->priv;
1677 #if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND)
1678     GdkScreen *screen;
1679     GtkAllocation widget_allocation;
1680     GdkVisual *visual;
1681 
1682     gtk_widget_get_allocation (widget, &widget_allocation);
1683     screen = gtk_widget_get_screen (GTK_WIDGET (window));
1684 
1685     if (gdk_screen_is_composited (screen) &&
1686         (visual = gdk_screen_get_rgba_visual (screen)) != NULL)
1687     {
1688           /* Set RGBA visual if possible so VTE can use real transparency */
1689         gtk_widget_set_visual (GTK_WIDGET (widget), visual);
1690         priv->have_argb_visual = TRUE;
1691     }
1692     else
1693     {
1694         gtk_widget_set_visual (GTK_WIDGET (window), gdk_screen_get_system_visual (screen));
1695         priv->have_argb_visual = FALSE;
1696     }
1697 #endif
1698 
1699     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
1700                            "[window %p] realize, size %d : %d at (%d, %d)\n",
1701                            widget,
1702                            widget_allocation.width, widget_allocation.height,
1703                            widget_allocation.x, widget_allocation.y);
1704 
1705     GTK_WIDGET_CLASS (terminal_window_parent_class)->realize (widget);
1706 
1707     /* Need to do this now since this requires the window to be realized */
1708     if (priv->active_screen != NULL)
1709         sync_screen_icon_title (priv->active_screen, NULL, window);
1710 }
1711 
1712 static gboolean
terminal_window_map_event(GtkWidget * widget,GdkEventAny * event)1713 terminal_window_map_event (GtkWidget    *widget,
1714                            GdkEventAny  *event)
1715 {
1716     TerminalWindow *window = TERMINAL_WINDOW (widget);
1717     TerminalWindowPrivate *priv = window->priv;
1718     gboolean (* map_event) (GtkWidget *, GdkEventAny *) =
1719         GTK_WIDGET_CLASS (terminal_window_parent_class)->map_event;
1720     GtkAllocation widget_allocation;
1721 
1722     gtk_widget_get_allocation (widget, &widget_allocation);
1723     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
1724                            "[window %p] map-event, size %d : %d at (%d, %d)\n",
1725                            widget,
1726                            widget_allocation.width, widget_allocation.height,
1727                            widget_allocation.x, widget_allocation.y);
1728 
1729     if (priv->clear_demands_attention)
1730     {
1731 #ifdef GDK_WINDOWING_X11
1732         terminal_util_x11_clear_demands_attention (gtk_widget_get_window (widget));
1733 #endif
1734 
1735         priv->clear_demands_attention = FALSE;
1736     }
1737 
1738     if (map_event)
1739         return map_event (widget, event);
1740 
1741     return FALSE;
1742 }
1743 
1744 
1745 static gboolean
terminal_window_state_event(GtkWidget * widget,GdkEventWindowState * event)1746 terminal_window_state_event (GtkWidget            *widget,
1747                              GdkEventWindowState  *event)
1748 {
1749     gboolean (* window_state_event) (GtkWidget *, GdkEventWindowState *event) =
1750         GTK_WIDGET_CLASS (terminal_window_parent_class)->window_state_event;
1751 
1752     if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
1753     {
1754         TerminalWindow *window = TERMINAL_WINDOW (widget);
1755         TerminalWindowPrivate *priv = window->priv;
1756         GtkAction *action;
1757         gboolean is_fullscreen;
1758 
1759         is_fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
1760 
1761         G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1762         action = gtk_action_group_get_action (priv->action_group, "ViewFullscreen");
1763         gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), is_fullscreen);
1764 
1765         action = gtk_action_group_get_action (priv->action_group, "PopupLeaveFullscreen");
1766         gtk_action_set_visible (action, is_fullscreen);
1767         G_GNUC_END_IGNORE_DEPRECATIONS;
1768     }
1769 
1770     if (window_state_event)
1771         return window_state_event (widget, event);
1772 
1773     return FALSE;
1774 }
1775 
1776 #ifdef GDK_WINDOWING_X11
1777 static void
terminal_window_window_manager_changed_cb(GdkScreen * screen,TerminalWindow * window)1778 terminal_window_window_manager_changed_cb (GdkScreen *screen,
1779         TerminalWindow *window)
1780 {
1781     TerminalWindowPrivate *priv = window->priv;
1782     GtkAction *action;
1783     gboolean supports_fs;
1784 
1785     supports_fs = gdk_x11_screen_supports_net_wm_hint (screen, gdk_atom_intern ("_NET_WM_STATE_FULLSCREEN", FALSE));
1786 
1787     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
1788     action = gtk_action_group_get_action (priv->action_group, "ViewFullscreen");
1789     gtk_action_set_sensitive (action, supports_fs);
1790     G_GNUC_END_IGNORE_DEPRECATIONS;
1791 }
1792 #endif
1793 
1794 static void
terminal_window_screen_update(TerminalWindow * window,GdkScreen * screen)1795 terminal_window_screen_update (TerminalWindow *window,
1796                                GdkScreen *screen)
1797 {
1798     TerminalApp *app;
1799 
1800 #ifdef GDK_WINDOWING_X11
1801     if (screen && GDK_IS_X11_SCREEN (screen))
1802     {
1803         terminal_window_window_manager_changed_cb (screen, window);
1804         g_signal_connect (screen, "window-manager-changed",
1805                           G_CALLBACK (terminal_window_window_manager_changed_cb), window);
1806     }
1807 #endif
1808 
1809     if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (screen), "GT::HasSettingsConnection")))
1810         return;
1811 
1812     g_object_set_data_full (G_OBJECT (screen), "GT::HasSettingsConnection",
1813                             GINT_TO_POINTER (TRUE),
1814                             (GDestroyNotify) app_setting_notify_destroy_cb);
1815 
1816     app = terminal_app_get ();
1817     app_setting_notify_cb (app, NULL, screen);
1818     g_signal_connect (app, "notify::" TERMINAL_APP_ENABLE_MNEMONICS,
1819                       G_CALLBACK (app_setting_notify_cb), screen);
1820     g_signal_connect (app, "notify::" TERMINAL_APP_ENABLE_MENU_BAR_ACCEL,
1821                       G_CALLBACK (app_setting_notify_cb), screen);
1822 }
1823 
1824 static void
terminal_window_screen_changed(GtkWidget * widget,GdkScreen * previous_screen)1825 terminal_window_screen_changed (GtkWidget *widget,
1826                                 GdkScreen *previous_screen)
1827 {
1828     TerminalWindow *window = TERMINAL_WINDOW (widget);
1829     void (* screen_changed) (GtkWidget *, GdkScreen *) =
1830         GTK_WIDGET_CLASS (terminal_window_parent_class)->screen_changed;
1831     GdkScreen *screen;
1832 
1833     if (screen_changed)
1834         screen_changed (widget, previous_screen);
1835 
1836     screen = gtk_widget_get_screen (widget);
1837     if (previous_screen == screen)
1838         return;
1839 
1840 #ifdef GDK_WINDOWING_X11
1841     if (previous_screen && GDK_IS_X11_SCREEN (previous_screen))
1842     {
1843         g_signal_handlers_disconnect_by_func (previous_screen,
1844                                               G_CALLBACK (terminal_window_window_manager_changed_cb),
1845                                               window);
1846     }
1847 #endif
1848 
1849     if (!screen)
1850         return;
1851 
1852     terminal_window_screen_update (window, screen);
1853 }
1854 
1855 static void
terminal_window_profile_list_changed_cb(TerminalApp * app,TerminalWindow * window)1856 terminal_window_profile_list_changed_cb (TerminalApp *app,
1857         TerminalWindow *window)
1858 {
1859     terminal_window_update_set_profile_menu (window);
1860     terminal_window_update_new_terminal_menus (window);
1861 }
1862 
1863 static void
terminal_window_encoding_list_changed_cb(TerminalApp * app,TerminalWindow * window)1864 terminal_window_encoding_list_changed_cb (TerminalApp *app,
1865         TerminalWindow *window)
1866 {
1867     terminal_window_update_encoding_menu (window);
1868 }
1869 
1870 static void
terminal_window_init(TerminalWindow * window)1871 terminal_window_init (TerminalWindow *window)
1872 {
1873     const GtkActionEntry menu_entries[] =
1874     {
1875         /* Toplevel */
1876         { "File", NULL, N_("_File"), NULL, NULL, NULL },
1877         { "FileNewWindowProfiles", "utilities-terminal", N_("Open _Terminal"), NULL, NULL, NULL },
1878         { "FileNewTabProfiles", STOCK_NEW_TAB, N_("Open Ta_b"), NULL, NULL, NULL },
1879         { "Edit", NULL, N_("_Edit"), NULL, NULL, NULL },
1880         { "View", NULL, N_("_View"), NULL, NULL, NULL },
1881         { "Search", NULL, N_("_Search"), NULL, NULL, NULL },
1882         { "Terminal", NULL, N_("_Terminal"), NULL, NULL, NULL },
1883         { "Tabs", NULL, N_("Ta_bs"), NULL, NULL, NULL },
1884         { "Help", NULL, N_("_Help"), NULL, NULL, NULL },
1885         { "Popup", NULL, NULL, NULL, NULL, NULL },
1886         { "NotebookPopup", NULL, "", NULL, NULL, NULL },
1887 
1888         /* File menu */
1889         {
1890             "FileNewWindow", "utilities-terminal", N_("Open _Terminal"), "<shift><control>N",
1891             NULL,
1892             G_CALLBACK (file_new_window_callback)
1893         },
1894         {
1895             "FileNewTab", STOCK_NEW_TAB, N_("Open Ta_b"), "<shift><control>T",
1896             NULL,
1897             G_CALLBACK (file_new_tab_callback)
1898         },
1899         {
1900             "FileNewProfile", "document-open", N_("New _Profile…"), "",
1901             NULL,
1902             G_CALLBACK (file_new_profile_callback)
1903         },
1904         {
1905             "FileSaveContents", "document-save", N_("_Save Contents"), "",
1906             NULL,
1907             G_CALLBACK (file_save_contents_callback)
1908         },
1909         {
1910             "FileCloseTab", "window-close", N_("C_lose Tab"), "<shift><control>W",
1911             NULL,
1912             G_CALLBACK (file_close_tab_callback)
1913         },
1914         {
1915             "FileCloseWindow", "window-close", N_("_Close Window"), "<shift><control>Q",
1916             NULL,
1917             G_CALLBACK (file_close_window_callback)
1918         },
1919 
1920         /* Edit menu */
1921         {
1922             "EditCopy", "edit-copy", N_("_Copy"), "<shift><control>C",
1923             NULL,
1924             G_CALLBACK (edit_copy_callback)
1925         },
1926         {
1927             "EditPaste", "edit-paste", N_("_Paste"), "<shift><control>V",
1928             NULL,
1929             G_CALLBACK (edit_paste_callback)
1930         },
1931         {
1932             "EditPasteURIPaths", "edit-paste", N_("Paste _Filenames"), "",
1933             NULL,
1934             G_CALLBACK (edit_paste_callback)
1935         },
1936         {
1937             "EditSelectAll", "edit-select-all", N_("Select _All"), "<shift><control>A",
1938             NULL,
1939             G_CALLBACK (edit_select_all_callback)
1940         },
1941         {
1942             "EditProfiles", NULL, N_("P_rofiles…"), NULL,
1943             NULL,
1944             G_CALLBACK (edit_profiles_callback)
1945         },
1946         {
1947             "EditKeybindings", NULL, N_("_Keyboard Shortcuts…"), NULL,
1948             NULL,
1949             G_CALLBACK (edit_keybindings_callback)
1950         },
1951         {
1952             "EditCurrentProfile", NULL, N_("Pr_ofile Preferences"), NULL,
1953             NULL,
1954             G_CALLBACK (edit_current_profile_callback)
1955         },
1956 
1957         /* View menu */
1958         {
1959             "ViewZoomIn", "zoom-in", N_("Zoom _In"), "<control>plus",
1960             NULL,
1961             G_CALLBACK (view_zoom_in_callback)
1962         },
1963         {
1964             "ViewZoomOut", "zoom-out", N_("Zoom _Out"), "<control>minus",
1965             NULL,
1966             G_CALLBACK (view_zoom_out_callback)
1967         },
1968         {
1969             "ViewZoom100", "zoom-original", N_("_Normal Size"), "<control>0",
1970             NULL,
1971             G_CALLBACK (view_zoom_normal_callback)
1972         },
1973 
1974         /* Search menu */
1975         {
1976             "SearchFind", "edit-find", N_("_Find..."), "<shift><control>F",
1977             NULL,
1978             G_CALLBACK (search_find_callback)
1979         },
1980         {
1981             "SearchFindNext", NULL, N_("Find Ne_xt"), "<shift><control>H",
1982             NULL,
1983             G_CALLBACK (search_find_next_callback)
1984         },
1985         {
1986             "SearchFindPrevious", NULL, N_("Find Pre_vious"), "<shift><control>G",
1987             NULL,
1988             G_CALLBACK (search_find_prev_callback)
1989         },
1990         {
1991             "SearchClearHighlight", NULL, N_("_Clear Highlight"), "<shift><control>J",
1992             NULL,
1993             G_CALLBACK (search_clear_highlight_callback)
1994         },
1995 #if 0
1996         {
1997             "SearchGoToLine", "go-jump", N_("Go to _Line..."), "<shift><control>I",
1998             NULL,
1999             G_CALLBACK (search_goto_line_callback)
2000         },
2001         {
2002             "SearchIncrementalSearch", "edit-find", N_("_Incremental Search..."), "<shift><control>K",
2003             NULL,
2004             G_CALLBACK (search_incremental_search_callback)
2005         },
2006 #endif
2007 
2008         /* Terminal menu */
2009         { "TerminalProfiles", NULL, N_("Change _Profile"), NULL, NULL, NULL },
2010         {
2011             "ProfilePrevious", NULL, N_("_Previous Profile"), "<alt>Page_Up",
2012             NULL,
2013             G_CALLBACK (terminal_next_or_previous_profile_cb)
2014         },
2015         {
2016             "ProfileNext", NULL, N_("_Next Profile"), "<alt>Page_Down",
2017             NULL,
2018             G_CALLBACK (terminal_next_or_previous_profile_cb)
2019         },
2020         {
2021             "TerminalSetTitle", NULL, N_("_Set Title…"), NULL,
2022             NULL,
2023             G_CALLBACK (terminal_set_title_callback)
2024         },
2025         { "TerminalSetEncoding", NULL, N_("Set _Character Encoding"), NULL, NULL, NULL },
2026         {
2027             "TerminalReset", NULL, N_("_Reset"), NULL,
2028             NULL,
2029             G_CALLBACK (terminal_reset_callback)
2030         },
2031         {
2032             "TerminalResetClear", NULL, N_("Reset and C_lear"), NULL,
2033             NULL,
2034             G_CALLBACK (terminal_reset_clear_callback)
2035         },
2036 
2037         /* Terminal/Encodings menu */
2038         {
2039             "TerminalAddEncoding", NULL, N_("_Add or Remove…"), NULL,
2040             NULL,
2041             G_CALLBACK (terminal_add_encoding_callback)
2042         },
2043 
2044         /* Tabs menu */
2045         {
2046             "TabsPrevious", NULL, N_("_Previous Tab"), "<control>Page_Up",
2047             NULL,
2048             G_CALLBACK (tabs_next_or_previous_tab_cb)
2049         },
2050         {
2051             "TabsNext", NULL, N_("_Next Tab"), "<control>Page_Down",
2052             NULL,
2053             G_CALLBACK (tabs_next_or_previous_tab_cb)
2054         },
2055         {
2056             "TabsMoveLeft", NULL, N_("Move Tab _Left"), "<shift><control>Page_Up",
2057             NULL,
2058             G_CALLBACK (tabs_move_left_callback)
2059         },
2060         {
2061             "TabsMoveRight", NULL, N_("Move Tab _Right"), "<shift><control>Page_Down",
2062             NULL,
2063             G_CALLBACK (tabs_move_right_callback)
2064         },
2065         {
2066             "TabsDetach", NULL, N_("_Detach tab"), NULL,
2067             NULL,
2068             G_CALLBACK (tabs_detach_tab_callback)
2069         },
2070 
2071         /* Help menu */
2072         {
2073             "HelpContents", "help-browser", N_("_Contents"), "F1",
2074             NULL,
2075             G_CALLBACK (help_contents_callback)
2076         },
2077         {
2078             "HelpAbout", "help-about", N_("_About"), NULL,
2079             NULL,
2080             G_CALLBACK (help_about_callback)
2081         },
2082 
2083         /* Popup menu */
2084         {
2085             "PopupSendEmail", NULL, N_("_Send Mail To…"), NULL,
2086             NULL,
2087             G_CALLBACK (popup_open_url_callback)
2088         },
2089         {
2090             "PopupCopyEmailAddress", NULL, N_("_Copy E-mail Address"), NULL,
2091             NULL,
2092             G_CALLBACK (popup_copy_url_callback)
2093         },
2094         {
2095             "PopupCall", NULL, N_("C_all To…"), NULL,
2096             NULL,
2097             G_CALLBACK (popup_open_url_callback)
2098         },
2099         {
2100             "PopupCopyCallAddress", NULL, N_("_Copy Call Address"), NULL,
2101             NULL,
2102             G_CALLBACK (popup_copy_url_callback)
2103         },
2104         {
2105             "PopupOpenLink", NULL, N_("_Open Link"), NULL,
2106             NULL,
2107             G_CALLBACK (popup_open_url_callback)
2108         },
2109         {
2110             "PopupCopyLinkAddress", NULL, N_("_Copy Link Address"), NULL,
2111             NULL,
2112             G_CALLBACK (popup_copy_url_callback)
2113         },
2114         { "PopupTerminalProfiles", NULL, N_("P_rofiles"), NULL, NULL, NULL },
2115         {
2116             "PopupCopy", "edit-copy", N_("_Copy"), "",
2117             NULL,
2118             G_CALLBACK (edit_copy_callback)
2119         },
2120         {
2121             "PopupPaste", "edit-paste", N_("_Paste"), "",
2122             NULL,
2123             G_CALLBACK (edit_paste_callback)
2124         },
2125         {
2126             "PopupPasteURIPaths", "edit-paste", N_("Paste _Filenames"), "",
2127             NULL,
2128             G_CALLBACK (edit_paste_callback)
2129         },
2130         {
2131             "PopupNewTerminal", "utilities-terminal", N_("Open _Terminal"), NULL,
2132             NULL,
2133             G_CALLBACK (file_new_window_callback)
2134         },
2135         {
2136             "PopupNewTab", "tab-new", N_("Open Ta_b"), NULL,
2137             NULL,
2138             G_CALLBACK (file_new_tab_callback)
2139         },
2140         {
2141             "PopupCloseWindow", "window-close", N_("C_lose Window"), NULL,
2142             NULL,
2143             G_CALLBACK (file_close_window_callback)
2144         },
2145         {
2146             "PopupCloseTab", "window-close", N_("C_lose Tab"), NULL,
2147             NULL,
2148             G_CALLBACK (file_close_tab_callback)
2149         },
2150         {
2151             "PopupLeaveFullscreen", NULL, N_("L_eave Full Screen"), NULL,
2152             NULL,
2153             G_CALLBACK (popup_leave_fullscreen_callback)
2154         },
2155         { "PopupInputMethods", NULL, N_("_Input Methods"), NULL, NULL, NULL }
2156     };
2157 
2158     const GtkToggleActionEntry toggle_menu_entries[] =
2159     {
2160         /* View Menu */
2161         {
2162             "ViewMenubar", NULL, N_("Show _Menubar"), NULL,
2163             NULL,
2164             G_CALLBACK (view_menubar_toggled_callback),
2165             FALSE
2166         },
2167         {
2168             "ViewFullscreen", NULL, N_("_Full Screen"), NULL,
2169             NULL,
2170             G_CALLBACK (view_fullscreen_toggled_callback),
2171             FALSE
2172         }
2173     };
2174     TerminalWindowPrivate *priv;
2175     TerminalApp *app;
2176     GtkActionGroup *action_group;
2177     GtkAction *action;
2178     GtkUIManager *manager;
2179     GError *error;
2180     GtkWindowGroup *window_group;
2181     GtkAccelGroup *accel_group;
2182     GtkClipboard *clipboard;
2183 
2184     priv = window->priv = terminal_window_get_instance_private (window);
2185 
2186     g_signal_connect (G_OBJECT (window), "delete_event",
2187                       G_CALLBACK(terminal_window_delete_event),
2188                       NULL);
2189     g_signal_connect (G_OBJECT (window), "focus_in_event",
2190                       G_CALLBACK(terminal_window_focus_in_event),
2191                       NULL);
2192 
2193 #ifdef MATE_ENABLE_DEBUG
2194     _TERMINAL_DEBUG_IF (TERMINAL_DEBUG_GEOMETRY)
2195     {
2196         g_signal_connect_after (window, "size-allocate", G_CALLBACK (terminal_window_size_allocate_cb), NULL);
2197     }
2198 #endif
2199 
2200     GtkStyleContext *context;
2201 
2202     context = gtk_widget_get_style_context (GTK_WIDGET (window));
2203     gtk_style_context_add_class (context, "mate-terminal");
2204 
2205     gtk_window_set_title (GTK_WINDOW (window), _("Terminal"));
2206 
2207     priv->active_screen = NULL;
2208     priv->menubar_visible = FALSE;
2209 
2210     priv->main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
2211     gtk_container_add (GTK_CONTAINER (window), priv->main_vbox);
2212     gtk_widget_show (priv->main_vbox);
2213 
2214     priv->notebook = gtk_notebook_new ();
2215     gtk_notebook_set_scrollable (GTK_NOTEBOOK (priv->notebook), TRUE);
2216     gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE);
2217     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE);
2218     gtk_notebook_set_group_name (GTK_NOTEBOOK (priv->notebook), I_("mate-terminal-window"));
2219     g_signal_connect (priv->notebook, "button-press-event",
2220                       G_CALLBACK (notebook_button_press_cb), settings_global);
2221     g_signal_connect (window, "key-press-event",
2222                       G_CALLBACK (window_key_press_cb), settings_global);
2223     g_signal_connect (priv->notebook, "popup-menu",
2224                       G_CALLBACK (notebook_popup_menu_cb), window);
2225     g_signal_connect_after (priv->notebook, "switch-page",
2226                             G_CALLBACK (notebook_page_selected_callback), window);
2227     g_signal_connect_after (priv->notebook, "page-added",
2228                             G_CALLBACK (notebook_page_added_callback), window);
2229     g_signal_connect_after (priv->notebook, "page-removed",
2230                             G_CALLBACK (notebook_page_removed_callback), window);
2231     g_signal_connect_data (priv->notebook, "page-reordered",
2232                            G_CALLBACK (terminal_window_update_tabs_menu_sensitivity),
2233                            window, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER);
2234 
2235     gtk_widget_add_events (priv->notebook, GDK_SCROLL_MASK);
2236     g_signal_connect (priv->notebook, "scroll-event",
2237                             G_CALLBACK (notebook_scroll_event_cb), window);
2238 
2239     g_signal_connect (priv->notebook, "create-window",
2240                     G_CALLBACK (handle_tab_droped_on_desktop), window);
2241 
2242     gtk_box_pack_end (GTK_BOX (priv->main_vbox), priv->notebook, TRUE, TRUE, 0);
2243     gtk_widget_show (priv->notebook);
2244 
2245     priv->old_char_width = -1;
2246     priv->old_char_height = -1;
2247 
2248     priv->old_chrome_width = -1;
2249     priv->old_chrome_height = -1;
2250     priv->old_padding_width = -1;
2251     priv->old_padding_height = -1;
2252 
2253     priv->old_geometry_widget = NULL;
2254 
2255     /* Create the UI manager */
2256     manager = priv->ui_manager = gtk_ui_manager_new ();
2257 
2258     accel_group = gtk_ui_manager_get_accel_group (manager);
2259     gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
2260     /* Workaround for bug #453193, bug #138609 and bug #559728 */
2261     g_signal_connect_after (accel_group, "accel-activate",
2262                             G_CALLBACK (terminal_window_accel_activate_cb), window);
2263 
2264     /* Create the actions */
2265     /* Note that this action group name is used in terminal-accels.c; do not change it */
2266     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2267     priv->action_group = action_group = gtk_action_group_new ("Main");
2268     gtk_action_group_set_translation_domain (action_group, NULL);
2269     gtk_action_group_add_actions (action_group, menu_entries,
2270                                   G_N_ELEMENTS (menu_entries), window);
2271     gtk_action_group_add_toggle_actions (action_group,
2272                                          toggle_menu_entries,
2273                                          G_N_ELEMENTS (toggle_menu_entries),
2274                                          window);
2275     G_GNUC_END_IGNORE_DEPRECATIONS;
2276     gtk_ui_manager_insert_action_group (manager, action_group, 0);
2277     g_object_unref (action_group);
2278 
2279    clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
2280    g_signal_connect_swapped (clipboard, "owner-change",
2281                              G_CALLBACK (update_edit_menu), window);
2282    update_edit_menu (window);
2283     /* Idem for this action, since the window is not fullscreen. */
2284     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2285     action = gtk_action_group_get_action (priv->action_group, "PopupLeaveFullscreen");
2286     gtk_action_set_visible (action, FALSE);
2287 
2288 #ifndef ENABLE_SAVE
2289     action = gtk_action_group_get_action (priv->action_group, "FileSaveContents");
2290     gtk_action_set_visible (action, FALSE);
2291 #endif
2292     G_GNUC_END_IGNORE_DEPRECATIONS;
2293 
2294     /* Load the UI */
2295     error = NULL;
2296     priv->ui_id = gtk_ui_manager_add_ui_from_resource (manager,
2297                   TERMINAL_RESOURCES_PATH_PREFIX G_DIR_SEPARATOR_S "ui/terminal.xml",
2298                   &error);
2299     g_assert_no_error (error);
2300 
2301     priv->menubar = gtk_ui_manager_get_widget (manager, "/menubar");
2302     gtk_box_pack_start (GTK_BOX (priv->main_vbox),
2303                         priv->menubar,
2304                         FALSE, FALSE, 0);
2305 
2306     /* Add tabs menu */
2307     priv->tabs_menu = terminal_tabs_menu_new (window);
2308 
2309     app = terminal_app_get ();
2310     terminal_window_profile_list_changed_cb (app, window);
2311     g_signal_connect (app, "profile-list-changed",
2312                       G_CALLBACK (terminal_window_profile_list_changed_cb), window);
2313 
2314     terminal_window_encoding_list_changed_cb (app, window);
2315     g_signal_connect (app, "encoding-list-changed",
2316                       G_CALLBACK (terminal_window_encoding_list_changed_cb), window);
2317 
2318     terminal_window_set_menubar_visible (window, TRUE);
2319     priv->use_default_menubar_visibility = TRUE;
2320 
2321     terminal_window_update_size_to_menu (window);
2322 
2323     /* We have to explicitly call this, since screen-changed is NOT
2324      * emitted for the toplevel the first time!
2325      */
2326     terminal_window_screen_update (window, gtk_widget_get_screen (GTK_WIDGET (window)));
2327 
2328     window_group = gtk_window_group_new ();
2329     gtk_window_group_add_window (window_group, GTK_WINDOW (window));
2330     g_object_unref (window_group);
2331 
2332     terminal_util_set_unique_role (GTK_WINDOW (window), "mate-terminal-window");
2333 }
2334 
2335 static void
terminal_window_class_init(TerminalWindowClass * klass)2336 terminal_window_class_init (TerminalWindowClass *klass)
2337 {
2338     GObjectClass *object_class = G_OBJECT_CLASS (klass);
2339     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
2340 
2341     object_class->dispose = terminal_window_dispose;
2342     object_class->finalize = terminal_window_finalize;
2343 
2344     widget_class->show = terminal_window_show;
2345     widget_class->realize = terminal_window_realize;
2346     widget_class->map_event = terminal_window_map_event;
2347     widget_class->window_state_event = terminal_window_state_event;
2348     widget_class->screen_changed = terminal_window_screen_changed;
2349 }
2350 
2351 static void
terminal_window_dispose(GObject * object)2352 terminal_window_dispose (GObject *object)
2353 {
2354     TerminalWindow *window = TERMINAL_WINDOW (object);
2355     TerminalWindowPrivate *priv = window->priv;
2356     TerminalApp *app;
2357     GdkScreen *screen;
2358     GtkClipboard *clipboard;
2359 
2360     remove_popup_info (window);
2361 
2362     priv->disposed = TRUE;
2363 
2364     if (priv->tabs_menu)
2365     {
2366         g_object_unref (priv->tabs_menu);
2367         priv->tabs_menu = NULL;
2368     }
2369 
2370     if (priv->profiles_action_group != NULL)
2371         disconnect_profiles_from_actions_in_group (priv->profiles_action_group);
2372     if (priv->new_terminal_action_group != NULL)
2373         disconnect_profiles_from_actions_in_group (priv->new_terminal_action_group);
2374 
2375     app = terminal_app_get ();
2376     g_signal_handlers_disconnect_by_func (app,
2377                                           G_CALLBACK (terminal_window_profile_list_changed_cb),
2378                                           window);
2379     g_signal_handlers_disconnect_by_func (app,
2380                                           G_CALLBACK (terminal_window_encoding_list_changed_cb),
2381                                           window);
2382     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
2383     g_signal_handlers_disconnect_by_func (clipboard,
2384                                           G_CALLBACK (update_edit_menu),
2385                                           window);
2386 
2387 #ifdef GDK_WINDOWING_X11
2388     screen = gtk_widget_get_screen (GTK_WIDGET (object));
2389     if (screen && GDK_IS_X11_SCREEN (screen))
2390     {
2391         g_signal_handlers_disconnect_by_func (screen,
2392                                               G_CALLBACK (terminal_window_window_manager_changed_cb),
2393                                               window);
2394     }
2395 #endif
2396 
2397     G_OBJECT_CLASS (terminal_window_parent_class)->dispose (object);
2398 }
2399 
2400 static void
terminal_window_finalize(GObject * object)2401 terminal_window_finalize (GObject *object)
2402 {
2403     TerminalWindow *window = TERMINAL_WINDOW (object);
2404     TerminalWindowPrivate *priv = window->priv;
2405 
2406     g_object_unref (priv->ui_manager);
2407 
2408     if (priv->confirm_close_dialog)
2409         gtk_dialog_response (GTK_DIALOG (priv->confirm_close_dialog),
2410                              GTK_RESPONSE_DELETE_EVENT);
2411 
2412     if (priv->search_find_dialog)
2413         gtk_dialog_response (GTK_DIALOG (priv->search_find_dialog),
2414                              GTK_RESPONSE_DELETE_EVENT);
2415 
2416     G_OBJECT_CLASS (terminal_window_parent_class)->finalize (object);
2417 }
2418 
2419 static gboolean
terminal_window_delete_event(GtkWidget * widget,GdkEvent * event,gpointer data)2420 terminal_window_delete_event (GtkWidget *widget,
2421                               GdkEvent *event,
2422                               gpointer data)
2423 {
2424     return confirm_close_window_or_tab (TERMINAL_WINDOW (widget), NULL);
2425 }
2426 
2427 static gboolean
terminal_window_focus_in_event(GtkWidget * widget,GdkEventFocus * event,gpointer data)2428 terminal_window_focus_in_event (GtkWidget *widget,
2429                                 GdkEventFocus *event,
2430                                 gpointer data)
2431 {
2432   TerminalWindow *window = TERMINAL_WINDOW (widget);
2433   TerminalWindowPrivate *priv = window->priv;
2434 
2435   if (event->in)
2436     priv->focus_time = time(NULL);
2437 
2438   return FALSE;
2439 }
2440 
2441 static void
terminal_window_show(GtkWidget * widget)2442 terminal_window_show (GtkWidget *widget)
2443 {
2444     TerminalWindow *window = TERMINAL_WINDOW (widget);
2445     GtkAllocation widget_allocation;
2446 
2447     gtk_widget_get_allocation (widget, &widget_allocation);
2448 
2449     TerminalWindowPrivate *priv = window->priv;
2450 
2451     if (priv->active_screen != NULL)
2452     {
2453         terminal_window_update_copy_selection (priv->active_screen, window);
2454 #if 0
2455         /* At this point, we have our GdkScreen, and hence the right
2456          * font size, so we can go ahead and size the window. */
2457         terminal_window_update_size (window, priv->active_screen, FALSE);
2458 #endif
2459     }
2460 
2461     terminal_window_update_geometry (window);
2462 
2463     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
2464                            "[window %p] show, size %d : %d at (%d, %d)\n",
2465                            widget,
2466                            widget_allocation.width, widget_allocation.height,
2467                            widget_allocation.x, widget_allocation.y);
2468 
2469     GTK_WIDGET_CLASS (terminal_window_parent_class)->show (widget);
2470 }
2471 
2472 TerminalWindow*
terminal_window_new(void)2473 terminal_window_new (void)
2474 {
2475     return g_object_new (TERMINAL_TYPE_WINDOW, NULL);
2476 }
2477 
2478 /**
2479  * terminal_window_set_is_restored:
2480  * @window:
2481  *
2482  * Marks the window as restored from session.
2483  */
2484 void
terminal_window_set_is_restored(TerminalWindow * window)2485 terminal_window_set_is_restored (TerminalWindow *window)
2486 {
2487     g_return_if_fail (TERMINAL_IS_WINDOW (window));
2488     g_return_if_fail (!gtk_widget_get_mapped (GTK_WIDGET (window)));
2489 
2490     window->priv->clear_demands_attention = TRUE;
2491 }
2492 
2493 static void
profile_set_callback(TerminalScreen * screen,TerminalProfile * old_profile,TerminalWindow * window)2494 profile_set_callback (TerminalScreen *screen,
2495                       TerminalProfile *old_profile,
2496                       TerminalWindow *window)
2497 {
2498     TerminalWindowPrivate *priv = window->priv;
2499 
2500     if (!gtk_widget_get_realized (GTK_WIDGET (window)))
2501         return;
2502 
2503     if (screen != priv->active_screen)
2504         return;
2505 
2506     terminal_window_update_set_profile_menu_active_profile (window);
2507 }
2508 
2509 static void
sync_screen_title(TerminalScreen * screen,GParamSpec * psepc,TerminalWindow * window)2510 sync_screen_title (TerminalScreen *screen,
2511                    GParamSpec *psepc,
2512                    TerminalWindow *window)
2513 {
2514     TerminalWindowPrivate *priv = window->priv;
2515 
2516     if (screen != priv->active_screen)
2517         return;
2518 
2519     gtk_window_set_title (GTK_WINDOW (window), terminal_screen_get_title (screen));
2520 }
2521 
2522 static void
sync_screen_icon_title(TerminalScreen * screen,GParamSpec * psepc,TerminalWindow * window)2523 sync_screen_icon_title (TerminalScreen *screen,
2524                         GParamSpec *psepc,
2525                         TerminalWindow *window)
2526 {
2527     TerminalWindowPrivate *priv = window->priv;
2528 
2529     if (!gtk_widget_get_realized (GTK_WIDGET (window)))
2530         return;
2531 
2532     if (screen != priv->active_screen)
2533         return;
2534 
2535     if (!terminal_screen_get_icon_title_set (screen))
2536         return;
2537 
2538     gdk_window_set_icon_name (gtk_widget_get_window (GTK_WIDGET (window)), terminal_screen_get_icon_title (screen));
2539 
2540     priv->icon_title_set = TRUE;
2541 }
2542 
2543 static void
sync_screen_icon_title_set(TerminalScreen * screen,GParamSpec * psepc,TerminalWindow * window)2544 sync_screen_icon_title_set (TerminalScreen *screen,
2545                             GParamSpec *psepc,
2546                             TerminalWindow *window)
2547 {
2548     TerminalWindowPrivate *priv = window->priv;
2549 
2550     if (!gtk_widget_get_realized (GTK_WIDGET (window)))
2551         return;
2552 
2553     /* No need to restore the title if we never set an icon title */
2554     if (!priv->icon_title_set)
2555         return;
2556 
2557     if (screen != priv->active_screen)
2558         return;
2559 
2560     if (terminal_screen_get_icon_title_set (screen))
2561         return;
2562 
2563     /* Need to reset the icon name */
2564     /* FIXME: Once gtk+ bug 535557 is fixed, use that to unset the icon title. */
2565 
2566     g_object_set_qdata (G_OBJECT (gtk_widget_get_window (GTK_WIDGET (window))),
2567                         g_quark_from_static_string ("gdk-icon-name-set"),
2568                         GUINT_TO_POINTER (FALSE));
2569     priv->icon_title_set = FALSE;
2570 
2571     /* Re-setting the right title will be done by the notify::title handler which comes after this one */
2572 }
2573 
2574 /* Notebook callbacks */
2575 
2576 static void
close_button_clicked_cb(GtkWidget * tab_label,GtkWidget * screen_container)2577 close_button_clicked_cb (GtkWidget *tab_label,
2578                          GtkWidget *screen_container)
2579 {
2580     GtkWidget *toplevel;
2581     TerminalWindow *window;
2582     TerminalScreen *screen;
2583 
2584     toplevel = gtk_widget_get_toplevel (screen_container);
2585     if (!gtk_widget_is_toplevel (toplevel))
2586         return;
2587 
2588     if (!TERMINAL_IS_WINDOW (toplevel))
2589         return;
2590 
2591     window = TERMINAL_WINDOW (toplevel);
2592 
2593     screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (screen_container));
2594     if (confirm_close_window_or_tab (window, screen))
2595         return;
2596 
2597     terminal_window_remove_screen (window, screen);
2598 }
2599 
2600 void
terminal_window_add_screen(TerminalWindow * window,TerminalScreen * screen,int position)2601 terminal_window_add_screen (TerminalWindow *window,
2602                             TerminalScreen *screen,
2603                             int            position)
2604 {
2605     TerminalWindowPrivate *priv = window->priv;
2606     GtkWidget *old_window;
2607     GtkWidget *screen_container, *tab_label;
2608 
2609     old_window = gtk_widget_get_toplevel (GTK_WIDGET (screen));
2610     if (gtk_widget_is_toplevel (old_window) &&
2611             TERMINAL_IS_WINDOW (old_window) &&
2612             TERMINAL_WINDOW (old_window)== window)
2613         return;
2614 
2615     if (TERMINAL_IS_WINDOW (old_window))
2616         terminal_window_remove_screen (TERMINAL_WINDOW (old_window), screen);
2617 
2618     screen_container = terminal_screen_container_new (screen);
2619     gtk_widget_show (screen_container);
2620 
2621     update_tab_visibility (window, +1);
2622 
2623     tab_label = terminal_tab_label_new (screen);
2624     g_signal_connect (tab_label, "close-button-clicked",
2625                       G_CALLBACK (close_button_clicked_cb), screen_container);
2626 
2627     gtk_notebook_insert_page (GTK_NOTEBOOK (priv->notebook),
2628                               screen_container,
2629                               tab_label,
2630                               position);
2631     gtk_container_child_set (GTK_CONTAINER (priv->notebook),
2632                              screen_container,
2633                              "tab-expand", TRUE,
2634                              "tab-fill", TRUE,
2635                              NULL);
2636     gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (priv->notebook),
2637                                       screen_container,
2638                                       TRUE);
2639     gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (priv->notebook),
2640                                      screen_container,
2641                                      TRUE);
2642 }
2643 
2644 void
terminal_window_remove_screen(TerminalWindow * window,TerminalScreen * screen)2645 terminal_window_remove_screen (TerminalWindow *window,
2646                                TerminalScreen *screen)
2647 {
2648     TerminalWindowPrivate *priv = window->priv;
2649     TerminalScreenContainer *screen_container;
2650 
2651     g_return_if_fail (gtk_widget_get_toplevel (GTK_WIDGET (screen)) == GTK_WIDGET (window));
2652 
2653     update_tab_visibility (window, -1);
2654 
2655     screen_container = terminal_screen_container_get_from_screen (screen);
2656     if (detach_tab)
2657     {
2658         gtk_notebook_detach_tab (GTK_NOTEBOOK (priv->notebook),
2659                                  GTK_WIDGET (screen_container));
2660         detach_tab = FALSE;
2661     }
2662     else
2663         gtk_container_remove (GTK_CONTAINER (priv->notebook),
2664                               GTK_WIDGET (screen_container));
2665 }
2666 
2667 void
terminal_window_move_screen(TerminalWindow * source_window,TerminalWindow * dest_window,TerminalScreen * screen,int dest_position)2668 terminal_window_move_screen (TerminalWindow *source_window,
2669                              TerminalWindow *dest_window,
2670                              TerminalScreen *screen,
2671                              int dest_position)
2672 {
2673     TerminalScreenContainer *screen_container;
2674 
2675     g_return_if_fail (TERMINAL_IS_WINDOW (source_window));
2676     g_return_if_fail (TERMINAL_IS_WINDOW (dest_window));
2677     g_return_if_fail (TERMINAL_IS_SCREEN (screen));
2678     g_return_if_fail (gtk_widget_get_toplevel (GTK_WIDGET (screen)) == GTK_WIDGET (source_window));
2679     g_return_if_fail (dest_position >= -1);
2680 
2681     screen_container = terminal_screen_container_get_from_screen (screen);
2682     g_assert (TERMINAL_IS_SCREEN_CONTAINER (screen_container));
2683 
2684     /* We have to ref the screen container as well as the screen,
2685      * because otherwise removing the screen container from the source
2686      * window's notebook will cause the container and its containing
2687      * screen to be gtk_widget_destroy()ed!
2688      */
2689     g_object_ref_sink (screen_container);
2690     g_object_ref_sink (screen);
2691 
2692     detach_tab = TRUE;
2693 
2694     terminal_window_remove_screen (source_window, screen);
2695 
2696     /* Now we can safely remove the screen from the container and let the container die */
2697     gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (screen))), GTK_WIDGET (screen));
2698     g_object_unref (screen_container);
2699 
2700     terminal_window_add_screen (dest_window, screen, dest_position);
2701     gtk_notebook_set_current_page (GTK_NOTEBOOK (dest_window->priv->notebook), dest_position);
2702     g_object_unref (screen);
2703 }
2704 
2705 GList*
terminal_window_list_screen_containers(TerminalWindow * window)2706 terminal_window_list_screen_containers (TerminalWindow *window)
2707 {
2708     TerminalWindowPrivate *priv = window->priv;
2709 
2710     /* We are trusting that GtkNotebook will return pages in order */
2711     return gtk_container_get_children (GTK_CONTAINER (priv->notebook));
2712 }
2713 
2714 void
terminal_window_set_menubar_visible(TerminalWindow * window,gboolean setting)2715 terminal_window_set_menubar_visible (TerminalWindow *window,
2716                                      gboolean        setting)
2717 {
2718     TerminalWindowPrivate *priv = window->priv;
2719     GtkAction *action;
2720 
2721     /* it's been set now, so don't override when adding a screen.
2722      * this side effect must happen before we short-circuit below.
2723      */
2724     priv->use_default_menubar_visibility = FALSE;
2725 
2726     if (setting == priv->menubar_visible)
2727         return;
2728 
2729     priv->menubar_visible = setting;
2730 
2731     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2732     action = gtk_action_group_get_action (priv->action_group, "ViewMenubar");
2733     gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), setting);
2734     G_GNUC_END_IGNORE_DEPRECATIONS;
2735 
2736     g_object_set (priv->menubar, "visible", setting, NULL);
2737 
2738     /* FIXMEchpe: use gtk_widget_get_realized instead? */
2739     if (priv->active_screen)
2740     {
2741         _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
2742                                "[window %p] setting size after toggling menubar visibility\n",
2743                                window);
2744 
2745         terminal_window_update_size (window, priv->active_screen, TRUE);
2746     }
2747 }
2748 
2749 gboolean
terminal_window_get_menubar_visible(TerminalWindow * window)2750 terminal_window_get_menubar_visible (TerminalWindow *window)
2751 {
2752     TerminalWindowPrivate *priv = window->priv;
2753 
2754     return priv->menubar_visible;
2755 }
2756 
2757 GtkWidget *
terminal_window_get_notebook(TerminalWindow * window)2758 terminal_window_get_notebook (TerminalWindow *window)
2759 {
2760     TerminalWindowPrivate *priv = window->priv;
2761 
2762     g_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL);
2763 
2764     return GTK_WIDGET (priv->notebook);
2765 }
2766 
2767 void
terminal_window_update_size(TerminalWindow * window,TerminalScreen * screen,gboolean even_if_mapped)2768 terminal_window_update_size (TerminalWindow *window,
2769                           TerminalScreen *screen,
2770                           gboolean        even_if_mapped)
2771 {
2772     terminal_window_update_size_set_geometry (window, screen,
2773                                               even_if_mapped, NULL);
2774 }
2775 
2776 gboolean
terminal_window_update_size_set_geometry(TerminalWindow * window,TerminalScreen * screen,gboolean even_if_mapped,gchar * geometry_string)2777 terminal_window_update_size_set_geometry (TerminalWindow *window,
2778                                           TerminalScreen *screen,
2779                                           gboolean        even_if_mapped,
2780                                           gchar          *geometry_string)
2781 {
2782     TerminalWindowPrivate *priv = window->priv;
2783     GtkWidget *widget;
2784     GtkWidget *app;
2785     gboolean result;
2786     int geom_result;
2787     gint force_pos_x = 0, force_pos_y = 0;
2788     unsigned int force_grid_width = 0, force_grid_height = 0;
2789     int grid_width, grid_height;
2790     gint pixel_width, pixel_height;
2791     GdkWindow *gdk_window;
2792     GdkGravity pos_gravity;
2793 
2794     gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
2795     result = TRUE;
2796 
2797     if (gdk_window != NULL &&
2798         (gdk_window_get_state (gdk_window) &
2799          (GDK_WINDOW_STATE_MAXIMIZED | GDK_WINDOW_STATE_TILED)))
2800     {
2801         /* Don't adjust the size of maximized or tiled (snapped, half-maximized)
2802          * windows: if we do, there will be ugly gaps of up to 1 character cell
2803          * around otherwise tiled windows. */
2804         return result;
2805     }
2806 
2807     /* be sure our geometry is up-to-date */
2808     terminal_window_update_geometry (window);
2809 
2810     if (GTK_IS_WIDGET (screen))
2811         widget = GTK_WIDGET (screen);
2812     else
2813         widget = GTK_WIDGET (window);
2814 
2815     app = gtk_widget_get_toplevel (widget);
2816     g_assert (app != NULL);
2817 
2818     terminal_screen_get_size (screen, &grid_width, &grid_height);
2819     if (geometry_string != NULL)
2820     {
2821         geom_result = terminal_window_XParseGeometry (geometry_string,
2822                                                       &force_pos_x,
2823                                                       &force_pos_y,
2824                                                       &force_grid_width,
2825                                                       &force_grid_height);
2826         if (geom_result == NoValue)
2827             result = FALSE;
2828     }
2829     else
2830         geom_result = NoValue;
2831 
2832     if ((geom_result & WidthValue) != 0)
2833         grid_width = force_grid_width;
2834     if ((geom_result & HeightValue) != 0)
2835         grid_height = force_grid_height;
2836 
2837     /* the "old" struct members were updated by update_geometry */
2838     pixel_width = priv->old_chrome_width + grid_width * priv->old_char_width;
2839     pixel_height = priv->old_chrome_height + grid_height * priv->old_char_height;
2840 
2841     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
2842                            "[window %p] size is %dx%d cells of %dx%d px\n",
2843                            window, grid_width, grid_height,
2844                            priv->old_char_width, priv->old_char_height);
2845 
2846     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
2847                            "[window %p] %dx%d + %dx%d = %dx%d\n",
2848                            window, grid_width * priv->old_char_width,
2849                            grid_height * priv->old_char_height,
2850                            priv->old_chrome_width, priv->old_chrome_height,
2851                            pixel_width, pixel_height);
2852 
2853     pos_gravity = GDK_GRAVITY_NORTH_WEST;
2854     if ((geom_result & XNegative) != 0 && (geom_result & YNegative) != 0)
2855         pos_gravity = GDK_GRAVITY_SOUTH_EAST;
2856     else if ((geom_result & XNegative) != 0)
2857         pos_gravity = GDK_GRAVITY_NORTH_EAST;
2858     else if ((geom_result & YNegative) != 0)
2859         pos_gravity = GDK_GRAVITY_SOUTH_WEST;
2860 
2861     if ((geom_result & XValue) == 0)
2862         force_pos_x = 0;
2863     if ((geom_result & YValue) == 0)
2864         force_pos_y = 0;
2865 
2866     if (pos_gravity == GDK_GRAVITY_SOUTH_EAST ||
2867         pos_gravity == GDK_GRAVITY_NORTH_EAST)
2868         force_pos_x = WidthOfScreen (gdk_x11_screen_get_xscreen (gtk_widget_get_screen (app))) -
2869                       pixel_width + force_pos_x;
2870     if (pos_gravity == GDK_GRAVITY_SOUTH_WEST ||
2871         pos_gravity == GDK_GRAVITY_SOUTH_EAST)
2872         force_pos_y = HeightOfScreen (gdk_x11_screen_get_xscreen (gtk_widget_get_screen (app))) -
2873                       pixel_height + force_pos_y;
2874 
2875     /* we don't let you put a window offscreen; maybe some people would
2876      * prefer to be able to, but it's kind of a bogus thing to do.
2877      */
2878     if (force_pos_x < 0)
2879         force_pos_x = 0;
2880     if (force_pos_y < 0)
2881         force_pos_y = 0;
2882 
2883     if (even_if_mapped && gtk_widget_get_mapped (app))
2884         gtk_window_resize (GTK_WINDOW (app), pixel_width, pixel_height);
2885     else
2886         gtk_window_set_default_size (GTK_WINDOW (app), pixel_width, pixel_height);
2887 
2888     if ((geom_result & XValue) != 0 || (geom_result & YValue) != 0)
2889     {
2890         gtk_window_set_gravity (GTK_WINDOW (app), pos_gravity);
2891         gtk_window_move (GTK_WINDOW (app), force_pos_x, force_pos_y);
2892     }
2893 
2894     return result;
2895 }
2896 
2897 void
terminal_window_switch_screen(TerminalWindow * window,TerminalScreen * screen)2898 terminal_window_switch_screen (TerminalWindow *window,
2899                                TerminalScreen *screen)
2900 {
2901     TerminalWindowPrivate *priv = window->priv;
2902     TerminalScreenContainer *screen_container;
2903     int page_num;
2904 
2905     screen_container = terminal_screen_container_get_from_screen (screen);
2906     g_assert (TERMINAL_IS_SCREEN_CONTAINER (screen_container));
2907     page_num = gtk_notebook_page_num (GTK_NOTEBOOK (priv->notebook),
2908                                       GTK_WIDGET (screen_container));
2909     gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), page_num);
2910 }
2911 
2912 TerminalScreen*
terminal_window_get_active(TerminalWindow * window)2913 terminal_window_get_active (TerminalWindow *window)
2914 {
2915     TerminalWindowPrivate *priv = window->priv;
2916 
2917     return priv->active_screen;
2918 }
2919 
2920 static gboolean
notebook_button_press_cb(GtkWidget * widget,GdkEventButton * event,GSettings * settings)2921 notebook_button_press_cb (GtkWidget *widget,
2922                           GdkEventButton *event,
2923                           GSettings *settings)
2924 {
2925     TerminalWindow *window = TERMINAL_WINDOW (gtk_widget_get_toplevel (widget));
2926     TerminalWindowPrivate *priv = window->priv;
2927     GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2928     GtkWidget *tab;
2929     GtkWidget *menu;
2930     GtkAction *action;
2931     int tab_clicked;
2932     int page_num;
2933     int before_pages;
2934     int later_pages;
2935 
2936     if ((event->type == GDK_BUTTON_PRESS && event->button == 2) &&
2937             (g_settings_get_boolean (settings, "middle-click-closes-tabs")))
2938     {
2939         tab_clicked = find_tab_num_at_pos (notebook,
2940                                            (int)event->x_root,
2941                                            (int)event->y_root);
2942         if (tab_clicked >= 0)
2943         {
2944             before_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
2945             page_num = gtk_notebook_get_current_page (notebook);
2946             gtk_notebook_set_current_page (notebook, tab_clicked);
2947             TerminalScreen *active_screen = priv->active_screen;
2948 
2949                 if (!(confirm_close_window_or_tab (window, active_screen)))
2950                 {
2951                     update_tab_visibility (window, -1);
2952                     gtk_notebook_remove_page(notebook, tab_clicked);
2953                 }
2954 
2955                 later_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
2956 
2957                 if (before_pages > later_pages) {
2958                     if (tab_clicked > page_num)
2959                         gtk_notebook_set_current_page (notebook, page_num);
2960                     else if (tab_clicked < page_num)
2961                         gtk_notebook_set_current_page (notebook, page_num - 1);
2962                 }
2963                 else
2964                     gtk_notebook_set_current_page (notebook, page_num);
2965 
2966         }
2967     }
2968 
2969     if (event->type != GDK_BUTTON_PRESS ||
2970             event->button != 3 ||
2971             (event->state & gtk_accelerator_get_default_mod_mask ()) != 0)
2972         return FALSE;
2973 
2974     tab_clicked = find_tab_num_at_pos (notebook,
2975                                        (int)event->x_root,
2976                                        (int)event->y_root);
2977     if (tab_clicked < 0)
2978         return FALSE;
2979 
2980     /* switch to the page the mouse is over */
2981     gtk_notebook_set_current_page (notebook, tab_clicked);
2982 
2983     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
2984     action = gtk_action_group_get_action (priv->action_group, "NotebookPopup");
2985     gtk_action_activate (action);
2986     G_GNUC_END_IGNORE_DEPRECATIONS;
2987 
2988     menu = gtk_ui_manager_get_widget (priv->ui_manager, "/NotebookPopup");
2989     if (gtk_menu_get_attach_widget (GTK_MENU (menu)))
2990       gtk_menu_detach (GTK_MENU (menu));
2991     tab = gtk_notebook_get_nth_page (notebook, tab_clicked);
2992     gtk_menu_attach_to_widget (GTK_MENU (menu), tab, NULL);
2993     gtk_menu_popup_at_pointer (GTK_MENU (menu), NULL);
2994 
2995     return TRUE;
2996 }
2997 
2998 static gboolean
window_key_press_cb(GtkWidget * widget,GdkEventKey * event,GSettings * settings)2999 window_key_press_cb (GtkWidget *widget,
3000                      GdkEventKey *event,
3001                      GSettings *settings)
3002 {
3003     if (g_settings_get_boolean (settings, "ctrl-tab-switch-tabs") &&
3004         event->state & GDK_CONTROL_MASK)
3005     {
3006         TerminalWindow *window = TERMINAL_WINDOW (widget);
3007         TerminalWindowPrivate *priv = window->priv;
3008         GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook);
3009 
3010         int pages = gtk_notebook_get_n_pages (notebook);
3011         int page_num = gtk_notebook_get_current_page (notebook);
3012 
3013         if (event->keyval == GDK_KEY_ISO_Left_Tab)
3014         {
3015             if (page_num != 0)
3016                 gtk_notebook_prev_page (notebook);
3017             else
3018                 gtk_notebook_set_current_page (notebook, (pages - 1));
3019             return TRUE;
3020         }
3021 
3022         if (event->keyval == GDK_KEY_Tab)
3023         {
3024             if (page_num != (pages -1))
3025                 gtk_notebook_next_page (notebook);
3026             else
3027                 gtk_notebook_set_current_page (notebook, 0);
3028             return TRUE;
3029         }
3030     }
3031     return FALSE;
3032 }
3033 
3034 static gboolean
notebook_popup_menu_cb(GtkWidget * widget,TerminalWindow * window)3035 notebook_popup_menu_cb (GtkWidget *widget,
3036                         TerminalWindow *window)
3037 {
3038     TerminalWindowPrivate *priv = window->priv;
3039     GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook);
3040     GtkWidget *focus_widget, *tab, *tab_label, *menu;
3041     GtkAction *action;
3042     int page_num;
3043 
3044     focus_widget = gtk_window_get_focus (GTK_WINDOW (window));
3045     /* Only respond if the notebook is the actual focus */
3046     if (focus_widget != priv->notebook)
3047         return FALSE;
3048 
3049     page_num = gtk_notebook_get_current_page (notebook);
3050     tab = gtk_notebook_get_nth_page (notebook, page_num);
3051     tab_label = gtk_notebook_get_tab_label (notebook, tab);
3052 
3053     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
3054     action = gtk_action_group_get_action (priv->action_group, "NotebookPopup");
3055     gtk_action_activate (action);
3056     G_GNUC_END_IGNORE_DEPRECATIONS;
3057 
3058     menu = gtk_ui_manager_get_widget (priv->ui_manager, "/NotebookPopup");
3059     if (gtk_menu_get_attach_widget (GTK_MENU (menu)))
3060       gtk_menu_detach (GTK_MENU (menu));
3061     gtk_menu_attach_to_widget (GTK_MENU (menu), tab_label, NULL);
3062     gtk_menu_popup_at_widget (GTK_MENU (menu),
3063                               tab_label,
3064                               GDK_GRAVITY_SOUTH_WEST,
3065                               GDK_GRAVITY_NORTH_WEST,
3066                               NULL);
3067     gtk_menu_shell_select_first (GTK_MENU_SHELL (menu), FALSE);
3068 
3069     return TRUE;
3070 }
3071 
3072 static void
notebook_page_selected_callback(GtkWidget * notebook,GtkWidget * page_widget,guint page_num,TerminalWindow * window)3073 notebook_page_selected_callback (GtkWidget       *notebook,
3074                                  GtkWidget       *page_widget,
3075                                  guint            page_num,
3076                                  TerminalWindow  *window)
3077 {
3078     TerminalWindowPrivate *priv = window->priv;
3079     GtkWidget *widget;
3080     TerminalScreen *screen;
3081     int old_grid_width, old_grid_height;
3082 
3083     _terminal_debug_print (TERMINAL_DEBUG_MDI,
3084                            "[window %p] MDI: page-selected %d\n",
3085                            window, page_num);
3086 
3087     if (priv->disposed)
3088         return;
3089 
3090     screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (page_widget));
3091     widget = GTK_WIDGET (screen);
3092     g_assert (screen != NULL);
3093 
3094     _terminal_debug_print (TERMINAL_DEBUG_MDI,
3095                            "[window %p] MDI: setting active tab to screen %p (old active screen %p)\n",
3096                            window, screen, priv->active_screen);
3097 
3098     if (priv->active_screen == screen)
3099         return;
3100 
3101     if (priv->active_screen != NULL)
3102     {
3103         terminal_screen_get_size (priv->active_screen, &old_grid_width, &old_grid_height);
3104 
3105         /* This is so that we maintain the same grid */
3106         vte_terminal_set_size (VTE_TERMINAL (screen), old_grid_width, old_grid_height);
3107     }
3108 
3109     /* Workaround to remove gtknotebook's feature of computing its size based on
3110      * all pages. When the widget is hidden, its size will not be taken into
3111      * account.
3112      */
3113     if (priv->active_screen)
3114         gtk_widget_hide (GTK_WIDGET (priv->active_screen)); /* FIXME */
3115 
3116     /* Make sure that the widget is no longer hidden due to the workaround */
3117     gtk_widget_show (widget);
3118 
3119     priv->active_screen = screen;
3120 
3121     /* Override menubar setting if it wasn't restored from session */
3122     if (priv->use_default_menubar_visibility)
3123     {
3124         gboolean setting =
3125             terminal_profile_get_property_boolean (terminal_screen_get_profile (screen), TERMINAL_PROFILE_DEFAULT_SHOW_MENUBAR);
3126 
3127         terminal_window_set_menubar_visible (window, setting);
3128     }
3129 
3130     sync_screen_icon_title_set (screen, NULL, window);
3131     sync_screen_icon_title (screen, NULL, window);
3132     sync_screen_title (screen, NULL, window);
3133 
3134     /* set size of window to current grid size */
3135     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
3136                            "[window %p] setting size after flipping notebook pages\n",
3137                            window);
3138     terminal_window_update_size (window, screen, TRUE);
3139 
3140     terminal_window_update_tabs_menu_sensitivity (window);
3141     terminal_window_update_encoding_menu_active_encoding (window);
3142     terminal_window_update_set_profile_menu_active_profile (window);
3143     terminal_window_update_copy_sensitivity (screen, window);
3144     terminal_window_update_zoom_sensitivity (window);
3145     terminal_window_update_search_sensitivity (screen, window);
3146 }
3147 
3148 static void
notebook_page_added_callback(GtkWidget * notebook,GtkWidget * container,guint page_num,TerminalWindow * window)3149 notebook_page_added_callback (GtkWidget       *notebook,
3150                               GtkWidget       *container,
3151                               guint            page_num,
3152                               TerminalWindow  *window)
3153 {
3154     TerminalWindowPrivate *priv = window->priv;
3155     TerminalScreen *screen;
3156     int pages;
3157 
3158     screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container));
3159 
3160     _terminal_debug_print (TERMINAL_DEBUG_MDI,
3161                            "[window %p] MDI: screen %p inserted\n",
3162                            window, screen);
3163 
3164     g_signal_connect (G_OBJECT (screen),
3165                       "profile-set",
3166                       G_CALLBACK (profile_set_callback),
3167                       window);
3168 
3169     /* FIXME: only connect on the active screen, not all screens! */
3170     g_signal_connect (screen, "notify::title",
3171                       G_CALLBACK (sync_screen_title), window);
3172     g_signal_connect (screen, "notify::icon-title",
3173                       G_CALLBACK (sync_screen_icon_title), window);
3174     g_signal_connect (screen, "notify::icon-title-set",
3175                       G_CALLBACK (sync_screen_icon_title_set), window);
3176     g_signal_connect (screen, "selection-changed",
3177                       G_CALLBACK (terminal_window_update_copy_sensitivity), window);
3178 
3179     g_signal_connect (screen, "show-popup-menu",
3180                       G_CALLBACK (screen_show_popup_menu_callback), window);
3181     g_signal_connect (screen, "match-clicked",
3182                       G_CALLBACK (screen_match_clicked_cb), window);
3183     g_signal_connect (screen, "resize-window",
3184                       G_CALLBACK (screen_resize_window_cb), window);
3185 
3186     g_signal_connect (screen, "close-screen",
3187                       G_CALLBACK (screen_close_cb), window);
3188 
3189     update_tab_visibility (window, 0);
3190     terminal_window_update_tabs_menu_sensitivity (window);
3191     terminal_window_update_search_sensitivity (screen, window);
3192 
3193 #if 0
3194     /* FIXMEchpe: wtf is this doing? */
3195 
3196     /* If we have an active screen, match its size and zoom */
3197     if (priv->active_screen)
3198     {
3199         int current_width, current_height;
3200         double scale;
3201 
3202         terminal_screen_get_size (priv->active_screen, &current_width, &current_height);
3203         vte_terminal_set_size (VTE_TERMINAL (screen), current_width, current_height);
3204 
3205         scale = terminal_screen_get_font_scale (priv->active_screen);
3206         terminal_screen_set_font_scale (screen, scale);
3207     }
3208 #endif
3209 
3210     if (priv->present_on_insert)
3211     {
3212         gtk_window_present_with_time (GTK_WINDOW (window), gtk_get_current_event_time ());
3213         priv->present_on_insert = FALSE;
3214     }
3215     pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
3216     if (pages == 2) terminal_window_update_size (window, priv->active_screen, TRUE);
3217 }
3218 
3219 static void
notebook_page_removed_callback(GtkWidget * notebook,GtkWidget * container,guint page_num,TerminalWindow * window)3220 notebook_page_removed_callback (GtkWidget       *notebook,
3221                                 GtkWidget       *container,
3222                                 guint            page_num,
3223                                 TerminalWindow  *window)
3224 {
3225     TerminalWindowPrivate *priv = window->priv;
3226     TerminalScreen *screen;
3227     int pages;
3228 
3229     if (priv->disposed)
3230         return;
3231 
3232     screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (container));
3233 
3234     _terminal_debug_print (TERMINAL_DEBUG_MDI,
3235                            "[window %p] MDI: screen %p removed\n",
3236                            window, screen);
3237 
3238     g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
3239                                           G_CALLBACK (profile_set_callback),
3240                                           window);
3241 
3242     g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
3243                                           G_CALLBACK (sync_screen_title),
3244                                           window);
3245 
3246     g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
3247                                           G_CALLBACK (sync_screen_icon_title),
3248                                           window);
3249 
3250     g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
3251                                           G_CALLBACK (sync_screen_icon_title_set),
3252                                           window);
3253 
3254     g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
3255                                           G_CALLBACK (terminal_window_update_copy_sensitivity),
3256                                           window);
3257 
3258     g_signal_handlers_disconnect_by_func (screen,
3259                                           G_CALLBACK (screen_show_popup_menu_callback),
3260                                           window);
3261 
3262     g_signal_handlers_disconnect_by_func (screen,
3263                                           G_CALLBACK (screen_match_clicked_cb),
3264                                           window);
3265     g_signal_handlers_disconnect_by_func (screen,
3266                                           G_CALLBACK (screen_resize_window_cb),
3267                                           window);
3268 
3269     g_signal_handlers_disconnect_by_func (screen,
3270                                           G_CALLBACK (screen_close_cb),
3271                                           window);
3272 
3273     terminal_window_update_tabs_menu_sensitivity (window);
3274     update_tab_visibility (window, 0);
3275     terminal_window_update_search_sensitivity (screen, window);
3276 
3277     pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook));
3278     if (pages == 1)
3279     {
3280         terminal_window_update_size (window, priv->active_screen, TRUE);
3281     }
3282     else if (pages == 0)
3283     {
3284         gtk_widget_destroy (GTK_WIDGET (window));
3285     }
3286 }
3287 
3288 void
terminal_window_update_copy_selection(TerminalScreen * screen,TerminalWindow * window)3289 terminal_window_update_copy_selection (TerminalScreen *screen,
3290         TerminalWindow *window)
3291 {
3292     TerminalWindowPrivate *priv = window->priv;
3293     priv->copy_selection =
3294         terminal_profile_get_property_boolean (terminal_screen_get_profile (screen),
3295             TERMINAL_PROFILE_COPY_SELECTION);
3296 }
3297 
3298 static gboolean
notebook_scroll_event_cb(GtkWidget * widget,GdkEventScroll * event,TerminalWindow * window)3299 notebook_scroll_event_cb (GtkWidget      *widget,
3300                           GdkEventScroll *event,
3301                           TerminalWindow *window)
3302 {
3303   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3304   GtkWidget *child, *event_widget, *action_widget;
3305 
3306   child = gtk_notebook_get_nth_page (notebook, gtk_notebook_get_current_page (notebook));
3307   if (child == NULL)
3308     return FALSE;
3309 
3310   event_widget = gtk_get_event_widget ((GdkEvent *) event);
3311 
3312   /* Ignore scroll events from the content of the page */
3313   if (event_widget == NULL ||
3314       event_widget == child ||
3315       gtk_widget_is_ancestor (event_widget, child))
3316     return FALSE;
3317 
3318   /* And also from the action widgets */
3319   action_widget = gtk_notebook_get_action_widget (notebook, GTK_PACK_START);
3320   if (event_widget == action_widget ||
3321       (action_widget != NULL && gtk_widget_is_ancestor (event_widget, action_widget)))
3322     return FALSE;
3323   action_widget = gtk_notebook_get_action_widget (notebook, GTK_PACK_END);
3324   if (event_widget == action_widget ||
3325       (action_widget != NULL && gtk_widget_is_ancestor (event_widget, action_widget)))
3326     return FALSE;
3327 
3328   switch (event->direction) {
3329     case GDK_SCROLL_RIGHT:
3330     case GDK_SCROLL_DOWN:
3331       gtk_notebook_next_page (notebook);
3332       break;
3333     case GDK_SCROLL_LEFT:
3334     case GDK_SCROLL_UP:
3335       gtk_notebook_prev_page (notebook);
3336       break;
3337     case GDK_SCROLL_SMOOTH:
3338       switch (gtk_notebook_get_tab_pos (notebook)) {
3339         case GTK_POS_LEFT:
3340         case GTK_POS_RIGHT:
3341           if (event->delta_y > 0)
3342             gtk_notebook_next_page (notebook);
3343           else if (event->delta_y < 0)
3344             gtk_notebook_prev_page (notebook);
3345           break;
3346         case GTK_POS_TOP:
3347         case GTK_POS_BOTTOM:
3348           if (event->delta_x > 0)
3349             gtk_notebook_next_page (notebook);
3350           else if (event->delta_x < 0)
3351             gtk_notebook_prev_page (notebook);
3352           break;
3353       }
3354       break;
3355     }
3356 
3357   return TRUE;
3358 }
3359 
3360 void
terminal_window_update_geometry(TerminalWindow * window)3361 terminal_window_update_geometry (TerminalWindow *window)
3362 {
3363     TerminalWindowPrivate *priv = window->priv;
3364     GtkWidget *widget;
3365     GdkGeometry hints;
3366     GtkBorder padding;
3367     GtkRequisition toplevel_request, vbox_request, widget_request;
3368     int grid_width, grid_height;
3369     int char_width, char_height;
3370     int chrome_width, chrome_height;
3371 
3372     if (priv->active_screen == NULL)
3373         return;
3374 
3375     widget = GTK_WIDGET (priv->active_screen);
3376 
3377     /* We set geometry hints from the active term; best thing
3378      * I can think of to do. Other option would be to try to
3379      * get some kind of union of all hints from all terms in the
3380      * window, but that doesn't make too much sense.
3381      */
3382     terminal_screen_get_cell_size (priv->active_screen, &char_width, &char_height);
3383 
3384     terminal_screen_get_size (priv->active_screen, &grid_width, &grid_height);
3385     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "%dx%d cells of %dx%d px = %dx%d px\n",
3386                            grid_width, grid_height, char_width, char_height,
3387                            char_width * grid_width, char_height * grid_height);
3388 
3389     gtk_style_context_get_padding(gtk_widget_get_style_context (widget),
3390                                   gtk_widget_get_state_flags (widget),
3391                                   &padding);
3392 
3393     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "padding = %dx%d px\n",
3394                            padding.left + padding.right,
3395                            padding.top + padding.bottom);
3396 
3397     gtk_widget_get_preferred_size (priv->main_vbox, NULL, &vbox_request);
3398     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "content area requests %dx%d px\n",
3399                            vbox_request.width, vbox_request.height);
3400 
3401     gtk_widget_get_preferred_size (GTK_WIDGET (window), NULL, &toplevel_request);
3402     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "window requests %dx%d px\n",
3403                            toplevel_request.width, toplevel_request.height);
3404 
3405     chrome_width = vbox_request.width - (char_width * grid_width);
3406     chrome_height = vbox_request.height - (char_height * grid_height);
3407     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "chrome: %dx%d px\n",
3408                            chrome_width, chrome_height);
3409 
3410     gtk_widget_get_preferred_size (widget, NULL, &widget_request);
3411     _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "terminal widget requests %dx%d px\n",
3412                            widget_request.width, widget_request.height);
3413 
3414     if (char_width != priv->old_char_width ||
3415              char_height != priv->old_char_height ||
3416              padding.left + padding.right != priv->old_padding_width ||
3417              padding.top + padding.bottom != priv->old_padding_height ||
3418              chrome_width != priv->old_chrome_width ||
3419              chrome_height != priv->old_chrome_height ||
3420              widget != GTK_WIDGET (priv->old_geometry_widget))
3421     {
3422         hints.base_width = chrome_width;
3423         hints.base_height = chrome_height;
3424 
3425 #define MIN_WIDTH_CHARS 4
3426 #define MIN_HEIGHT_CHARS 1
3427 
3428         hints.width_inc = char_width;
3429         hints.height_inc = char_height;
3430 
3431         /* min size is min size of the whole window, remember. */
3432         hints.min_width = hints.base_width + hints.width_inc * MIN_WIDTH_CHARS;
3433         hints.min_height = hints.base_height + hints.height_inc * MIN_HEIGHT_CHARS;
3434 
3435         gtk_window_set_geometry_hints (GTK_WINDOW (window),
3436                                        NULL,
3437                                        &hints,
3438                                        GDK_HINT_RESIZE_INC |
3439                                        GDK_HINT_MIN_SIZE |
3440                                        GDK_HINT_BASE_SIZE);
3441 
3442         _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
3443                                "[window %p] hints: base %dx%d min %dx%d inc %d %d\n",
3444                                window,
3445                                hints.base_width,
3446                                hints.base_height,
3447                                hints.min_width,
3448                                hints.min_height,
3449                                hints.width_inc,
3450                                hints.height_inc);
3451 
3452         priv->old_geometry_widget = widget;
3453     }
3454     else
3455     {
3456         _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
3457                                "[window %p] hints: increment unchanged, not setting\n",
3458                                window);
3459     }
3460 
3461     /* We need these for the size calculation in terminal_window_update_size(),
3462      * so we set them unconditionally. */
3463     priv->old_char_width = char_width;
3464     priv->old_char_height = char_height;
3465     priv->old_chrome_width = chrome_width;
3466     priv->old_chrome_height = chrome_height;
3467     priv->old_padding_width = padding.left + padding.right;
3468     priv->old_padding_height = padding.top + padding.bottom;
3469 }
3470 
3471 static void
file_new_window_callback(GtkAction * action,TerminalWindow * window)3472 file_new_window_callback (GtkAction *action,
3473                           TerminalWindow *window)
3474 {
3475     TerminalWindowPrivate *priv = window->priv;
3476     TerminalApp *app;
3477     TerminalWindow *new_window;
3478     TerminalProfile *profile;
3479     char *new_working_directory;
3480 
3481     app = terminal_app_get ();
3482 
3483     profile = g_object_get_data (G_OBJECT (action), PROFILE_DATA_KEY);
3484     if (!profile)
3485         profile = terminal_screen_get_profile (priv->active_screen);
3486     if (!profile)
3487         profile = terminal_app_get_profile_for_new_term (app);
3488     if (!profile)
3489         return;
3490 
3491     if (_terminal_profile_get_forgotten (profile))
3492         return;
3493 
3494     new_window = terminal_app_new_window (app, gtk_widget_get_screen (GTK_WIDGET (window)));
3495 
3496     new_working_directory = terminal_screen_get_current_dir_with_fallback (priv->active_screen);
3497     terminal_app_new_terminal (app, new_window, profile,
3498                                NULL, NULL,
3499                                new_working_directory,
3500                                terminal_screen_get_initial_environment (priv->active_screen),
3501                                1.0);
3502     g_free (new_working_directory);
3503 
3504     gtk_window_present (GTK_WINDOW (new_window));
3505 }
3506 
3507 static void
file_new_tab_callback(GtkAction * action,TerminalWindow * window)3508 file_new_tab_callback (GtkAction *action,
3509                        TerminalWindow *window)
3510 {
3511     TerminalWindowPrivate *priv = window->priv;
3512     TerminalApp *app;
3513     TerminalProfile *profile;
3514     char *new_working_directory;
3515 
3516     app = terminal_app_get ();
3517     profile = g_object_get_data (G_OBJECT (action), PROFILE_DATA_KEY);
3518     if (!profile)
3519         profile = terminal_screen_get_profile (priv->active_screen);
3520     if (!profile)
3521         profile = terminal_app_get_profile_for_new_term (app);
3522     if (!profile)
3523         return;
3524 
3525     if (_terminal_profile_get_forgotten (profile))
3526         return;
3527 
3528     new_working_directory = terminal_screen_get_current_dir_with_fallback (priv->active_screen);
3529     terminal_app_new_terminal (app, window, profile,
3530                                NULL, NULL,
3531                                new_working_directory,
3532                                terminal_screen_get_initial_environment (priv->active_screen),
3533                                1.0);
3534     g_free (new_working_directory);
3535 }
3536 
3537 static void
confirm_close_response_cb(GtkWidget * dialog,int response,TerminalWindow * window)3538 confirm_close_response_cb (GtkWidget *dialog,
3539                            int response,
3540                            TerminalWindow *window)
3541 {
3542     TerminalScreen *screen;
3543 
3544     screen = g_object_get_data (G_OBJECT (dialog), "close-screen");
3545 
3546     gtk_widget_destroy (dialog);
3547 
3548     if (response != GTK_RESPONSE_ACCEPT)
3549         return;
3550 
3551     if (screen)
3552         terminal_window_remove_screen (window, screen);
3553     else
3554         gtk_widget_destroy (GTK_WIDGET (window));
3555 }
3556 
3557 /* Returns: TRUE if closing needs to wait until user confirmation;
3558  * FALSE if the terminal or window can close immediately.
3559  */
3560 static gboolean
confirm_close_window_or_tab(TerminalWindow * window,TerminalScreen * screen)3561 confirm_close_window_or_tab (TerminalWindow *window,
3562                              TerminalScreen *screen)
3563 {
3564     GtkBuilder *builder;
3565     TerminalWindowPrivate *priv = window->priv;
3566     GtkWidget *dialog;
3567     gboolean do_confirm;
3568     gboolean has_processes;
3569     int n_tabs;
3570     char *confirm_msg;
3571 
3572     do_confirm = g_settings_get_boolean (settings_global, "confirm-window-close");
3573 
3574     if (!do_confirm)
3575         return FALSE;
3576 
3577     if (screen)
3578     {
3579         has_processes = terminal_screen_has_foreground_process (screen);
3580         n_tabs = 1;
3581     }
3582     else
3583     {
3584         GList *tabs, *t;
3585 
3586         do_confirm = FALSE;
3587 
3588         tabs = terminal_window_list_screen_containers (window);
3589         n_tabs = g_list_length (tabs);
3590 
3591         for (t = tabs; t != NULL; t = t->next)
3592         {
3593             TerminalScreen *terminal_screen;
3594 
3595             terminal_screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (t->data));
3596             has_processes = terminal_screen_has_foreground_process (terminal_screen);
3597             if (has_processes)
3598                 break;
3599         }
3600         g_list_free (tabs);
3601     }
3602 
3603 
3604     if (has_processes)
3605     {
3606         if (n_tabs > 1)
3607             confirm_msg = _("There are still processes running in some terminals in this window.\n"
3608                             "Closing the window will kill all of them.");
3609         else
3610             confirm_msg = _("There is still a process running in this terminal.\n"
3611                             "Closing the terminal will kill it.");
3612     } else if (n_tabs > 1)
3613             confirm_msg = _("There are multiple tabs open in this window.");
3614     else
3615         return FALSE;
3616 
3617 
3618     builder = gtk_builder_new_from_resource (TERMINAL_RESOURCES_PATH_PREFIX G_DIR_SEPARATOR_S "ui/confirm-close-dialog.ui");
3619     priv->confirm_close_dialog = dialog = GTK_WIDGET (gtk_builder_get_object (builder, "confirm_close_dialog"));
3620     if (n_tabs > 1) {
3621         gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (builder, "question_text")), _("Close this window?"));
3622         gtk_button_set_label (GTK_BUTTON (gtk_builder_get_object (builder, "button_close")), _("C_lose Window"));
3623     } else {
3624         gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (builder, "question_text")), _("Close this terminal?"));
3625         gtk_button_set_label (GTK_BUTTON (gtk_builder_get_object (builder, "button_close")), _("C_lose Terminal"));
3626     }
3627     gtk_label_set_text (GTK_LABEL (gtk_builder_get_object (builder, "description_text")), confirm_msg);
3628     g_object_unref (builder);
3629 
3630     g_object_set_data (G_OBJECT (dialog), "close-screen", screen);
3631 
3632     g_signal_connect (dialog, "destroy",
3633                       G_CALLBACK (gtk_widget_destroyed), &priv->confirm_close_dialog);
3634     g_signal_connect (dialog, "response",
3635                       G_CALLBACK (confirm_close_response_cb), window);
3636 
3637     gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
3638     gtk_window_set_title (GTK_WINDOW (dialog), "");
3639     gtk_window_present (GTK_WINDOW (dialog));
3640 
3641     return TRUE;
3642 }
3643 
3644 static void
file_close_window_callback(GtkAction * action,TerminalWindow * window)3645 file_close_window_callback (GtkAction *action,
3646                             TerminalWindow *window)
3647 {
3648     if (confirm_close_window_or_tab (window, NULL))
3649         return;
3650 
3651     gtk_widget_destroy (GTK_WIDGET (window));
3652 }
3653 
3654 #ifdef ENABLE_SAVE
3655 static void
save_contents_dialog_on_response(GtkDialog * dialog,gint response_id,gpointer terminal)3656 save_contents_dialog_on_response (GtkDialog *dialog, gint response_id, gpointer terminal)
3657 {
3658     GtkWindow *parent;
3659     gchar *filename_uri = NULL;
3660     GFile *file;
3661     GOutputStream *stream;
3662     GError *error = NULL;
3663 
3664     if (response_id != GTK_RESPONSE_ACCEPT)
3665     {
3666         gtk_widget_destroy (GTK_WIDGET (dialog));
3667         return;
3668     }
3669 
3670     parent = (GtkWindow*) gtk_widget_get_ancestor (GTK_WIDGET (terminal), GTK_TYPE_WINDOW);
3671     filename_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
3672 
3673     gtk_widget_destroy (GTK_WIDGET (dialog));
3674 
3675     if (filename_uri == NULL)
3676         return;
3677 
3678     file = g_file_new_for_uri (filename_uri);
3679     stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
3680 
3681     if (stream)
3682     {
3683         /* FIXME
3684          * Should be replaced with the async version when vte implements that.
3685          */
3686         vte_terminal_write_contents_sync (terminal, stream,
3687                                           VTE_WRITE_DEFAULT,
3688                                           NULL, &error);
3689         g_object_unref (stream);
3690     }
3691 
3692     if (error)
3693     {
3694         terminal_util_show_error_dialog (parent, NULL, error,
3695                                          "%s", _("Could not save contents"));
3696         g_error_free (error);
3697     }
3698 
3699     g_object_unref(file);
3700     g_free(filename_uri);
3701 }
3702 #endif /* ENABLE_SAVE */
3703 
3704 static void
file_save_contents_callback(GtkAction * action,TerminalWindow * window)3705 file_save_contents_callback (GtkAction *action,
3706                              TerminalWindow *window)
3707 {
3708 #ifdef ENABLE_SAVE
3709     GtkWidget *dialog = NULL;
3710     TerminalWindowPrivate *priv = window->priv;
3711     VteTerminal *terminal;
3712 
3713     if (!priv->active_screen)
3714         return;
3715 
3716     terminal = VTE_TERMINAL (priv->active_screen);
3717     g_return_if_fail (VTE_IS_TERMINAL (terminal));
3718 
3719     dialog = gtk_file_chooser_dialog_new (_("Save as..."),
3720                                           GTK_WINDOW(window),
3721                                           GTK_FILE_CHOOSER_ACTION_SAVE,
3722                                           "gtk-cancel", GTK_RESPONSE_CANCEL,
3723                                           "gtk-save", GTK_RESPONSE_ACCEPT,
3724                                           NULL);
3725 
3726     gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
3727     /* XXX where should we save to? */
3728     gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP));
3729 
3730     gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW(window));
3731     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
3732     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
3733 
3734     g_signal_connect (dialog, "response", G_CALLBACK (save_contents_dialog_on_response), terminal);
3735     g_signal_connect (dialog, "delete_event", G_CALLBACK (terminal_util_dialog_response_on_delete), NULL);
3736 
3737     gtk_window_present (GTK_WINDOW (dialog));
3738 #endif /* ENABLE_SAVE */
3739 }
3740 
3741 static void
file_close_tab_callback(GtkAction * action,TerminalWindow * window)3742 file_close_tab_callback (GtkAction *action,
3743                          TerminalWindow *window)
3744 {
3745     TerminalWindowPrivate *priv = window->priv;
3746     TerminalScreen *active_screen = priv->active_screen;
3747 
3748     if (!active_screen)
3749         return;
3750 
3751     if (confirm_close_window_or_tab (window, active_screen))
3752         return;
3753 
3754     terminal_window_remove_screen (window, active_screen);
3755 }
3756 
3757 static void
edit_copy_callback(GtkAction * action,TerminalWindow * window)3758 edit_copy_callback (GtkAction *action,
3759                     TerminalWindow *window)
3760 {
3761     TerminalWindowPrivate *priv = window->priv;
3762 
3763     if (!priv->active_screen)
3764         return;
3765 
3766 #if VTE_CHECK_VERSION (0, 50, 0)
3767     vte_terminal_copy_clipboard_format (VTE_TERMINAL (priv->active_screen), VTE_FORMAT_TEXT);
3768 #else
3769     vte_terminal_copy_clipboard (VTE_TERMINAL (priv->active_screen));
3770 #endif
3771 }
3772 
3773 typedef struct
3774 {
3775     TerminalScreen *screen;
3776     gboolean uris_as_paths;
3777 } PasteData;
3778 
3779 static void
clipboard_uris_received_cb(GtkClipboard * clipboard,char ** uris,PasteData * data)3780 clipboard_uris_received_cb (GtkClipboard *clipboard,
3781                             /* const */ char **uris,
3782                             PasteData *data)
3783 {
3784     char *text;
3785     gsize len;
3786 
3787     if (!uris)
3788     {
3789         g_object_unref (data->screen);
3790         g_slice_free (PasteData, data);
3791         return;
3792     }
3793 
3794     /* This potentially modifies the strings in |uris| but that's ok */
3795     if (data->uris_as_paths)
3796         terminal_util_transform_uris_to_quoted_fuse_paths (uris);
3797 
3798     text = terminal_util_concat_uris (uris, &len);
3799     vte_terminal_feed_child (VTE_TERMINAL (data->screen), text, len);
3800     g_free (text);
3801 
3802     g_object_unref (data->screen);
3803     g_slice_free (PasteData, data);
3804 }
3805 
3806 static void
clipboard_targets_received_cb(GtkClipboard * clipboard,GdkAtom * targets,int n_targets,PasteData * data)3807 clipboard_targets_received_cb (GtkClipboard *clipboard,
3808                                GdkAtom *targets,
3809                                int n_targets,
3810                                PasteData *data)
3811 {
3812     if (!targets)
3813     {
3814         g_object_unref (data->screen);
3815         g_slice_free (PasteData, data);
3816         return;
3817     }
3818 
3819     if (gtk_targets_include_uri (targets, n_targets))
3820     {
3821         gtk_clipboard_request_uris (clipboard,
3822                                     (GtkClipboardURIReceivedFunc) clipboard_uris_received_cb,
3823                                     data);
3824         return;
3825     }
3826     else /* if (gtk_targets_include_text (targets, n_targets)) */
3827     {
3828         vte_terminal_paste_clipboard (VTE_TERMINAL (data->screen));
3829     }
3830 
3831     g_object_unref (data->screen);
3832     g_slice_free (PasteData, data);
3833 }
3834 
3835 static void
edit_paste_callback(GtkAction * action,TerminalWindow * window)3836 edit_paste_callback (GtkAction *action,
3837                      TerminalWindow *window)
3838 {
3839     TerminalWindowPrivate *priv = window->priv;
3840     GtkClipboard *clipboard;
3841     PasteData *data;
3842     const char *name;
3843 
3844     if (!priv->active_screen)
3845         return;
3846 
3847     clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
3848     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
3849     name = gtk_action_get_name (action);
3850     G_GNUC_END_IGNORE_DEPRECATIONS;
3851 
3852     data = g_slice_new (PasteData);
3853     data->screen = g_object_ref (priv->active_screen);
3854     data->uris_as_paths = (name == I_("EditPasteURIPaths") || name == I_("PopupPasteURIPaths"));
3855 
3856     gtk_clipboard_request_targets (clipboard,
3857                                    (GtkClipboardTargetsReceivedFunc) clipboard_targets_received_cb,
3858                                    data);
3859 }
3860 
3861 static void
edit_select_all_callback(GtkAction * action,TerminalWindow * window)3862 edit_select_all_callback (GtkAction *action,
3863                           TerminalWindow *window)
3864 {
3865     TerminalWindowPrivate *priv = window->priv;
3866 
3867     if (!priv->active_screen)
3868         return;
3869 
3870     vte_terminal_select_all (VTE_TERMINAL (priv->active_screen));
3871 }
3872 
3873 static void
edit_keybindings_callback(GtkAction * action,TerminalWindow * window)3874 edit_keybindings_callback (GtkAction *action,
3875                            TerminalWindow *window)
3876 {
3877     terminal_app_edit_keybindings (terminal_app_get (),
3878                                    GTK_WINDOW (window));
3879 }
3880 
3881 static void
edit_current_profile_callback(GtkAction * action,TerminalWindow * window)3882 edit_current_profile_callback (GtkAction *action,
3883                                TerminalWindow *window)
3884 {
3885     TerminalWindowPrivate *priv = window->priv;
3886 
3887     terminal_app_edit_profile (terminal_app_get (),
3888                                terminal_screen_get_profile (priv->active_screen),
3889                                GTK_WINDOW (window),
3890                                NULL);
3891 }
3892 
3893 static void
file_new_profile_callback(GtkAction * action,TerminalWindow * window)3894 file_new_profile_callback (GtkAction *action,
3895                            TerminalWindow *window)
3896 {
3897     TerminalWindowPrivate *priv = window->priv;
3898 
3899     terminal_app_new_profile (terminal_app_get (),
3900                               terminal_screen_get_profile (priv->active_screen),
3901                               GTK_WINDOW (window));
3902 }
3903 
3904 static void
edit_profiles_callback(GtkAction * action,TerminalWindow * window)3905 edit_profiles_callback (GtkAction *action,
3906                         TerminalWindow *window)
3907 {
3908     terminal_app_manage_profiles (terminal_app_get (),
3909                                   GTK_WINDOW (window));
3910 }
3911 
3912 static void
view_menubar_toggled_callback(GtkToggleAction * action,TerminalWindow * window)3913 view_menubar_toggled_callback (GtkToggleAction *action,
3914                                TerminalWindow *window)
3915 {
3916     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
3917     terminal_window_set_menubar_visible (window, gtk_toggle_action_get_active (action));
3918     G_GNUC_END_IGNORE_DEPRECATIONS;
3919 }
3920 
3921 static void
view_fullscreen_toggled_callback(GtkToggleAction * action,TerminalWindow * window)3922 view_fullscreen_toggled_callback (GtkToggleAction *action,
3923                                   TerminalWindow *window)
3924 {
3925     gboolean toggle_action_check;
3926 
3927     g_return_if_fail (gtk_widget_get_realized (GTK_WIDGET (window)));
3928 
3929     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
3930     toggle_action_check = gtk_toggle_action_get_active (action);
3931     G_GNUC_END_IGNORE_DEPRECATIONS;
3932     if (toggle_action_check)
3933         gtk_window_fullscreen (GTK_WINDOW (window));
3934     else
3935         gtk_window_unfullscreen (GTK_WINDOW (window));
3936 }
3937 
3938 static const double zoom_factors[] =
3939 {
3940     TERMINAL_SCALE_MINIMUM,
3941     TERMINAL_SCALE_XXXXX_SMALL,
3942     TERMINAL_SCALE_XXXX_SMALL,
3943     TERMINAL_SCALE_XXX_SMALL,
3944     PANGO_SCALE_XX_SMALL,
3945     PANGO_SCALE_X_SMALL,
3946     PANGO_SCALE_SMALL,
3947     PANGO_SCALE_MEDIUM,
3948     PANGO_SCALE_LARGE,
3949     PANGO_SCALE_X_LARGE,
3950     PANGO_SCALE_XX_LARGE,
3951     TERMINAL_SCALE_XXX_LARGE,
3952     TERMINAL_SCALE_XXXX_LARGE,
3953     TERMINAL_SCALE_XXXXX_LARGE,
3954     TERMINAL_SCALE_MAXIMUM
3955 };
3956 
3957 static gboolean
find_larger_zoom_factor(double current,double * found)3958 find_larger_zoom_factor (double  current,
3959                          double *found)
3960 {
3961     guint i;
3962 
3963     for (i = 0; i < G_N_ELEMENTS (zoom_factors); ++i)
3964     {
3965         /* Find a font that's larger than this one */
3966         if ((zoom_factors[i] - current) > 1e-6)
3967         {
3968             *found = zoom_factors[i];
3969             return TRUE;
3970         }
3971     }
3972 
3973     return FALSE;
3974 }
3975 
3976 static gboolean
find_smaller_zoom_factor(double current,double * found)3977 find_smaller_zoom_factor (double  current,
3978                           double *found)
3979 {
3980     int i;
3981 
3982     i = (int) G_N_ELEMENTS (zoom_factors) - 1;
3983     while (i >= 0)
3984     {
3985         /* Find a font that's smaller than this one */
3986         if ((current - zoom_factors[i]) > 1e-6)
3987         {
3988             *found = zoom_factors[i];
3989             return TRUE;
3990         }
3991 
3992         --i;
3993     }
3994 
3995     return FALSE;
3996 }
3997 
3998 static void
view_zoom_in_callback(GtkAction * action,TerminalWindow * window)3999 view_zoom_in_callback (GtkAction *action,
4000                        TerminalWindow *window)
4001 {
4002     TerminalWindowPrivate *priv = window->priv;
4003     double current;
4004 
4005     if (priv->active_screen == NULL)
4006         return;
4007 
4008     current = terminal_screen_get_font_scale (priv->active_screen);
4009     if (!find_larger_zoom_factor (current, &current))
4010         return;
4011 
4012     terminal_screen_set_font_scale (priv->active_screen, current);
4013     terminal_window_update_zoom_sensitivity (window);
4014 }
4015 
4016 static void
view_zoom_out_callback(GtkAction * action,TerminalWindow * window)4017 view_zoom_out_callback (GtkAction *action,
4018                         TerminalWindow *window)
4019 {
4020     TerminalWindowPrivate *priv = window->priv;
4021     double current;
4022 
4023     if (priv->active_screen == NULL)
4024         return;
4025 
4026     current = terminal_screen_get_font_scale (priv->active_screen);
4027     if (!find_smaller_zoom_factor (current, &current))
4028         return;
4029 
4030     terminal_screen_set_font_scale (priv->active_screen, current);
4031     terminal_window_update_zoom_sensitivity (window);
4032 }
4033 
4034 static void
view_zoom_normal_callback(GtkAction * action,TerminalWindow * window)4035 view_zoom_normal_callback (GtkAction *action,
4036                            TerminalWindow *window)
4037 {
4038     TerminalWindowPrivate *priv = window->priv;
4039 
4040     if (priv->active_screen == NULL)
4041         return;
4042 
4043     terminal_screen_set_font_scale (priv->active_screen, PANGO_SCALE_MEDIUM);
4044     terminal_window_update_zoom_sensitivity (window);
4045 }
4046 
4047 
4048 static void
search_find_response_callback(GtkWidget * dialog,int response,gpointer user_data)4049 search_find_response_callback (GtkWidget *dialog,
4050                                int        response,
4051                                gpointer   user_data)
4052 {
4053     TerminalWindow *window = TERMINAL_WINDOW (user_data);
4054     TerminalWindowPrivate *priv = window->priv;
4055     TerminalSearchFlags flags;
4056     VteRegex *regex;
4057 
4058     if (response != GTK_RESPONSE_ACCEPT)
4059         return;
4060 
4061     if (G_UNLIKELY (!priv->active_screen))
4062         return;
4063 
4064     regex = terminal_search_dialog_get_regex (dialog);
4065     g_return_if_fail (regex != NULL);
4066 
4067     flags = terminal_search_dialog_get_search_flags (dialog);
4068 
4069     vte_terminal_search_set_regex (VTE_TERMINAL (priv->active_screen), regex, 0);
4070     vte_terminal_search_set_wrap_around (VTE_TERMINAL (priv->active_screen),
4071                                          (flags & TERMINAL_SEARCH_FLAG_WRAP_AROUND));
4072 
4073     if (flags & TERMINAL_SEARCH_FLAG_BACKWARDS)
4074         vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen));
4075     else
4076         vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen));
4077 
4078     terminal_window_update_search_sensitivity (priv->active_screen, window);
4079 }
4080 
4081 static gboolean
search_dialog_delete_event_cb(GtkWidget * widget,GdkEventAny * event,gpointer user_data)4082 search_dialog_delete_event_cb (GtkWidget   *widget,
4083                                GdkEventAny *event,
4084                                gpointer     user_data)
4085 {
4086     /* prevent destruction */
4087     return TRUE;
4088 }
4089 
4090 static void
search_find_callback(GtkAction * action,TerminalWindow * window)4091 search_find_callback (GtkAction *action,
4092                       TerminalWindow *window)
4093 {
4094     TerminalWindowPrivate *priv = window->priv;
4095 
4096     if (!priv->search_find_dialog)
4097     {
4098         GtkWidget *dialog;
4099 
4100         dialog = priv->search_find_dialog = terminal_search_dialog_new (GTK_WINDOW (window));
4101 
4102         g_signal_connect (dialog, "destroy",
4103                           G_CALLBACK (gtk_widget_destroyed), &priv->search_find_dialog);
4104         g_signal_connect (dialog, "response",
4105                           G_CALLBACK (search_find_response_callback), window);
4106         g_signal_connect (dialog, "delete-event",
4107                           G_CALLBACK (search_dialog_delete_event_cb), NULL);
4108     }
4109 
4110     terminal_search_dialog_present (priv->search_find_dialog);
4111 }
4112 
4113 static void
search_find_next_callback(GtkAction * action,TerminalWindow * window)4114 search_find_next_callback (GtkAction *action,
4115                            TerminalWindow *window)
4116 {
4117     if (G_UNLIKELY (!window->priv->active_screen))
4118         return;
4119 
4120     vte_terminal_search_find_next (VTE_TERMINAL (window->priv->active_screen));
4121 }
4122 
4123 static void
search_find_prev_callback(GtkAction * action,TerminalWindow * window)4124 search_find_prev_callback (GtkAction *action,
4125                            TerminalWindow *window)
4126 {
4127     if (G_UNLIKELY (!window->priv->active_screen))
4128         return;
4129 
4130     vte_terminal_search_find_previous (VTE_TERMINAL (window->priv->active_screen));
4131 }
4132 
4133 static void
search_clear_highlight_callback(GtkAction * action,TerminalWindow * window)4134 search_clear_highlight_callback (GtkAction *action,
4135                                  TerminalWindow *window)
4136 {
4137     if (G_UNLIKELY (!window->priv->active_screen))
4138         return;
4139 
4140     vte_terminal_search_set_regex (VTE_TERMINAL (window->priv->active_screen), NULL, 0);
4141 }
4142 
4143 static void
terminal_next_or_previous_profile_cb(GtkAction * action,TerminalWindow * window)4144 terminal_next_or_previous_profile_cb (GtkAction *action,
4145                               TerminalWindow *window)
4146 {
4147     TerminalWindowPrivate *priv = window->priv;
4148     TerminalProfile *active_profile, *new_profile = NULL;
4149     GList *profiles, *p;
4150 
4151     const char *name;
4152     guint backwards = 0;
4153 
4154     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4155     name = gtk_action_get_name (action);
4156     G_GNUC_END_IGNORE_DEPRECATIONS;
4157     if (strcmp (name, "ProfilePrevious") == 0)
4158     {
4159         backwards = 1;
4160     }
4161 
4162     profiles = terminal_app_get_profile_list (terminal_app_get ());
4163     if (profiles == NULL)
4164         return;
4165 
4166     if (priv->active_screen)
4167         active_profile = terminal_screen_get_profile (priv->active_screen);
4168     else
4169         return;
4170 
4171     for (p = profiles; p != NULL; p = p->next)
4172     {
4173         TerminalProfile *profile = (TerminalProfile *) p->data;
4174         if (profile == active_profile)
4175         {
4176             if (backwards) {
4177                 p = p->prev;
4178                 if (p == NULL)
4179                     p = g_list_last (profiles);
4180                 new_profile = p->data;
4181                 break;
4182             }
4183             else
4184             {
4185                 p = p->next;
4186                 if (p == NULL)
4187                     p = g_list_first (profiles);
4188                 new_profile = p->data;
4189                 break;
4190             }
4191         }
4192     }
4193 
4194     if (new_profile)
4195         terminal_screen_set_profile (priv->active_screen, new_profile);
4196 
4197     g_list_free (profiles);
4198 }
4199 
4200 static void
terminal_set_title_dialog_response_cb(GtkWidget * dialog,int response,TerminalScreen * screen)4201 terminal_set_title_dialog_response_cb (GtkWidget *dialog,
4202                                        int response,
4203                                        TerminalScreen *screen)
4204 {
4205     if (response == GTK_RESPONSE_OK)
4206     {
4207         GtkEntry *entry;
4208         const char *text;
4209 
4210         entry = GTK_ENTRY (g_object_get_data (G_OBJECT (dialog), "title-entry"));
4211         text = gtk_entry_get_text (entry);
4212         terminal_screen_set_user_title (screen, text);
4213     }
4214 
4215     gtk_widget_destroy (dialog);
4216 }
4217 
4218 static void
terminal_set_title_callback(GtkAction * action,TerminalWindow * window)4219 terminal_set_title_callback (GtkAction *action,
4220                              TerminalWindow *window)
4221 {
4222     GtkBuilder *builder;
4223     TerminalWindowPrivate *priv = window->priv;
4224     GtkWidget *dialog, *entry;
4225 
4226     if (priv->active_screen == NULL)
4227         return;
4228 
4229     builder = gtk_builder_new_from_resource (TERMINAL_RESOURCES_PATH_PREFIX G_DIR_SEPARATOR_S "ui/set-title-dialog.ui");
4230     dialog = GTK_WIDGET (gtk_builder_get_object (builder, "dialog"));
4231     entry = GTK_WIDGET (gtk_builder_get_object (builder, "title_entry"));
4232     g_object_unref (builder);
4233 
4234     gtk_widget_grab_focus (entry);
4235     gtk_entry_set_text (GTK_ENTRY (entry), terminal_screen_get_raw_title (priv->active_screen));
4236     gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
4237     g_object_set_data (G_OBJECT (dialog), "title-entry", entry);
4238 
4239     g_signal_connect (dialog, "response",
4240                       G_CALLBACK (terminal_set_title_dialog_response_cb), priv->active_screen);
4241     g_signal_connect (dialog, "delete-event",
4242                       G_CALLBACK (terminal_util_dialog_response_on_delete), NULL);
4243 
4244     gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
4245 
4246     gtk_window_present (GTK_WINDOW (dialog));
4247 }
4248 
4249 static void
terminal_add_encoding_callback(GtkAction * action,TerminalWindow * window)4250 terminal_add_encoding_callback (GtkAction *action,
4251                                 TerminalWindow *window)
4252 {
4253     terminal_app_edit_encodings (terminal_app_get (),
4254                                  GTK_WINDOW (window));
4255 }
4256 
4257 static void
terminal_reset_callback(GtkAction * action,TerminalWindow * window)4258 terminal_reset_callback (GtkAction *action,
4259                          TerminalWindow *window)
4260 {
4261     TerminalWindowPrivate *priv = window->priv;
4262 
4263     if (priv->active_screen == NULL)
4264         return;
4265 
4266     vte_terminal_reset (VTE_TERMINAL (priv->active_screen), TRUE, FALSE);
4267 }
4268 
4269 static void
terminal_reset_clear_callback(GtkAction * action,TerminalWindow * window)4270 terminal_reset_clear_callback (GtkAction *action,
4271                                TerminalWindow *window)
4272 {
4273     TerminalWindowPrivate *priv = window->priv;
4274 
4275     if (priv->active_screen == NULL)
4276         return;
4277 
4278     vte_terminal_reset (VTE_TERMINAL (priv->active_screen), TRUE, TRUE);
4279 }
4280 
4281 static void
tabs_next_or_previous_tab_cb(GtkAction * action,TerminalWindow * window)4282 tabs_next_or_previous_tab_cb (GtkAction *action,
4283                               TerminalWindow *window)
4284 {
4285     TerminalWindowPrivate *priv = window->priv;
4286     GtkNotebookClass *klass;
4287     const char *name;
4288     guint keyval = 0;
4289 
4290     G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
4291     name = gtk_action_get_name (action);
4292     G_GNUC_END_IGNORE_DEPRECATIONS;
4293     if (strcmp (name, "TabsNext") == 0)
4294     {
4295         keyval = GDK_KEY_Page_Down;
4296     }
4297     else if (strcmp (name, "TabsPrevious") == 0)
4298     {
4299         keyval = GDK_KEY_Page_Up;
4300     }
4301 
4302     klass = GTK_NOTEBOOK_GET_CLASS (GTK_NOTEBOOK (priv->notebook));
4303     gtk_binding_set_activate (gtk_binding_set_by_class (klass),
4304                               keyval,
4305                               GDK_CONTROL_MASK,
4306                               G_OBJECT (priv->notebook));
4307 }
4308 
4309 static void
tabs_move_left_callback(GtkAction * action,TerminalWindow * window)4310 tabs_move_left_callback (GtkAction *action,
4311                          TerminalWindow *window)
4312 {
4313     TerminalWindowPrivate *priv = window->priv;
4314     GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook);
4315     gint page_num,last_page;
4316     GtkWidget *page;
4317 
4318     page_num = gtk_notebook_get_current_page (notebook);
4319     last_page = gtk_notebook_get_n_pages (notebook) - 1;
4320     page = gtk_notebook_get_nth_page (notebook, page_num);
4321 
4322     gtk_notebook_reorder_child (notebook, page, page_num == 0 ? last_page : page_num - 1);
4323 }
4324 
4325 static void
tabs_move_right_callback(GtkAction * action,TerminalWindow * window)4326 tabs_move_right_callback (GtkAction *action,
4327                           TerminalWindow *window)
4328 {
4329     TerminalWindowPrivate *priv = window->priv;
4330     GtkNotebook *notebook = GTK_NOTEBOOK (priv->notebook);
4331     gint page_num,last_page;
4332     GtkWidget *page;
4333 
4334     page_num = gtk_notebook_get_current_page (notebook);
4335     last_page = gtk_notebook_get_n_pages (notebook) - 1;
4336     page = gtk_notebook_get_nth_page (notebook, page_num);
4337 
4338     gtk_notebook_reorder_child (notebook, page, page_num == last_page ? 0 : page_num + 1);
4339 }
4340 
4341 static void
tabs_detach_tab_callback(GtkAction * action,TerminalWindow * window)4342 tabs_detach_tab_callback (GtkAction *action,
4343                           TerminalWindow *window)
4344 {
4345     TerminalWindowPrivate *priv = window->priv;
4346     TerminalApp *app;
4347     TerminalWindow *new_window;
4348     TerminalScreen *screen;
4349 
4350     app = terminal_app_get ();
4351 
4352     screen = priv->active_screen;
4353 
4354     new_window = terminal_app_new_window (app, gtk_widget_get_screen (GTK_WIDGET (window)));
4355 
4356     terminal_window_move_screen (window, new_window, screen, -1);
4357 
4358     /* FIXME: this seems wrong if tabs are shown in the window */
4359     terminal_window_update_size (new_window, screen, FALSE);
4360 
4361     gtk_window_present_with_time (GTK_WINDOW (new_window), gtk_get_current_event_time ());
4362 }
4363 
4364 static void
help_contents_callback(GtkAction * action,TerminalWindow * window)4365 help_contents_callback (GtkAction *action,
4366                         TerminalWindow *window)
4367 {
4368     terminal_util_show_help (NULL, GTK_WINDOW (window));
4369 }
4370 
4371 #define ABOUT_GROUP "About"
4372 #define EMAILIFY(string) (g_strdelimit ((string), "%", '@'))
4373 
4374 static void
help_about_callback(GtkAction * action,TerminalWindow * window)4375 help_about_callback (GtkAction *action,
4376                      TerminalWindow *window)
4377 {
4378     char *licence_text;
4379     GBytes *bytes;
4380     const guint8 *data;
4381     GKeyFile *key_file;
4382     GError *error = NULL;
4383     char **authors, **contributors, **artists, **documenters, **array_strv;
4384     gchar *comments = NULL;
4385     gsize data_len, n_authors = 0, n_contributors = 0, n_artists = 0, n_documenters = 0 , i;
4386     GPtrArray *array;
4387 
4388 
4389     bytes = g_resources_lookup_data (TERMINAL_RESOURCES_PATH_PREFIX G_DIR_SEPARATOR_S "ui/terminal.about",
4390                                      G_RESOURCE_LOOKUP_FLAGS_NONE,
4391                                      &error);
4392     g_assert_no_error (error);
4393 
4394     data = g_bytes_get_data (bytes, &data_len);
4395     key_file = g_key_file_new ();
4396     g_key_file_load_from_data (key_file, (const char *) data, data_len, 0, &error);
4397     g_assert_no_error (error);
4398 
4399     authors = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Authors", &n_authors, NULL);
4400     contributors = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Contributors", &n_contributors, NULL);
4401     artists = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Artists", &n_artists, NULL);
4402     documenters = g_key_file_get_string_list (key_file, ABOUT_GROUP, "Documenters", &n_documenters, NULL);
4403     g_key_file_free (key_file);
4404     g_bytes_unref (bytes);
4405 
4406     array = g_ptr_array_new ();
4407 
4408     for (i = 0; i < n_authors; ++i)
4409         g_ptr_array_add (array, EMAILIFY (authors[i]));
4410     g_free (authors); /* strings are now owned by the array */
4411 
4412     if (n_contributors > 0)
4413     {
4414         g_ptr_array_add (array, g_strdup (""));
4415         g_ptr_array_add (array, g_strdup (_("Contributors:")));
4416         for (i = 0; i < n_contributors; ++i)
4417             g_ptr_array_add (array, EMAILIFY (contributors[i]));
4418     }
4419     g_free (contributors); /* strings are now owned by the array */
4420 
4421     g_ptr_array_add (array, NULL);
4422     array_strv = (char **) g_ptr_array_free (array, FALSE);
4423 
4424     for (i = 0; i < n_artists; ++i)
4425         artists[i] = EMAILIFY (artists[i]);
4426     for (i = 0; i < n_documenters; ++i)
4427         documenters[i] = EMAILIFY (documenters[i]);
4428 
4429     licence_text = terminal_util_get_licence_text ();
4430 
4431     comments = g_strdup_printf (_("MATE Terminal is a terminal emulator for the MATE Desktop Environment.\nPowered by Virtual TErminal %d.%d.%d"),
4432                                 vte_get_major_version (), vte_get_minor_version (), vte_get_micro_version ());
4433 
4434     gtk_show_about_dialog (GTK_WINDOW (window),
4435                            "program-name", _("MATE Terminal"),
4436                            "version", VERSION,
4437                            "title", _("About MATE Terminal"),
4438                            "copyright", _("Copyright \xc2\xa9 2002–2004 Havoc Pennington\n"
4439                                           "Copyright \xc2\xa9 2003–2004, 2007 Mariano Suárez-Alvarez\n"
4440                                           "Copyright \xc2\xa9 2006 Guilherme de S. Pastore\n"
4441                                           "Copyright \xc2\xa9 2007–2010 Christian Persch\n"
4442                                           "Copyright \xc2\xa9 2011 Perberos\n"
4443                                           "Copyright \xc2\xa9 2012-2021 MATE developers"),
4444                            "comments", comments,
4445                            "authors", array_strv,
4446                            "artists", artists,
4447                            "documenters", documenters,
4448                            "license", licence_text,
4449                            "wrap-license", TRUE,
4450                            "translator-credits", _("translator-credits"),
4451                            "logo-icon-name", MATE_TERMINAL_ICON_NAME,
4452                            "website", PACKAGE_URL,
4453                            NULL);
4454 
4455     g_free (comments);
4456     g_strfreev (array_strv);
4457     g_strfreev (artists);
4458     g_strfreev (documenters);
4459     g_free (licence_text);
4460 }
4461 
4462 GtkUIManager *
terminal_window_get_ui_manager(TerminalWindow * window)4463 terminal_window_get_ui_manager (TerminalWindow *window)
4464 {
4465     TerminalWindowPrivate *priv = window->priv;
4466 
4467     return priv->ui_manager;
4468 }
4469 
4470 void
terminal_window_save_state(TerminalWindow * window,GKeyFile * key_file,const char * group)4471 terminal_window_save_state (TerminalWindow *window,
4472                             GKeyFile *key_file,
4473                             const char *group)
4474 {
4475     TerminalWindowPrivate *priv = window->priv;
4476     GList *tabs, *lt;
4477     TerminalScreen *active_screen;
4478     GdkWindowState state;
4479     GPtrArray *tab_names_array;
4480     char **tab_names;
4481     gsize len;
4482 
4483     //XXXif (priv->menub)//XXX
4484     g_key_file_set_boolean (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_MENUBAR_VISIBLE,
4485                             priv->menubar_visible);
4486 
4487     g_key_file_set_string (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_ROLE,
4488                            gtk_window_get_role (GTK_WINDOW (window)));
4489 
4490     state = gdk_window_get_state (gtk_widget_get_window (GTK_WIDGET (window)));
4491     if (state & GDK_WINDOW_STATE_MAXIMIZED)
4492         g_key_file_set_boolean (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_MAXIMIZED, TRUE);
4493     if (state & GDK_WINDOW_STATE_FULLSCREEN)
4494         g_key_file_set_boolean (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_FULLSCREEN, TRUE);
4495 
4496     active_screen = terminal_window_get_active (window);
4497     tabs = terminal_window_list_screen_containers (window);
4498 
4499     tab_names_array = g_ptr_array_sized_new (g_list_length (tabs) + 1);
4500 
4501     for (lt = tabs; lt != NULL; lt = lt->next)
4502     {
4503         TerminalScreen *screen;
4504         char *tab_group;
4505 
4506         screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (lt->data));
4507 
4508         tab_group = g_strdup_printf ("Terminal%p", screen);
4509         g_ptr_array_add (tab_names_array, tab_group);
4510 
4511         terminal_screen_save_config (screen, key_file, tab_group);
4512 
4513         if (screen == active_screen)
4514         {
4515             int w, h, x, y;
4516             char *geometry;
4517 
4518             g_key_file_set_string (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_ACTIVE_TAB, tab_group);
4519 
4520             /* FIXME saving the geometry is not great :-/ */
4521             terminal_screen_get_size (screen, &w, &h);
4522             gtk_window_get_position (GTK_WINDOW (window), &x, &y);
4523             geometry = g_strdup_printf ("%dx%d+%d+%d", w, h, x, y);
4524             g_key_file_set_string (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_GEOMETRY, geometry);
4525             g_free (geometry);
4526         }
4527     }
4528 
4529     g_list_free (tabs);
4530 
4531     len = tab_names_array->len;
4532     g_ptr_array_add (tab_names_array, NULL);
4533     tab_names = (char **) g_ptr_array_free (tab_names_array, FALSE);
4534     g_key_file_set_string_list (key_file, group, TERMINAL_CONFIG_WINDOW_PROP_TABS, (const char * const *) tab_names, len);
4535     g_strfreev (tab_names);
4536 }
4537 
4538 
4539 TerminalWindow *
terminal_window_get_latest_focused(TerminalWindow * window1,TerminalWindow * window2)4540 terminal_window_get_latest_focused (TerminalWindow *window1,
4541                                     TerminalWindow *window2)
4542 {
4543   TerminalWindowPrivate *priv1 = NULL;
4544   TerminalWindowPrivate *priv2 = NULL;
4545 
4546   if (!window1)
4547     return window2;
4548 
4549   if (!window2)
4550     return window1;
4551 
4552   priv1 = window1->priv;
4553   priv2 = window2->priv;
4554 
4555   if (priv2->focus_time > priv1->focus_time)
4556     return window2;
4557 
4558   return window1;
4559 }
4560