/*
* Copyright © 2001 Havoc Pennington
* Copyright © 2002 Red Hat, Inc.
* Copyright © 2007, 2008, 2009, 2011, 2017 Christian Persch
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include "terminal-app.h"
#include "terminal-debug.h"
#include "terminal-enums.h"
#include "terminal-headerbar.h"
#include "terminal-icon-button.h"
#include "terminal-intl.h"
#include "terminal-mdi-container.h"
#include "terminal-menu-button.h"
#include "terminal-notebook.h"
#include "terminal-schemas.h"
#include "terminal-screen-container.h"
#include "terminal-search-popover.h"
#include "terminal-tab-label.h"
#include "terminal-util.h"
#include "terminal-window.h"
#include "terminal-libgsystem.h"
struct _TerminalWindowPrivate
{
char *uuid;
GtkClipboard *clipboard;
TerminalScreenPopupInfo *popup_info;
GtkWidget *menubar;
TerminalMdiContainer *mdi_container;
GtkWidget *main_vbox;
TerminalScreen *active_screen;
/* Size of a character cell in pixels */
int old_char_width;
int old_char_height;
/* Width and height added to the actual terminal grid by "chrome" inside
* what was traditionally the X11 window: menu bar, title bar,
* style-provided padding. This must be included when resizing the window
* and also included in geometry hints. */
int old_chrome_width;
int old_chrome_height;
/* Width and height added to the window by client-side decorations.
* This must be included in geometry hints but must not be included when
* resizing the window. */
int old_csd_width;
int old_csd_height;
/* Width and height of the padding around the geometry widget. */
int old_padding_width;
int old_padding_height;
void *old_geometry_widget; /* only used for pointer value as it may be freed */
GtkWidget *confirm_close_dialog;
TerminalSearchPopover *search_popover;
guint use_default_menubar_visibility : 1;
guint disposed : 1;
guint present_on_insert : 1;
guint realized : 1;
};
#define TERMINAL_WINDOW_CSS_NAME "terminal-window"
#define MIN_WIDTH_CHARS 4
#define MIN_HEIGHT_CHARS 1
#if 1
/*
* We don't want to enable content saving until vte supports it async.
* So we disable this code for stable versions.
*/
#include "terminal-version.h"
#if (TERMINAL_MINOR_VERSION & 1) != 0
#define ENABLE_SAVE
#else
#undef ENABLE_SAVE
#endif
#endif
/* See bug #789356 */
#define WINDOW_STATE_TILED (GDK_WINDOW_STATE_TILED | \
GDK_WINDOW_STATE_LEFT_TILED | \
GDK_WINDOW_STATE_RIGHT_TILED | \
GDK_WINDOW_STATE_TOP_TILED | \
GDK_WINDOW_STATE_BOTTOM_TILED)
static void terminal_window_dispose (GObject *object);
static void terminal_window_finalize (GObject *object);
static gboolean terminal_window_state_event (GtkWidget *widget,
GdkEventWindowState *event);
static gboolean terminal_window_delete_event (GtkWidget *widget,
GdkEvent *event,
gpointer data);
static gboolean notebook_button_press_cb (GtkWidget *notebook,
GdkEventButton *event,
TerminalWindow *window);
static gboolean notebook_popup_menu_cb (GtkWidget *notebook,
TerminalWindow *window);
static void mdi_screen_switched_cb (TerminalMdiContainer *container,
TerminalScreen *old_active_screen,
TerminalScreen *screen,
TerminalWindow *window);
static void mdi_screen_added_cb (TerminalMdiContainer *container,
TerminalScreen *screen,
TerminalWindow *window);
static void mdi_screen_removed_cb (TerminalMdiContainer *container,
TerminalScreen *screen,
TerminalWindow *window);
static void mdi_screens_reordered_cb (TerminalMdiContainer *container,
TerminalWindow *window);
static void screen_close_request_cb (TerminalMdiContainer *container,
TerminalScreen *screen,
TerminalWindow *window);
/* Menu action callbacks */
static gboolean find_larger_zoom_factor (double *zoom);
static gboolean find_smaller_zoom_factor (double *zoom);
static void terminal_window_update_zoom_sensitivity (TerminalWindow *window);
static void terminal_window_update_search_sensitivity (TerminalScreen *screen,
TerminalWindow *window);
static void terminal_window_update_paste_sensitivity (TerminalWindow *window);
static void terminal_window_show (GtkWidget *widget);
static gboolean confirm_close_window_or_tab (TerminalWindow *window,
TerminalScreen *screen);
G_DEFINE_TYPE (TerminalWindow, terminal_window, GTK_TYPE_APPLICATION_WINDOW)
/* Zoom helpers */
static const double zoom_factors[] = {
TERMINAL_SCALE_MINIMUM,
TERMINAL_SCALE_XXXXX_SMALL,
TERMINAL_SCALE_XXXX_SMALL,
TERMINAL_SCALE_XXX_SMALL,
PANGO_SCALE_XX_SMALL,
PANGO_SCALE_X_SMALL,
PANGO_SCALE_SMALL,
PANGO_SCALE_MEDIUM,
PANGO_SCALE_LARGE,
PANGO_SCALE_X_LARGE,
PANGO_SCALE_XX_LARGE,
TERMINAL_SCALE_XXX_LARGE,
TERMINAL_SCALE_XXXX_LARGE,
TERMINAL_SCALE_XXXXX_LARGE,
TERMINAL_SCALE_MAXIMUM
};
static gboolean
find_larger_zoom_factor (double *zoom)
{
double current = *zoom;
guint i;
for (i = 0; i < G_N_ELEMENTS (zoom_factors); ++i)
{
/* Find a font that's larger than this one */
if ((zoom_factors[i] - current) > 1e-6)
{
*zoom = zoom_factors[i];
return TRUE;
}
}
return FALSE;
}
static gboolean
find_smaller_zoom_factor (double *zoom)
{
double current = *zoom;
int i;
i = (int) G_N_ELEMENTS (zoom_factors) - 1;
while (i >= 0)
{
/* Find a font that's smaller than this one */
if ((current - zoom_factors[i]) > 1e-6)
{
*zoom = zoom_factors[i];
return TRUE;
}
--i;
}
return FALSE;
}
static inline GSimpleAction *
lookup_action (TerminalWindow *window,
const char *name)
{
GAction *action;
action = g_action_map_lookup_action (G_ACTION_MAP (window), name);
g_return_val_if_fail (action != NULL, NULL);
return G_SIMPLE_ACTION (action);
}
/* Context menu helpers */
/* We don't want context menus to show accelerators.
* Setting the menu's accel group and/or accel path to NULL
* unfortunately doesn't hide accelerators; we need to walk
* the menu items and remove the accelerators on each,
* manually.
*/
static void
popup_menu_remove_accelerators (GtkWidget *menu)
{
gs_free_list GList *menu_items;
GList *l;
menu_items = gtk_container_get_children (GTK_CONTAINER (menu));
for (l = menu_items; l != NULL; l = l ->next) {
GtkMenuItem *item = (GtkMenuItem*) (l->data);
GtkWidget *label, *submenu;
if (!GTK_IS_MENU_ITEM (item))
continue;
if (GTK_IS_ACCEL_LABEL ((label = gtk_bin_get_child (GTK_BIN (item)))))
gtk_accel_label_set_accel (GTK_ACCEL_LABEL (label), 0, 0);
/* Recurse into submenus */
if ((submenu = gtk_menu_item_get_submenu (item)))
popup_menu_remove_accelerators (submenu);
}
}
/* Because we're using gtk_menu_attach_to_widget(), the attach
* widget holds a strong reference to the menu, causing it not to
* be automatically destroyed once popped down. So we need to
* detach the menu from the attach widget manually, which will
* cause the menu to be destroyed. We cannot do so in the
* "deactivate" handler however, since that causes the menu
* item activation to be lost. The "selection-done" signal
* appears to be the right place.
*/
static void
popup_menu_destroy_cb (GtkWidget *menu,
gpointer user_data)
{
/* g_printerr ("Menu %p destroyed!\n", menu); */
}
static void
popup_menu_selection_done_cb (GtkMenu *menu,
gpointer user_data)
{
g_signal_handlers_disconnect_by_func
(menu, G_CALLBACK (popup_menu_selection_done_cb), user_data);
/* g_printerr ("selection-done %p\n", menu); */
/* This will remove the ref from the attach widget widget, and destroy the menu */
if (gtk_menu_get_attach_widget (menu) != NULL)
gtk_menu_detach (menu);
}
static void
popup_menu_detach_cb (GtkWidget *attach_widget,
GtkMenu *menu)
{
gtk_menu_shell_deactivate (GTK_MENU_SHELL (menu));
}
static GtkWidget *
context_menu_new (GMenuModel *menu,
GtkWidget *widget)
{
GtkWidget *popup_menu;
popup_menu = gtk_menu_new_from_model (menu);
gtk_style_context_add_class (gtk_widget_get_style_context (popup_menu),
GTK_STYLE_CLASS_CONTEXT_MENU);
gtk_menu_attach_to_widget (GTK_MENU (popup_menu), widget,
(GtkMenuDetachFunc)popup_menu_detach_cb);
popup_menu_remove_accelerators (popup_menu);
/* Staggered destruction */
g_signal_connect (popup_menu, "selection-done",
G_CALLBACK (popup_menu_selection_done_cb), widget);
g_signal_connect (popup_menu, "destroy",
G_CALLBACK (popup_menu_destroy_cb), widget);
return popup_menu;
}
/* GAction callbacks */
static void
action_new_terminal_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalApp *app;
TerminalSettingsList *profiles_list;
gs_unref_object GSettings *profile = NULL;
gboolean can_toggle = FALSE;
g_assert (TERMINAL_IS_WINDOW (window));
app = terminal_app_get ();
const char *mode_str, *uuid_str;
g_variant_get (parameter, "(&s&s)", &mode_str, &uuid_str);
TerminalNewTerminalMode mode;
if (g_str_equal (mode_str, "tab"))
mode = TERMINAL_NEW_TERMINAL_MODE_TAB;
else if (g_str_equal (mode_str, "window"))
mode = TERMINAL_NEW_TERMINAL_MODE_WINDOW;
else if (g_str_equal (mode_str, "tab-default")) {
mode = TERMINAL_NEW_TERMINAL_MODE_TAB;
can_toggle = TRUE;
} else {
mode = g_settings_get_enum (terminal_app_get_global_settings (app),
TERMINAL_SETTING_NEW_TERMINAL_MODE_KEY);
can_toggle = TRUE;
}
if (can_toggle) {
GdkEvent *event = gtk_get_current_event ();
if (event != NULL) {
GdkModifierType modifiers;
if ((gdk_event_get_state (event, &modifiers) &&
(modifiers & gtk_accelerator_get_default_mod_mask () & GDK_CONTROL_MASK))) {
/* Invert */
if (mode == TERMINAL_NEW_TERMINAL_MODE_WINDOW)
mode = TERMINAL_NEW_TERMINAL_MODE_TAB;
else
mode = TERMINAL_NEW_TERMINAL_MODE_WINDOW;
}
gdk_event_free (event);
}
}
TerminalScreen *parent_screen = priv->active_screen;
profiles_list = terminal_app_get_profiles_list (app);
if (g_str_equal (uuid_str, "current"))
profile = terminal_screen_ref_profile (parent_screen);
else if (g_str_equal (uuid_str, "default"))
profile = terminal_settings_list_ref_default_child (profiles_list);
else
profile = terminal_settings_list_ref_child (profiles_list, uuid_str);
if (profile == NULL)
return;
if (mode == TERMINAL_NEW_TERMINAL_MODE_WINDOW)
window = terminal_window_new (G_APPLICATION (app));
TerminalScreen *screen = terminal_screen_new (profile,
NULL /* title */,
1.0);
/* Now add the new screen to the window */
terminal_window_add_screen (window, screen, -1);
terminal_window_switch_screen (window, screen);
gtk_widget_grab_focus (GTK_WIDGET (screen));
/* Start child process, if possible by using the same args as the parent screen */
terminal_screen_reexec_from_screen (screen, parent_screen, NULL, NULL);
if (mode == TERMINAL_NEW_TERMINAL_MODE_WINDOW)
gtk_window_present (GTK_WINDOW (window));
}
#ifdef ENABLE_SAVE
static void
save_contents_dialog_on_response (GtkDialog *dialog, gint response_id, gpointer terminal)
{
GtkWindow *parent;
gs_free gchar *filename_uri = NULL;
gs_unref_object GFile *file = NULL;
GOutputStream *stream;
gs_free_error GError *error = NULL;
if (response_id != GTK_RESPONSE_ACCEPT)
{
gtk_widget_destroy (GTK_WIDGET (dialog));
return;
}
parent = (GtkWindow*) gtk_widget_get_ancestor (GTK_WIDGET (terminal), GTK_TYPE_WINDOW);
filename_uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
gtk_widget_destroy (GTK_WIDGET (dialog));
if (filename_uri == NULL)
return;
file = g_file_new_for_uri (filename_uri);
stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
if (stream)
{
/* XXX
* FIXME
* This is a sync operation.
* Should be replaced with the async version when vte implements that.
*/
vte_terminal_write_contents_sync (terminal, stream,
VTE_WRITE_DEFAULT,
NULL, &error);
g_object_unref (stream);
}
if (error)
{
terminal_util_show_error_dialog (parent, NULL, error,
"%s", _("Could not save contents"));
}
}
static void
action_save_contents_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
GtkWidget *dialog = NULL;
TerminalWindowPrivate *priv = window->priv;
VteTerminal *terminal;
if (priv->active_screen == NULL)
return;
terminal = VTE_TERMINAL (priv->active_screen);
g_return_if_fail (VTE_IS_TERMINAL (terminal));
dialog = gtk_file_chooser_dialog_new (_("Save as…"),
GTK_WINDOW(window),
GTK_FILE_CHOOSER_ACTION_SAVE,
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (window));
gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
g_signal_connect (dialog, "response", G_CALLBACK (save_contents_dialog_on_response), terminal);
g_signal_connect (dialog, "delete_event", G_CALLBACK (terminal_util_dialog_response_on_delete), NULL);
gtk_window_present (GTK_WINDOW (dialog));
}
#endif /* ENABLE_SAVE */
#ifdef ENABLE_PRINT
static void
print_begin_cb (GtkPrintOperation *op,
GtkPrintContext *context,
TerminalApp *app)
{
GtkPrintSettings *settings;
GtkPageSetup *page_setup;
/* Don't save if the print dialogue was cancelled */
if (gtk_print_operation_get_status(op) == GTK_PRINT_STATUS_FINISHED_ABORTED)
return;
settings = gtk_print_operation_get_print_settings (op);
page_setup = gtk_print_operation_get_default_page_setup (op);
terminal_util_save_print_settings (settings, page_setup);
}
static void
print_done_cb (GtkPrintOperation *op,
GtkPrintOperationResult result,
TerminalWindow *window)
{
if (result != GTK_PRINT_OPERATION_RESULT_ERROR)
return;
/* FIXME: show error */
}
static void
action_print_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
gs_unref_object GtkPrintSettings *settings = NULL;
gs_unref_object GtkPageSetup *page_setup = NULL;
gs_unref_object GtkPrintOperation *op = NULL;
gs_free_error GError *error = NULL;
GtkPrintOperationResult result;
if (priv->active_screen == NULL)
return;
op = vte_print_operation_new (VTE_TERMINAL (priv->active_screen),
VTE_PRINT_OPERATION_DEFAULT /* flags */);
if (op == NULL)
return;
terminal_util_load_print_settings (&settings, &page_setup);
if (settings != NULL)
gtk_print_operation_set_print_settings (op, settings);
if (page_setup != NULL)
gtk_print_operation_set_default_page_setup (op, page_setup);
g_signal_connect (op, "begin-print", G_CALLBACK (print_begin_cb), window);
g_signal_connect (op, "done", G_CALLBACK (print_done_cb), window);
/* FIXME: show progress better */
result = gtk_print_operation_run (op,
/* this is the only supported one: */
GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG,
GTK_WINDOW (window),
&error);
/* VtePrintOperation always runs async */
g_assert_cmpint (result, ==, GTK_PRINT_OPERATION_RESULT_IN_PROGRESS);
}
#endif /* ENABLE_PRINT */
#ifdef ENABLE_EXPORT
static void
action_export_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
gs_unref_object VteExportOperation *op = NULL;
gs_free_error GError *error = NULL;
if (priv->active_screen == NULL)
return;
op = vte_export_operation_new (VTE_TERMINAL (priv->active_screen),
TRUE /* interactive */,
VTE_EXPORT_FORMAT_ASK /* allow user to choose export format */,
NULL, NULL /* GSettings & key to load/store default directory from, FIXME */,
NULL, NULL /* progress callback & user data, FIXME */);
if (op == NULL)
return;
/* FIXME: show progress better */
vte_export_operation_run_async (op, GTK_WINDOW (window), NULL /* cancellable */);
}
#endif /* ENABLE_EXPORT */
static void
action_close_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalScreen *screen;
const char *mode_str;
g_assert_nonnull (parameter);
g_variant_get (parameter, "&s", &mode_str);
if (g_str_equal (mode_str, "tab"))
screen = priv->active_screen;
else if (g_str_equal (mode_str, "window"))
screen = NULL;
else
return;
if (confirm_close_window_or_tab (window, screen))
return;
if (screen)
terminal_window_remove_screen (window, screen);
else
gtk_widget_destroy (GTK_WIDGET (window));
}
static void
action_copy_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
const char *format_str;
VteFormat format;
if (priv->active_screen == NULL)
return;
g_assert_nonnull (parameter);
g_variant_get (parameter, "&s", &format_str);
if (g_str_equal (format_str, "text"))
format = VTE_FORMAT_TEXT;
else if (g_str_equal (format_str, "html"))
format = VTE_FORMAT_HTML;
else
return;
vte_terminal_copy_clipboard_format (VTE_TERMINAL (priv->active_screen), format);
}
/* Clipboard helpers */
typedef struct {
GWeakRef screen_weak_ref;
} PasteData;
static void
clipboard_uris_received_cb (GtkClipboard *clipboard,
/* const */ char **uris,
PasteData *data)
{
gs_unref_object TerminalScreen *screen = NULL;
if (uris != NULL && uris[0] != NULL &&
(screen = g_weak_ref_get (&data->screen_weak_ref))) {
gs_free char *text;
gsize len;
/* This potentially modifies the strings in |uris| but that's ok */
terminal_util_transform_uris_to_quoted_fuse_paths (uris);
text = terminal_util_concat_uris (uris, &len);
vte_terminal_feed_child (VTE_TERMINAL (screen), text, len);
}
g_weak_ref_clear (&data->screen_weak_ref);
g_slice_free (PasteData, data);
}
static void
request_clipboard_contents_for_paste (TerminalWindow *window,
gboolean paste_as_uris)
{
TerminalWindowPrivate *priv = window->priv;
GdkAtom *targets;
int n_targets;
if (priv->active_screen == NULL)
return;
targets = terminal_app_get_clipboard_targets (terminal_app_get (),
priv->clipboard,
&n_targets);
if (targets == NULL)
return;
if (paste_as_uris && gtk_targets_include_uri (targets, n_targets)) {
PasteData *data = g_slice_new (PasteData);
g_weak_ref_init (&data->screen_weak_ref, priv->active_screen);
gtk_clipboard_request_uris (priv->clipboard,
(GtkClipboardURIReceivedFunc) clipboard_uris_received_cb,
data);
return;
} else if (gtk_targets_include_text (targets, n_targets)) {
vte_terminal_paste_clipboard (VTE_TERMINAL (priv->active_screen));
}
}
static void
action_paste_text_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
request_clipboard_contents_for_paste (window, FALSE);
}
static void
action_paste_uris_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
request_clipboard_contents_for_paste (window, TRUE);
}
static void
action_select_all_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
if (priv->active_screen == NULL)
return;
vte_terminal_select_all (VTE_TERMINAL (priv->active_screen));
}
static void
action_reset_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
g_assert_nonnull (parameter);
if (priv->active_screen == NULL)
return;
vte_terminal_reset (VTE_TERMINAL (priv->active_screen),
TRUE,
g_variant_get_boolean (parameter));
}
static void
tab_switch_relative (TerminalWindow *window,
int change)
{
TerminalWindowPrivate *priv = window->priv;
int n_screens, value;
n_screens = terminal_mdi_container_get_n_screens (priv->mdi_container);
value = terminal_mdi_container_get_active_screen_num (priv->mdi_container) + change;
gboolean keynav_wrap_around;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
"gtk-keynav-wrap-around", &keynav_wrap_around,
NULL);
if (keynav_wrap_around) {
if (value < 0)
value += n_screens;
else if (value >= n_screens)
value -= n_screens;
}
if (value < 0 || value >= n_screens)
return;
g_action_change_state (G_ACTION (lookup_action (window, "active-tab")),
g_variant_new_int32 (value));
}
static void
action_tab_switch_left_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
tab_switch_relative (window, -1);
}
static void
action_tab_switch_right_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
tab_switch_relative (window, 1);
}
static void
action_tab_move_left_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
int change;
if (priv->active_screen == NULL)
return;
change = gtk_widget_get_direction (GTK_WIDGET (window)) == GTK_TEXT_DIR_RTL ? 1 : -1;
terminal_mdi_container_reorder_screen (priv->mdi_container,
terminal_mdi_container_get_active_screen (priv->mdi_container),
change);
}
static void
action_tab_move_right_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
int change;
if (priv->active_screen == NULL)
return;
change = gtk_widget_get_direction (GTK_WIDGET (window)) == GTK_TEXT_DIR_RTL ? -1 : 1;
terminal_mdi_container_reorder_screen (priv->mdi_container,
terminal_mdi_container_get_active_screen (priv->mdi_container),
change);
}
static void
action_zoom_in_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
double zoom;
if (priv->active_screen == NULL)
return;
zoom = vte_terminal_get_font_scale (VTE_TERMINAL (priv->active_screen));
if (!find_larger_zoom_factor (&zoom))
return;
vte_terminal_set_font_scale (VTE_TERMINAL (priv->active_screen), zoom);
terminal_window_update_zoom_sensitivity (window);
}
static void
action_zoom_out_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
double zoom;
if (priv->active_screen == NULL)
return;
zoom = vte_terminal_get_font_scale (VTE_TERMINAL (priv->active_screen));
if (!find_smaller_zoom_factor (&zoom))
return;
vte_terminal_set_font_scale (VTE_TERMINAL (priv->active_screen), zoom);
terminal_window_update_zoom_sensitivity (window);
}
static void
action_zoom_normal_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
if (priv->active_screen == NULL)
return;
vte_terminal_set_font_scale (VTE_TERMINAL (priv->active_screen), PANGO_SCALE_MEDIUM);
terminal_window_update_zoom_sensitivity (window);
}
static void
action_tab_detach_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalApp *app;
TerminalWindow *new_window;
TerminalScreen *screen;
char geometry[32];
int width, height;
app = terminal_app_get ();
screen = priv->active_screen;
terminal_screen_get_size (screen, &width, &height);
g_snprintf (geometry, sizeof (geometry), "%dx%d", width, height);
new_window = terminal_window_new (G_APPLICATION (app));
terminal_window_move_screen (window, new_window, screen, -1);
terminal_window_parse_geometry (new_window, geometry);
gtk_window_present_with_time (GTK_WINDOW (new_window), gtk_get_current_event_time ());
}
static void
action_help_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
terminal_util_show_help (NULL);
}
static void
action_about_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
terminal_util_show_about ();
}
static void
action_edit_preferences_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
terminal_app_edit_preferences (terminal_app_get (),
terminal_screen_get_profile (priv->active_screen),
NULL);
}
static void
action_size_to_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
guint width, height;
g_assert_nonnull (parameter);
if (priv->active_screen == NULL)
return;
g_variant_get (parameter, "(uu)", &width, &height);
if (width < MIN_WIDTH_CHARS || height < MIN_HEIGHT_CHARS ||
width > 256 || height > 256)
return;
vte_terminal_set_size (VTE_TERMINAL (priv->active_screen), width, height);
terminal_window_update_size (window);
}
static void
action_open_match_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalScreenPopupInfo *info = priv->popup_info;
if (info == NULL)
return;
if (info->url == NULL)
return;
terminal_util_open_url (GTK_WIDGET (window), info->url, info->url_flavor,
gtk_get_current_event_time ());
}
static void
action_copy_match_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalScreenPopupInfo *info = priv->popup_info;
if (info == NULL)
return;
if (info->url == NULL)
return;
gtk_clipboard_set_text (priv->clipboard, info->url, -1);
}
static void
action_open_hyperlink_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalScreenPopupInfo *info = priv->popup_info;
if (info == NULL)
return;
if (info->hyperlink == NULL)
return;
terminal_util_open_url (GTK_WIDGET (window), info->hyperlink, FLAVOR_AS_IS,
gtk_get_current_event_time ());
}
static void
action_copy_hyperlink_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalScreenPopupInfo *info = priv->popup_info;
if (info == NULL)
return;
if (info->hyperlink == NULL)
return;
gtk_clipboard_set_text (priv->clipboard, info->hyperlink, -1);
}
static void
action_enter_fullscreen_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
g_action_group_change_action_state (G_ACTION_GROUP (window), "fullscreen",
g_variant_new_boolean (TRUE));
}
static void
action_leave_fullscreen_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
g_action_group_change_action_state (G_ACTION_GROUP (window), "fullscreen",
g_variant_new_boolean (FALSE));
}
static void
action_inspector_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
gtk_window_set_interactive_debugging (TRUE);
}
static void
search_popover_search_cb (TerminalSearchPopover *popover,
gboolean backward,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (G_UNLIKELY (priv->active_screen == NULL))
return;
if (backward)
vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen));
else
vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen));
}
static void
search_popover_notify_regex_cb (TerminalSearchPopover *popover,
GParamSpec *pspec G_GNUC_UNUSED,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
VteRegex *regex;
if (G_UNLIKELY (priv->active_screen == NULL))
return;
regex = terminal_search_popover_get_regex (popover);
vte_terminal_search_set_regex (VTE_TERMINAL (priv->active_screen), regex, 0);
terminal_window_update_search_sensitivity (priv->active_screen, window);
}
static void
search_popover_notify_wrap_around_cb (TerminalSearchPopover *popover,
GParamSpec *pspec G_GNUC_UNUSED,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
gboolean wrap;
if (G_UNLIKELY (priv->active_screen == NULL))
return;
wrap = terminal_search_popover_get_wrap_around (popover);
vte_terminal_search_set_wrap_around (VTE_TERMINAL (priv->active_screen), wrap);
}
static void
action_find_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
if (G_UNLIKELY(priv->active_screen == NULL))
return;
if (priv->search_popover != NULL) {
search_popover_notify_regex_cb (priv->search_popover, NULL, window);
search_popover_notify_wrap_around_cb (priv->search_popover, NULL, window);
gtk_window_present_with_time (GTK_WINDOW (priv->search_popover),
gtk_get_current_event_time ());
gtk_widget_grab_focus (GTK_WIDGET (priv->search_popover));
return;
}
if (priv->active_screen == NULL)
return;
priv->search_popover = terminal_search_popover_new (GTK_WIDGET (window));
g_signal_connect (priv->search_popover, "search", G_CALLBACK (search_popover_search_cb), window);
search_popover_notify_regex_cb (priv->search_popover, NULL, window);
g_signal_connect (priv->search_popover, "notify::regex", G_CALLBACK (search_popover_notify_regex_cb), window);
search_popover_notify_wrap_around_cb (priv->search_popover, NULL, window);
g_signal_connect (priv->search_popover, "notify::wrap-around", G_CALLBACK (search_popover_notify_wrap_around_cb), window);
g_signal_connect (priv->search_popover, "destroy", G_CALLBACK (gtk_widget_destroyed), &priv->search_popover);
gtk_window_present_with_time (GTK_WINDOW (priv->search_popover), gtk_get_current_event_time ());
gtk_widget_grab_focus (GTK_WIDGET (priv->search_popover));
}
static void
action_find_forward_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
if (priv->active_screen == NULL)
return;
vte_terminal_search_find_next (VTE_TERMINAL (priv->active_screen));
}
static void
action_find_backward_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
if (priv->active_screen == NULL)
return;
vte_terminal_search_find_previous (VTE_TERMINAL (priv->active_screen));
}
static void
action_find_clear_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
if (priv->active_screen == NULL)
return;
vte_terminal_search_set_regex (VTE_TERMINAL (priv->active_screen), NULL, 0);
vte_terminal_unselect_all (VTE_TERMINAL (priv->active_screen));
}
static void
action_shadow_activate_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
gs_free char *param = g_variant_print(parameter, TRUE);
_terminal_debug_print (TERMINAL_DEBUG_ACCELS,
"Window %p shadow action activated for %s\n",
window, param);
/* We make sure in terminal-accels to always install the keybinding
* for the real action first, so that it's first in line for activation.
* That means we can make this here a NOP, instead of forwarding the
* activation to the shadowed action.
*/
}
static void
action_menubar_visible_state_cb (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
TerminalWindow *window = user_data;
gboolean active;
active = g_variant_get_boolean (state);
terminal_window_set_menubar_visible (window, active); /* this also sets the action state */
}
static void
action_fullscreen_state_cb (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
TerminalWindow *window = user_data;
if (!gtk_widget_get_realized (GTK_WIDGET (window)))
return;
if (g_variant_get_boolean (state))
gtk_window_fullscreen (GTK_WINDOW (window));
else
gtk_window_unfullscreen (GTK_WINDOW (window));
/* The window-state-changed callback will update the action's actual state */
}
static void
action_read_only_state_cb (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
g_assert_nonnull (state);
g_simple_action_set_state (action, state);
terminal_window_update_paste_sensitivity (window);
if (priv->active_screen == NULL)
return;
vte_terminal_set_input_enabled (VTE_TERMINAL (priv->active_screen),
!g_variant_get_boolean (state));
}
static void
action_profile_state_cb (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
TerminalSettingsList *profiles_list;
const gchar *uuid;
gs_unref_object GSettings *profile;
g_assert_nonnull (state);
uuid = g_variant_get_string (state, NULL);
profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
profile = terminal_settings_list_ref_child (profiles_list, uuid);
if (profile == NULL)
return;
g_simple_action_set_state (action, state);
terminal_screen_set_profile (priv->active_screen, profile);
}
static void
action_active_tab_set_cb (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
int value, n_screens;
g_assert_nonnull (parameter);
n_screens = terminal_mdi_container_get_n_screens (priv->mdi_container);
value = g_variant_get_int32 (parameter);
if (value < 0)
value += n_screens;
if (value < 0 || value >= n_screens)
return;
g_action_change_state (G_ACTION (action), g_variant_new_int32 (value));
}
static void
action_active_tab_state_cb (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
TerminalWindow *window = user_data;
TerminalWindowPrivate *priv = window->priv;
g_assert_nonnull (state);
g_simple_action_set_state (action, state);
terminal_mdi_container_set_active_screen_num (priv->mdi_container, g_variant_get_int32 (state));
}
/* Menubar mnemonics & accel settings handling */
static void
enable_menubar_accel_changed_cb (GSettings *settings,
const char *key,
GtkSettings *gtk_settings)
{
if (g_settings_get_boolean (settings, key))
gtk_settings_reset_property (gtk_settings, "gtk-menu-bar-accel");
else
g_object_set (gtk_settings, "gtk-menu-bar-accel", NULL, NULL);
}
/* The menubar is shown by the app, and the use of mnemonics (e.g. Alt+F for File) is toggled.
* The mnemonic modifier is per window, so it doesn't affect the Find or Preferences windows.
* If the menubar is shown by the shell, a non-mnemonic variant of the menu is loaded instead
* in terminal-app.c. See over there for further details. */
static void
enable_mnemonics_changed_cb (GSettings *settings,
const char *key,
TerminalWindow *window)
{
gboolean enabled = g_settings_get_boolean (settings, key);
if (enabled)
gtk_window_set_mnemonic_modifier (GTK_WINDOW (window), GDK_MOD1_MASK);
else
gtk_window_set_mnemonic_modifier (GTK_WINDOW (window), GDK_MODIFIER_MASK & ~GDK_RELEASE_MASK);
}
static void
app_setting_notify_destroy_cb (GtkSettings *gtk_settings)
{
g_signal_handlers_disconnect_by_func (terminal_app_get_global_settings (terminal_app_get ()),
G_CALLBACK (enable_menubar_accel_changed_cb),
gtk_settings);
}
/* utility functions */
static int
find_tab_num_at_pos (GtkNotebook *notebook,
int screen_x,
int screen_y)
{
GtkPositionType tab_pos;
int page_num = 0;
GtkNotebook *nb = GTK_NOTEBOOK (notebook);
GtkWidget *page;
GtkAllocation tab_allocation;
tab_pos = gtk_notebook_get_tab_pos (GTK_NOTEBOOK (notebook));
while ((page = gtk_notebook_get_nth_page (nb, page_num)))
{
GtkWidget *tab;
int max_x, max_y, x_root, y_root;
tab = gtk_notebook_get_tab_label (nb, page);
g_return_val_if_fail (tab != NULL, -1);
if (!gtk_widget_get_mapped (GTK_WIDGET (tab)))
{
page_num++;
continue;
}
gdk_window_get_origin (gtk_widget_get_window (tab), &x_root, &y_root);
gtk_widget_get_allocation (tab, &tab_allocation);
max_x = x_root + tab_allocation.x + tab_allocation.width;
max_y = y_root + tab_allocation.y + tab_allocation.height;
if ((tab_pos == GTK_POS_TOP || tab_pos == GTK_POS_BOTTOM) && screen_x <= max_x)
return page_num;
if ((tab_pos == GTK_POS_LEFT || tab_pos == GTK_POS_RIGHT) && screen_y <= max_y)
return page_num;
page_num++;
}
return -1;
}
static void
terminal_window_update_set_profile_menu_active_profile (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
GSettings *new_active_profile;
TerminalSettingsList *profiles_list;
char *uuid;
if (priv->active_screen == NULL)
return;
new_active_profile = terminal_screen_get_profile (priv->active_screen);
profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
uuid = terminal_settings_list_dup_uuid_from_child (profiles_list, new_active_profile);
g_simple_action_set_state (lookup_action (window, "profile"),
g_variant_new_take_string (uuid));
}
static void
terminal_window_update_terminal_menu (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (priv->active_screen == NULL)
return;
gboolean read_only = !vte_terminal_get_input_enabled (VTE_TERMINAL (priv->active_screen));
g_simple_action_set_state (lookup_action (window, "read-only"),
g_variant_new_boolean (read_only));
}
/* Actions stuff */
static void
terminal_window_update_copy_sensitivity (TerminalScreen *screen,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
gboolean can_copy;
if (screen != priv->active_screen)
return;
can_copy = vte_terminal_get_has_selection (VTE_TERMINAL (screen));
g_simple_action_set_enabled (lookup_action (window, "copy"), can_copy);
}
static void
terminal_window_update_zoom_sensitivity (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
TerminalScreen *screen;
screen = priv->active_screen;
if (screen == NULL)
return;
double v;
double zoom = v = vte_terminal_get_font_scale (VTE_TERMINAL (screen));
g_simple_action_set_enabled (lookup_action (window, "zoom-in"),
find_larger_zoom_factor (&v));
v = zoom;
g_simple_action_set_enabled (lookup_action (window, "zoom-out"),
find_smaller_zoom_factor (&v));
}
static void
terminal_window_update_search_sensitivity (TerminalScreen *screen,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (screen != priv->active_screen)
return;
gboolean can_search = vte_terminal_search_get_regex (VTE_TERMINAL (screen)) != NULL;
g_simple_action_set_enabled (lookup_action (window, "find-forward"), can_search);
g_simple_action_set_enabled (lookup_action (window, "find-backward"), can_search);
g_simple_action_set_enabled (lookup_action (window, "find-clear"), can_search);
}
static void
clipboard_targets_changed_cb (TerminalApp *app,
GtkClipboard *clipboard,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (clipboard != priv->clipboard)
return;
terminal_window_update_paste_sensitivity (window);
}
static void
terminal_window_update_paste_sensitivity (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
GdkAtom *targets;
int n_targets;
targets = terminal_app_get_clipboard_targets (terminal_app_get(), priv->clipboard, &n_targets);
gboolean can_paste;
gboolean can_paste_uris;
if (n_targets) {
can_paste = gtk_targets_include_text (targets, n_targets);
can_paste_uris = gtk_targets_include_uri (targets, n_targets);
} else {
can_paste = can_paste_uris = FALSE;
}
gs_unref_variant GVariant *ro_state = g_action_get_state (g_action_map_lookup_action (G_ACTION_MAP (window), "read-only"));
gboolean read_only = g_variant_get_boolean (ro_state);
g_simple_action_set_enabled (lookup_action (window, "paste-text"), can_paste && !read_only);
g_simple_action_set_enabled (lookup_action (window, "paste-uris"), can_paste_uris && !read_only);
}
static void
screen_resize_window_cb (TerminalScreen *screen,
guint columns,
guint rows,
TerminalWindow* window)
{
TerminalWindowPrivate *priv = window->priv;
GtkWidget *widget = GTK_WIDGET (screen);
if (gtk_widget_get_realized (widget) &&
(gdk_window_get_state (gtk_widget_get_window (widget)) & (GDK_WINDOW_STATE_MAXIMIZED |
GDK_WINDOW_STATE_FULLSCREEN |
WINDOW_STATE_TILED)) != 0)
return;
vte_terminal_set_size (VTE_TERMINAL (priv->active_screen), columns, rows);
if (screen == priv->active_screen)
terminal_window_update_size (window);
}
static void
terminal_window_update_tabs_actions_sensitivity (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (priv->disposed)
return;
int num_pages = terminal_mdi_container_get_n_screens (priv->mdi_container);
int page_num = terminal_mdi_container_get_active_screen_num (priv->mdi_container);
gboolean not_only = num_pages > 1;
gboolean not_first = page_num > 0;
gboolean not_last = page_num + 1 < num_pages;
gboolean not_first_lr, not_last_lr;
if (gtk_widget_get_direction (GTK_WIDGET (window)) == GTK_TEXT_DIR_RTL) {
not_first_lr = not_last;
not_last_lr = not_first;
} else {
not_first_lr = not_first;
not_last_lr = not_last;
}
/* Hide the tabs menu in single-tab windows */
g_simple_action_set_enabled (lookup_action (window, "tabs-menu"), not_only);
/* Disable shadowing of MDI actions in SDI windows */
g_simple_action_set_enabled (lookup_action (window, "shadow-mdi"), not_only);
/* Disable tab switching (and all its shortcuts) in SDI windows */
g_simple_action_set_enabled (lookup_action (window, "active-tab"), not_only);
/* Set the active tab */
g_simple_action_set_state (lookup_action (window, "active-tab"),
g_variant_new_int32 (page_num));
/* Keynav wraps around? See bug #92139 */
gboolean keynav_wrap_around;
g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
"gtk-keynav-wrap-around", &keynav_wrap_around,
NULL);
gboolean wrap = keynav_wrap_around && not_only;
g_simple_action_set_enabled (lookup_action (window, "tab-switch-left"), not_first || wrap);
g_simple_action_set_enabled (lookup_action (window, "tab-switch-right"), not_last || wrap);
g_simple_action_set_enabled (lookup_action (window, "tab-move-left"), not_first_lr || wrap);
g_simple_action_set_enabled (lookup_action (window, "tab-move-right"), not_last_lr || wrap);
g_simple_action_set_enabled (lookup_action (window, "tab-detach"), not_only);
}
static GtkNotebook *
handle_tab_droped_on_desktop (GtkNotebook *source_notebook,
GtkWidget *container,
gint x,
gint y,
gpointer data G_GNUC_UNUSED)
{
TerminalWindow *source_window;
TerminalWindow *new_window;
TerminalWindowPrivate *new_priv;
source_window = TERMINAL_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (source_notebook)));
g_return_val_if_fail (TERMINAL_IS_WINDOW (source_window), NULL);
new_window = terminal_window_new (G_APPLICATION (terminal_app_get ()));
new_priv = new_window->priv;
new_priv->present_on_insert = TRUE;
return GTK_NOTEBOOK (new_priv->mdi_container);
}
/* Terminal screen popup menu handling */
static void
remove_popup_info (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (priv->popup_info != NULL)
{
terminal_screen_popup_info_unref (priv->popup_info);
priv->popup_info = NULL;
}
}
static void
screen_popup_menu_selection_done_cb (GtkWidget *popup,
GtkWidget *window)
{
g_signal_handlers_disconnect_by_func
(popup, G_CALLBACK (screen_popup_menu_selection_done_cb), window);
GtkWidget *attach_widget = gtk_menu_get_attach_widget (GTK_MENU (popup));
if (attach_widget != window || !TERMINAL_IS_WINDOW (attach_widget))
return;
remove_popup_info (TERMINAL_WINDOW (attach_widget));
}
static void
screen_show_popup_menu_cb (TerminalScreen *screen,
TerminalScreenPopupInfo *info,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
TerminalApp *app = terminal_app_get ();
if (screen != priv->active_screen)
return;
remove_popup_info (window);
priv->popup_info = terminal_screen_popup_info_ref (info);
gs_unref_object GMenu *menu = g_menu_new ();
/* Hyperlink section */
if (info->hyperlink != NULL) {
gs_unref_object GMenu *section1 = g_menu_new ();
g_menu_append (section1, _("Open _Hyperlink"), "win.open-hyperlink");
g_menu_append (section1, _("Copy Hyperlink _Address"), "win.copy-hyperlink");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section1));
}
/* Matched link section */
else if (info->url != NULL) {
gs_unref_object GMenu *section2 = g_menu_new ();
const char *open_label = NULL, *copy_label = NULL;
switch (info->url_flavor) {
case FLAVOR_EMAIL:
open_label = _("Send Mail _To…");
copy_label = _("Copy Mail _Address");
break;
case FLAVOR_VOIP_CALL:
open_label = _("Call _To…");
copy_label = _("Copy Call _Address ");
break;
case FLAVOR_AS_IS:
case FLAVOR_DEFAULT_TO_HTTP:
default:
open_label = _("_Open Link");
copy_label = _("Copy _Link");
break;
}
g_menu_append (section2, open_label, "win.open-match");
g_menu_append (section2, copy_label, "win.copy-match");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section2));
}
/* Info section */
if (info->number_info != NULL) {
gs_unref_object GMenu *section3 = g_menu_new ();
/* Non-existent action will make this item insensitive */
gs_unref_object GMenuItem *item3 = g_menu_item_new (info->number_info, "win.notexist");
g_menu_append_item (section3, item3);
g_menu_append_section (menu, NULL, G_MENU_MODEL (section3));
}
/* Clipboard section */
gs_unref_object GMenu *section4 = g_menu_new ();
g_menu_append (section4, _("_Copy"), "win.copy::text");
g_menu_append (section4, _("Copy as _HTML"), "win.copy::html");
g_menu_append (section4, _("_Paste"), "win.paste-text");
if (g_action_get_enabled (G_ACTION (lookup_action (window, "paste-uris"))))
g_menu_append (section4, _("Paste as _Filenames"), "win.paste-uris");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section4));
/* Profile and property section */
gs_unref_object GMenu *section5 = g_menu_new ();
g_menu_append (section5, _("Read-_Only"), "win.read-only");
GMenuModel *profiles_menu = terminal_app_get_profile_section (app);
if (profiles_menu != NULL && g_menu_model_get_n_items (profiles_menu) > 1) {
gs_unref_object GMenu *submenu5 = g_menu_new ();
g_menu_append_section (submenu5, NULL, profiles_menu);
gs_unref_object GMenuItem *item5 = g_menu_item_new (_("P_rofiles"), NULL);
g_menu_item_set_submenu (item5, G_MENU_MODEL (submenu5));
g_menu_append_item (section5, item5);
}
g_menu_append (section5, _("_Preferences"), "win.edit-preferences");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section5));
/* New Terminal section */
gs_unref_object GMenu *section6 = g_menu_new ();
if (terminal_app_get_menu_unified (app)) {
gs_unref_object GMenuItem *item6 = g_menu_item_new (_("New _Terminal"), NULL);
g_menu_item_set_action_and_target (item6, "win.new-terminal",
"(ss)", "default", "current");
g_menu_append_item (section6, item6);
} else {
gs_unref_object GMenuItem *item61 = g_menu_item_new (_("New _Window"), NULL);
g_menu_item_set_action_and_target (item61, "win.new-terminal",
"(ss)", "window", "current");
g_menu_append_item (section6, item61);
gs_unref_object GMenuItem *item62 = g_menu_item_new (_("New _Tab"), NULL);
g_menu_item_set_action_and_target (item62, "win.new-terminal",
"(ss)", "tab", "current");
g_menu_append_item (section6, item62);
}
g_menu_append_section (menu, NULL, G_MENU_MODEL (section6));
/* Window section */
gs_unref_object GMenu *section7 = g_menu_new ();
/* Only show this if the WM doesn't show the menubar */
if (g_action_get_enabled (G_ACTION (lookup_action (window, "menubar-visible"))))
g_menu_append (section7, _("Show _Menubar"), "win.menubar-visible");
if (g_action_get_enabled (G_ACTION (lookup_action (window, "leave-fullscreen"))))
g_menu_append (section7, _("L_eave Full Screen"), "win.leave-fullscreen");
g_menu_append_section (menu, NULL, G_MENU_MODEL (section7));
/* Now create the popup menu and show it */
GtkWidget *popup_menu = context_menu_new (G_MENU_MODEL (menu), GTK_WIDGET (window));
/* Remove the popup info after the menu is done */
g_signal_connect (popup_menu, "selection-done",
G_CALLBACK (screen_popup_menu_selection_done_cb), window);
gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL,
NULL, NULL,
info->button,
info->timestamp);
if (info->button == 0)
gtk_menu_shell_select_first (GTK_MENU_SHELL (popup_menu), FALSE);
}
static gboolean
screen_match_clicked_cb (TerminalScreen *screen,
const char *url,
int url_flavor,
guint state,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (screen != priv->active_screen)
return FALSE;
gtk_widget_grab_focus (GTK_WIDGET (screen));
terminal_util_open_url (GTK_WIDGET (window), url, url_flavor,
gtk_get_current_event_time ());
return TRUE;
}
static void
screen_close_cb (TerminalScreen *screen,
TerminalWindow *window)
{
terminal_window_remove_screen (window, screen);
}
static void
notebook_update_tabs_menu_cb (GtkMenuButton *button,
TerminalWindow *window)
{
gs_unref_object GMenu *menu;
gs_free_list GList *tabs;
GList *t;
int i;
menu = g_menu_new ();
tabs = terminal_window_list_screen_containers (window);
for (t = tabs, i = 0; t != NULL; t = t->next, i++) {
TerminalScreenContainer *container = t->data;
TerminalScreen *screen = terminal_screen_container_get_screen (container);
gs_unref_object GMenuItem *item;
const char *title;
if (t->next == NULL) {
/* Last entry. If it has no dedicated shortcut "Switch to Tab N",
* display the accel of "Switch to Last Tab". */
GtkApplication *app = GTK_APPLICATION (g_application_get_default ());
gs_free gchar *detailed_action = g_strdup_printf("win.active-tab(%d)", i);
gs_strfreev gchar **accels = gtk_application_get_accels_for_action (app, detailed_action);
if (accels[0] == NULL)
i = -1;
}
title = terminal_screen_get_title (screen);
item = g_menu_item_new (title && title[0] ? title : _("Terminal"), NULL);
g_menu_item_set_action_and_target (item, "win.active-tab", "i", i);
g_menu_append_item (menu, item);
}
gtk_menu_button_set_menu_model (button, G_MENU_MODEL (menu));
/* Need this so the menu is positioned correctly */
gtk_widget_set_halign (GTK_WIDGET (gtk_menu_button_get_popup (button)), GTK_ALIGN_END);
}
static void
terminal_window_fill_notebook_action_box (TerminalWindow *window,
gboolean add_new_tab_button)
{
TerminalWindowPrivate *priv = window->priv;
GtkWidget *box, *new_tab_button, *tabs_menu_button;
box = terminal_notebook_get_action_box (TERMINAL_NOTEBOOK (priv->mdi_container), GTK_PACK_END);
/* Create the NewTerminal button */
if (add_new_tab_button)
{
new_tab_button = terminal_icon_button_new ("tab-new-symbolic");
gtk_actionable_set_action_name (GTK_ACTIONABLE (new_tab_button), "win.new-terminal");
gtk_actionable_set_action_target (GTK_ACTIONABLE (new_tab_button), "(ss)", "tab", "current");
gtk_box_pack_start (GTK_BOX (box), new_tab_button, FALSE, FALSE, 0);
gtk_widget_show (new_tab_button);
}
/* Create Tabs menu button */
tabs_menu_button = terminal_menu_button_new ();
g_signal_connect (tabs_menu_button, "update-menu",
G_CALLBACK (notebook_update_tabs_menu_cb), window);
gtk_box_pack_start (GTK_BOX (box), tabs_menu_button, FALSE, FALSE, 0);
gtk_menu_button_set_align_widget (GTK_MENU_BUTTON (tabs_menu_button), box);
gtk_widget_show (tabs_menu_button);
}
/*****************************************/
#ifdef ENABLE_DEBUG
static void
terminal_window_size_request_cb (GtkWidget *widget,
GtkRequisition *req)
{
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] size-request result %d : %d\n",
widget, req->width, req->height);
}
static void
terminal_window_size_allocate_cb (GtkWidget *widget,
GtkAllocation *allocation)
{
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] size-alloc result %d : %d at (%d, %d)\n",
widget,
allocation->width, allocation->height,
allocation->x, allocation->y);
}
#endif /* ENABLE_DEBUG */
static void
terminal_window_realize (GtkWidget *widget)
{
TerminalWindow *window = TERMINAL_WINDOW (widget);
TerminalWindowPrivate *priv = window->priv;
GtkAllocation widget_allocation;
gtk_widget_get_allocation (widget, &widget_allocation);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] realize, size %d : %d at (%d, %d)\n",
widget,
widget_allocation.width, widget_allocation.height,
widget_allocation.x, widget_allocation.y);
GTK_WIDGET_CLASS (terminal_window_parent_class)->realize (widget);
/* Now that we've been realized, we should know precisely how large the
* client-side decorations are going to be. Recalculate the geometry hints,
* export them to the windowing system, and resize the window accordingly. */
priv->realized = TRUE;
terminal_window_update_size (window);
}
static gboolean
terminal_window_state_event (GtkWidget *widget,
GdkEventWindowState *event)
{
gboolean (* window_state_event) (GtkWidget *, GdkEventWindowState *event) =
GTK_WIDGET_CLASS (terminal_window_parent_class)->window_state_event;
if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
{
TerminalWindow *window = TERMINAL_WINDOW (widget);
gboolean is_fullscreen;
is_fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0;
g_simple_action_set_state (lookup_action (window, "fullscreen"),
g_variant_new_boolean (is_fullscreen));
g_simple_action_set_enabled (lookup_action (window, "leave-fullscreen"),
is_fullscreen);
g_simple_action_set_enabled (lookup_action (window, "size-to"),
!is_fullscreen);
}
if (window_state_event)
return window_state_event (widget, event);
return FALSE;
}
static void
terminal_window_screen_update (TerminalWindow *window,
GdkScreen *screen)
{
GSettings *settings;
GtkSettings *gtk_settings;
if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (screen), "GT::HasSettingsConnection")))
return;
settings = terminal_app_get_global_settings (terminal_app_get ());
gtk_settings = gtk_settings_get_for_screen (screen);
g_object_set_data_full (G_OBJECT (screen), "GT::HasSettingsConnection",
gtk_settings,
(GDestroyNotify) app_setting_notify_destroy_cb);
g_settings_bind (settings,
TERMINAL_SETTING_ENABLE_SHORTCUTS_KEY,
gtk_settings,
"gtk-enable-accels",
G_SETTINGS_BIND_GET);
enable_menubar_accel_changed_cb (settings,
TERMINAL_SETTING_ENABLE_MENU_BAR_ACCEL_KEY,
gtk_settings);
g_signal_connect (settings, "changed::" TERMINAL_SETTING_ENABLE_MENU_BAR_ACCEL_KEY,
G_CALLBACK (enable_menubar_accel_changed_cb),
gtk_settings);
}
static void
terminal_window_screen_changed (GtkWidget *widget,
GdkScreen *previous_screen)
{
TerminalWindow *window = TERMINAL_WINDOW (widget);
void (* screen_changed) (GtkWidget *, GdkScreen *) =
GTK_WIDGET_CLASS (terminal_window_parent_class)->screen_changed;
GdkScreen *screen;
if (screen_changed)
screen_changed (widget, previous_screen);
screen = gtk_widget_get_screen (widget);
if (previous_screen == screen)
return;
if (!screen)
return;
terminal_window_screen_update (window, screen);
}
static void
terminal_window_init (TerminalWindow *window)
{
const GActionEntry action_entries[] = {
/* Actions without state */
{ "about", action_about_cb, NULL, NULL, NULL },
{ "close", action_close_cb, "s", NULL, NULL },
{ "copy", action_copy_cb, "s", NULL, NULL },
{ "copy-hyperlink", action_copy_hyperlink_cb, NULL, NULL, NULL },
{ "copy-match", action_copy_match_cb, NULL, NULL, NULL },
{ "edit-preferences", action_edit_preferences_cb, NULL, NULL, NULL },
{ "enter-fullscreen", action_enter_fullscreen_cb, NULL, NULL, NULL },
{ "find", action_find_cb, NULL, NULL, NULL },
{ "find-backward", action_find_backward_cb, NULL, NULL, NULL },
{ "find-clear", action_find_clear_cb, NULL, NULL, NULL },
{ "find-forward", action_find_forward_cb, NULL, NULL, NULL },
{ "help", action_help_cb, NULL, NULL, NULL },
{ "inspector", action_inspector_cb, NULL, NULL, NULL },
{ "leave-fullscreen", action_leave_fullscreen_cb, NULL, NULL, NULL },
{ "new-terminal", action_new_terminal_cb, "(ss)", NULL, NULL },
{ "open-match", action_open_match_cb, NULL, NULL, NULL },
{ "open-hyperlink", action_open_hyperlink_cb, NULL, NULL, NULL },
{ "paste-text", action_paste_text_cb, NULL, NULL, NULL },
{ "paste-uris", action_paste_uris_cb, NULL, NULL, NULL },
{ "reset", action_reset_cb, "b", NULL, NULL },
{ "select-all", action_select_all_cb, NULL, NULL, NULL },
{ "size-to", action_size_to_cb, "(uu)", NULL, NULL },
{ "tab-detach", action_tab_detach_cb, NULL, NULL, NULL },
{ "tab-move-left", action_tab_move_left_cb, NULL, NULL, NULL },
{ "tab-move-right", action_tab_move_right_cb, NULL, NULL, NULL },
{ "tab-switch-left", action_tab_switch_left_cb, NULL, NULL, NULL },
{ "tab-switch-right", action_tab_switch_right_cb, NULL, NULL, NULL },
{ "tabs-menu", NULL, NULL, NULL, NULL },
{ "zoom-in", action_zoom_in_cb, NULL, NULL, NULL },
{ "zoom-normal", action_zoom_normal_cb, NULL, NULL, NULL },
{ "zoom-out", action_zoom_out_cb, NULL, NULL, NULL },
#ifdef ENABLE_EXPORT
{ "export", action_export_cb, NULL, NULL, NULL },
#endif
#ifdef ENABLE_PRINT
{ "print", action_print_cb, NULL, NULL, NULL },
#endif
#ifdef ENABLE_SAVE
{ "save-contents", action_save_contents_cb, NULL, NULL, NULL },
#endif
/* Shadow actions for keybinding comsumption, see comment in terminal-accels.c */
{ "shadow", action_shadow_activate_cb, "s", NULL, NULL },
{ "shadow-mdi", action_shadow_activate_cb, "s", NULL, NULL },
/* Actions with state */
{ "active-tab", action_active_tab_set_cb, "i", "@i 0", action_active_tab_state_cb },
{ "header-menu", NULL /* toggles state */, NULL, "false", NULL },
{ "fullscreen", NULL /* toggles state */, NULL, "false", action_fullscreen_state_cb },
{ "menubar-visible", NULL /* toggles state */, NULL, "true", action_menubar_visible_state_cb },
{ "profile", NULL /* changes state */, "s", "''", action_profile_state_cb },
{ "read-only", NULL /* toggles state */, NULL, "false", action_read_only_state_cb },
};
TerminalWindowPrivate *priv;
TerminalApp *app;
GSettings *gtk_debug_settings;
GtkWindowGroup *window_group;
// GtkAccelGroup *accel_group;
uuid_t u;
char uuidstr[37], role[64];
gboolean shell_shows_menubar;
gboolean use_headerbar;
GSimpleAction *action;
app = terminal_app_get ();
priv = window->priv = G_TYPE_INSTANCE_GET_PRIVATE (window, TERMINAL_TYPE_WINDOW, TerminalWindowPrivate);
gtk_widget_init_template (GTK_WIDGET (window));
uuid_generate (u);
uuid_unparse (u, uuidstr);
priv->uuid = g_strdup (uuidstr);
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK(terminal_window_delete_event),
NULL);
#ifdef ENABLE_DEBUG
_TERMINAL_DEBUG_IF (TERMINAL_DEBUG_GEOMETRY)
{
g_signal_connect_after (window, "size-request", G_CALLBACK (terminal_window_size_request_cb), NULL);
g_signal_connect_after (window, "size-allocate", G_CALLBACK (terminal_window_size_allocate_cb), NULL);
}
#endif
use_headerbar = terminal_app_get_use_headerbar (app);
if (use_headerbar) {
GtkWidget *headerbar;
headerbar = terminal_headerbar_new ();
gtk_window_set_titlebar (GTK_WINDOW (window), headerbar);
}
gtk_window_set_title (GTK_WINDOW (window), _("Terminal"));
priv->active_screen = NULL;
priv->main_vbox = gtk_bin_get_child (GTK_BIN (window));
priv->mdi_container = TERMINAL_MDI_CONTAINER (terminal_notebook_new ());
g_signal_connect (priv->mdi_container, "screen-close-request",
G_CALLBACK (screen_close_request_cb), window);
g_signal_connect_after (priv->mdi_container, "screen-switched",
G_CALLBACK (mdi_screen_switched_cb), window);
g_signal_connect_after (priv->mdi_container, "screen-added",
G_CALLBACK (mdi_screen_added_cb), window);
g_signal_connect_after (priv->mdi_container, "screen-removed",
G_CALLBACK (mdi_screen_removed_cb), window);
g_signal_connect_after (priv->mdi_container, "screens-reordered",
G_CALLBACK (mdi_screens_reordered_cb), window);
g_signal_connect_swapped (priv->mdi_container, "notify::tab-pos",
G_CALLBACK (terminal_window_update_geometry), window);
g_signal_connect_swapped (priv->mdi_container, "notify::show-tabs",
G_CALLBACK (terminal_window_update_geometry), window);
/* FIXME hack hack! */
if (GTK_IS_NOTEBOOK (priv->mdi_container)) {
g_signal_connect (priv->mdi_container, "button-press-event",
G_CALLBACK (notebook_button_press_cb), window);
g_signal_connect (priv->mdi_container, "popup-menu",
G_CALLBACK (notebook_popup_menu_cb), window);
g_signal_connect (priv->mdi_container, "create-window",
G_CALLBACK (handle_tab_droped_on_desktop), window);
}
gtk_box_pack_end (GTK_BOX (priv->main_vbox), GTK_WIDGET (priv->mdi_container), TRUE, TRUE, 0);
gtk_widget_show (GTK_WIDGET (priv->mdi_container));
priv->old_char_width = -1;
priv->old_char_height = -1;
priv->old_chrome_width = -1;
priv->old_chrome_height = -1;
priv->old_csd_width = -1;
priv->old_csd_height = -1;
priv->old_padding_width = -1;
priv->old_padding_height = -1;
priv->old_geometry_widget = NULL;
/* GAction setup */
g_action_map_add_action_entries (G_ACTION_MAP (window),
action_entries, G_N_ELEMENTS (action_entries),
window);
g_simple_action_set_enabled (lookup_action (window, "leave-fullscreen"), FALSE);
GSettings *global_settings = terminal_app_get_global_settings (app);
enable_mnemonics_changed_cb (global_settings, TERMINAL_SETTING_ENABLE_MNEMONICS_KEY, window);
g_signal_connect (global_settings, "changed::" TERMINAL_SETTING_ENABLE_MNEMONICS_KEY,
G_CALLBACK (enable_mnemonics_changed_cb), window);
/* Hide "menubar-visible" when the menubar is shown by the shell */
g_object_get (gtk_widget_get_settings (GTK_WIDGET (window)),
"gtk-shell-shows-menubar", &shell_shows_menubar,
NULL);
if (shell_shows_menubar) {
g_simple_action_set_enabled (lookup_action (window, "menubar-visible"), FALSE);
} else {
priv->menubar = gtk_menu_bar_new_from_model (terminal_app_get_menubar (app));
gtk_box_pack_start (GTK_BOX (priv->main_vbox),
priv->menubar,
FALSE, FALSE, 0);
terminal_window_set_menubar_visible (window, !use_headerbar);
priv->use_default_menubar_visibility = !use_headerbar;
}
/* Maybe make Inspector available */
action = lookup_action (window, "inspector");
gtk_debug_settings = terminal_app_get_gtk_debug_settings (app);
if (gtk_debug_settings != NULL)
g_settings_bind (gtk_debug_settings,
"enable-inspector-keybinding",
action,
"enabled",
G_SETTINGS_BIND_GET | G_SETTINGS_BIND_NO_SENSITIVITY);
else
g_simple_action_set_enabled (action, FALSE);
priv->clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), GDK_SELECTION_CLIPBOARD);
clipboard_targets_changed_cb (app, priv->clipboard, window);
g_signal_connect (app, "clipboard-targets-changed",
G_CALLBACK (clipboard_targets_changed_cb), window);
terminal_window_fill_notebook_action_box (window, !use_headerbar);
/* We have to explicitly call this, since screen-changed is NOT
* emitted for the toplevel the first time!
*/
terminal_window_screen_update (window, gtk_widget_get_screen (GTK_WIDGET (window)));
window_group = gtk_window_group_new ();
gtk_window_group_add_window (window_group, GTK_WINDOW (window));
g_object_unref (window_group);
g_snprintf (role, sizeof (role), "gnome-terminal-window-%s", uuidstr);
gtk_window_set_role (GTK_WINDOW (window), role);
}
static void
terminal_window_style_updated (GtkWidget *widget)
{
TerminalWindow *window = TERMINAL_WINDOW (widget);
GTK_WIDGET_CLASS (terminal_window_parent_class)->style_updated (widget);
terminal_window_update_size (window);
}
static void
terminal_window_class_init (TerminalWindowClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
object_class->dispose = terminal_window_dispose;
object_class->finalize = terminal_window_finalize;
widget_class->show = terminal_window_show;
widget_class->realize = terminal_window_realize;
widget_class->window_state_event = terminal_window_state_event;
widget_class->screen_changed = terminal_window_screen_changed;
widget_class->style_updated = terminal_window_style_updated;
GtkWindowClass *window_klass;
GtkBindingSet *binding_set;
window_klass = g_type_class_ref (GTK_TYPE_WINDOW);
binding_set = gtk_binding_set_by_class (window_klass);
gtk_binding_entry_skip (binding_set, GDK_KEY_I, GDK_CONTROL_MASK|GDK_SHIFT_MASK);
gtk_binding_entry_skip (binding_set, GDK_KEY_D, GDK_CONTROL_MASK|GDK_SHIFT_MASK);
g_type_class_unref (window_klass);
g_type_class_add_private (object_class, sizeof (TerminalWindowPrivate));
gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/terminal/ui/window.ui");
gtk_widget_class_set_css_name(widget_class, TERMINAL_WINDOW_CSS_NAME);
}
static void
terminal_window_dispose (GObject *object)
{
TerminalWindow *window = TERMINAL_WINDOW (object);
TerminalWindowPrivate *priv = window->priv;
TerminalApp *app = terminal_app_get ();
if (!priv->disposed) {
GSettings *global_settings = terminal_app_get_global_settings (app);
g_signal_handlers_disconnect_by_func (global_settings,
G_CALLBACK (enable_mnemonics_changed_cb),
window);
}
priv->disposed = TRUE;
if (priv->clipboard != NULL) {
g_signal_handlers_disconnect_by_func (app,
G_CALLBACK (clipboard_targets_changed_cb),
window);
priv->clipboard = NULL;
}
remove_popup_info (window);
if (priv->search_popover != NULL)
{
g_signal_handlers_disconnect_matched (priv->search_popover, G_SIGNAL_MATCH_DATA,
0, 0, NULL, NULL, window);
gtk_widget_destroy (GTK_WIDGET (priv->search_popover));
priv->search_popover = NULL;
}
G_OBJECT_CLASS (terminal_window_parent_class)->dispose (object);
}
static void
terminal_window_finalize (GObject *object)
{
TerminalWindow *window = TERMINAL_WINDOW (object);
TerminalWindowPrivate *priv = window->priv;
if (priv->confirm_close_dialog)
gtk_dialog_response (GTK_DIALOG (priv->confirm_close_dialog),
GTK_RESPONSE_DELETE_EVENT);
g_free (priv->uuid);
G_OBJECT_CLASS (terminal_window_parent_class)->finalize (object);
}
static gboolean
terminal_window_delete_event (GtkWidget *widget,
GdkEvent *event,
gpointer data)
{
return confirm_close_window_or_tab (TERMINAL_WINDOW (widget), NULL);
}
static void
terminal_window_show (GtkWidget *widget)
{
TerminalWindow *window = TERMINAL_WINDOW (widget);
TerminalWindowPrivate *priv = window->priv;
GtkAllocation widget_allocation;
gtk_widget_get_allocation (widget, &widget_allocation);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] show, size %d : %d at (%d, %d)\n",
widget,
widget_allocation.width, widget_allocation.height,
widget_allocation.x, widget_allocation.y);
/* Because of the unexpected reentrancy caused by adding the tab to the notebook
* showing the TerminalWindow, we can get here when the first page has been
* added but not yet set current. By setting the page current, we get the
* right size when we first show the window */
if (GTK_IS_NOTEBOOK (priv->mdi_container) &&
gtk_notebook_get_current_page (GTK_NOTEBOOK (priv->mdi_container)) == -1)
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->mdi_container), 0);
if (priv->active_screen != NULL)
{
/* At this point, we have our GdkScreen, and hence the right
* font size, so we can go ahead and size the window. */
terminal_window_update_size (window);
}
GTK_WIDGET_CLASS (terminal_window_parent_class)->show (widget);
}
TerminalWindow*
terminal_window_new (GApplication *app)
{
return g_object_new (TERMINAL_TYPE_WINDOW,
"application", app,
"show-menubar", FALSE,
NULL);
}
static void
profile_set_cb (TerminalScreen *screen,
GSettings *old_profile,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (!gtk_widget_get_realized (GTK_WIDGET (window)))
return;
if (screen != priv->active_screen)
return;
terminal_window_update_set_profile_menu_active_profile (window);
}
static void
sync_screen_title (TerminalScreen *screen,
GParamSpec *psepc,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
const char *title;
if (screen != priv->active_screen)
return;
title = terminal_screen_get_title (screen);
gtk_window_set_title (GTK_WINDOW (window),
title && title[0] ? title : _("Terminal"));
}
static void
screen_font_any_changed_cb (TerminalScreen *screen,
GParamSpec *psepc,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
if (!gtk_widget_get_realized (GTK_WIDGET (window)))
return;
if (screen != priv->active_screen)
return;
terminal_window_update_size (window);
}
static void
screen_hyperlink_hover_uri_changed (TerminalScreen *screen,
const char *uri,
const GdkRectangle *bbox G_GNUC_UNUSED,
TerminalWindow *window G_GNUC_UNUSED)
{
gs_free char *label = NULL;
if (!gtk_widget_get_realized (GTK_WIDGET (screen)))
return;
label = terminal_util_hyperlink_uri_label (uri);
gtk_widget_set_tooltip_text (GTK_WIDGET (screen), label);
}
/* MDI container callbacks */
static void
screen_close_request_cb (TerminalMdiContainer *container,
TerminalScreen *screen,
TerminalWindow *window)
{
if (confirm_close_window_or_tab (window, screen))
return;
terminal_window_remove_screen (window, screen);
}
int
terminal_window_get_active_screen_num (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
return terminal_mdi_container_get_active_screen_num (priv->mdi_container);
}
void
terminal_window_add_screen (TerminalWindow *window,
TerminalScreen *screen,
int position)
{
TerminalWindowPrivate *priv = window->priv;
GtkWidget *old_window;
old_window = gtk_widget_get_toplevel (GTK_WIDGET (screen));
if (gtk_widget_is_toplevel (old_window) &&
TERMINAL_IS_WINDOW (old_window) &&
TERMINAL_WINDOW (old_window)== window)
return;
if (TERMINAL_IS_WINDOW (old_window))
terminal_window_remove_screen (TERMINAL_WINDOW (old_window), screen);
if (position == -1) {
GSettings *global_settings = terminal_app_get_global_settings (terminal_app_get ());
TerminalNewTabPosition position_pref = g_settings_get_enum (global_settings,
TERMINAL_SETTING_NEW_TAB_POSITION_KEY);
switch (position_pref) {
case TERMINAL_NEW_TAB_POSITION_NEXT:
position = terminal_window_get_active_screen_num (window) + 1;
break;
default:
case TERMINAL_NEW_TAB_POSITION_LAST:
position = -1;
break;
}
}
terminal_mdi_container_add_screen (priv->mdi_container, screen, position);
}
void
terminal_window_remove_screen (TerminalWindow *window,
TerminalScreen *screen)
{
TerminalWindowPrivate *priv = window->priv;
terminal_mdi_container_remove_screen (priv->mdi_container, screen);
}
void
terminal_window_move_screen (TerminalWindow *source_window,
TerminalWindow *dest_window,
TerminalScreen *screen,
int dest_position)
{
TerminalScreenContainer *screen_container;
g_return_if_fail (TERMINAL_IS_WINDOW (source_window));
g_return_if_fail (TERMINAL_IS_WINDOW (dest_window));
g_return_if_fail (TERMINAL_IS_SCREEN (screen));
g_return_if_fail (gtk_widget_get_toplevel (GTK_WIDGET (screen)) == GTK_WIDGET (source_window));
g_return_if_fail (dest_position >= -1);
screen_container = terminal_screen_container_get_from_screen (screen);
g_assert (TERMINAL_IS_SCREEN_CONTAINER (screen_container));
/* We have to ref the screen container as well as the screen,
* because otherwise removing the screen container from the source
* window's notebook will cause the container and its containing
* screen to be gtk_widget_destroy()ed!
*/
g_object_ref_sink (screen_container);
g_object_ref_sink (screen);
terminal_window_remove_screen (source_window, screen);
/* Now we can safely remove the screen from the container and let the container die */
gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (screen))), GTK_WIDGET (screen));
g_object_unref (screen_container);
terminal_window_add_screen (dest_window, screen, dest_position);
terminal_mdi_container_set_active_screen (dest_window->priv->mdi_container, screen);
g_object_unref (screen);
}
GList*
terminal_window_list_screen_containers (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
return terminal_mdi_container_list_screen_containers (priv->mdi_container);
}
void
terminal_window_set_menubar_visible (TerminalWindow *window,
gboolean setting)
{
TerminalWindowPrivate *priv = window->priv;
if (priv->menubar == NULL)
return;
/* it's been set now, so don't override when adding a screen.
* this side effect must happen before we short-circuit below.
*/
priv->use_default_menubar_visibility = FALSE;
g_simple_action_set_state (lookup_action (window, "menubar-visible"),
g_variant_new_boolean (setting));
g_object_set (priv->menubar, "visible", setting, NULL);
/* FIXMEchpe: use gtk_widget_get_realized instead? */
if (priv->active_screen)
{
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] setting size after toggling menubar visibility\n",
window);
terminal_window_update_size (window);
}
}
GtkWidget *
terminal_window_get_mdi_container (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
g_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL);
return GTK_WIDGET (priv->mdi_container);
}
void
terminal_window_update_size (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
int grid_width, grid_height;
int pixel_width, pixel_height;
GdkWindow *gdk_window;
gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
if (gdk_window != NULL &&
(gdk_window_get_state (gdk_window) &
(GDK_WINDOW_STATE_MAXIMIZED | WINDOW_STATE_TILED | GDK_WINDOW_STATE_FULLSCREEN)))
{
/* Don't adjust the size of maximized or tiled (snapped, half-maximized)
* windows: if we do, there will be ugly gaps of up to 1 character cell
* around otherwise tiled windows. */
return;
}
/* be sure our geometry is up-to-date */
terminal_window_update_geometry (window);
terminal_screen_get_size (priv->active_screen, &grid_width, &grid_height);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] size is %dx%d cells of %dx%d px\n",
window, grid_width, grid_height,
priv->old_char_width, priv->old_char_height);
/* the "old" struct members were updated by update_geometry */
pixel_width = priv->old_chrome_width + grid_width * priv->old_char_width;
pixel_height = priv->old_chrome_height + grid_height * priv->old_char_height;
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] %dx%d + %dx%d = %dx%d\n",
window, grid_width * priv->old_char_width,
grid_height * priv->old_char_height,
priv->old_chrome_width, priv->old_chrome_height,
pixel_width, pixel_height);
gtk_window_resize (GTK_WINDOW (window), pixel_width, pixel_height);
}
void
terminal_window_switch_screen (TerminalWindow *window,
TerminalScreen *screen)
{
TerminalWindowPrivate *priv = window->priv;
terminal_mdi_container_set_active_screen (priv->mdi_container, screen);
}
TerminalScreen*
terminal_window_get_active (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
return terminal_mdi_container_get_active_screen (priv->mdi_container);
}
static void
notebook_show_context_menu (TerminalWindow *window,
GdkEvent *event,
guint button,
guint32 timestamp)
{
/* Load the UI */
gs_unref_object GMenu *menu;
terminal_util_load_objects_resource ("/org/gnome/terminal/ui/notebook-menu.ui",
"notebook-popup", &menu,
NULL);
GtkWidget *popup_menu = context_menu_new (G_MENU_MODEL (menu), GTK_WIDGET (window));
gtk_widget_set_halign (popup_menu, GTK_ALIGN_START);
gtk_menu_popup (GTK_MENU (popup_menu), NULL, NULL,
NULL, NULL,
button, timestamp);
if (button == 0)
gtk_menu_shell_select_first (GTK_MENU_SHELL (popup_menu), FALSE);
}
static gboolean
notebook_button_press_cb (GtkWidget *widget,
GdkEventButton *event,
TerminalWindow *window)
{
GtkNotebook *notebook = GTK_NOTEBOOK (widget);
int tab_clicked;
if (event->type != GDK_BUTTON_PRESS ||
event->button != GDK_BUTTON_SECONDARY ||
(event->state & gtk_accelerator_get_default_mod_mask ()) != 0)
return FALSE;
tab_clicked = find_tab_num_at_pos (notebook, event->x_root, event->y_root);
if (tab_clicked < 0)
return FALSE;
/* switch to the page the mouse is over */
gtk_notebook_set_current_page (notebook, tab_clicked);
notebook_show_context_menu (window, (GdkEvent*)event, event->button, event->time);
return TRUE;
}
static gboolean
notebook_popup_menu_cb (GtkWidget *widget,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
GtkWidget *focus_widget;
focus_widget = gtk_window_get_focus (GTK_WINDOW (window));
/* Only respond if the notebook is the actual focus */
if (focus_widget != GTK_WIDGET (priv->mdi_container))
return FALSE;
notebook_show_context_menu (window, NULL, 0, gtk_get_current_event_time ());
return TRUE;
}
static void
mdi_screen_switched_cb (TerminalMdiContainer *container,
TerminalScreen *old_active_screen,
TerminalScreen *screen,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
int old_grid_width, old_grid_height;
_terminal_debug_print (TERMINAL_DEBUG_MDI,
"[window %p] MDI: screen-switched old %p new %p\n",
window, old_active_screen, screen);
if (priv->disposed)
return;
if (screen == NULL || old_active_screen == screen)
return;
if (priv->search_popover != NULL)
gtk_widget_hide (GTK_WIDGET (priv->search_popover));
_terminal_debug_print (TERMINAL_DEBUG_MDI,
"[window %p] MDI: setting active tab to screen %p (old active screen %p)\n",
window, screen, priv->active_screen);
if (old_active_screen != NULL && screen != NULL) {
terminal_screen_get_size (old_active_screen, &old_grid_width, &old_grid_height);
/* This is so that we maintain the same grid */
vte_terminal_set_size (VTE_TERMINAL (screen), old_grid_width, old_grid_height);
}
priv->active_screen = screen;
/* Override menubar setting if it wasn't restored from session */
if (priv->use_default_menubar_visibility)
{
gboolean setting =
g_settings_get_boolean (terminal_app_get_global_settings (terminal_app_get ()),
TERMINAL_SETTING_DEFAULT_SHOW_MENUBAR_KEY);
terminal_window_set_menubar_visible (window, setting);
}
sync_screen_title (screen, NULL, window);
/* set size of window to current grid size */
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] setting size after flipping notebook pages\n",
window);
terminal_window_update_size (window);
terminal_window_update_tabs_actions_sensitivity (window);
terminal_window_update_terminal_menu (window);
terminal_window_update_set_profile_menu_active_profile (window);
terminal_window_update_copy_sensitivity (screen, window);
terminal_window_update_zoom_sensitivity (window);
terminal_window_update_search_sensitivity (screen, window);
terminal_window_update_paste_sensitivity (window);
}
static void
mdi_screen_added_cb (TerminalMdiContainer *container,
TerminalScreen *screen,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
int pages;
_terminal_debug_print (TERMINAL_DEBUG_MDI,
"[window %p] MDI: screen %p inserted\n",
window, screen);
g_signal_connect (G_OBJECT (screen),
"profile-set",
G_CALLBACK (profile_set_cb),
window);
/* FIXME: only connect on the active screen, not all screens! */
g_signal_connect (screen, "notify::title",
G_CALLBACK (sync_screen_title), window);
g_signal_connect (screen, "notify::font-desc",
G_CALLBACK (screen_font_any_changed_cb), window);
g_signal_connect (screen, "notify::font-scale",
G_CALLBACK (screen_font_any_changed_cb), window);
g_signal_connect (screen, "notify::cell-height-scale",
G_CALLBACK (screen_font_any_changed_cb), window);
g_signal_connect (screen, "notify::cell-width-scale",
G_CALLBACK (screen_font_any_changed_cb), window);
g_signal_connect (screen, "selection-changed",
G_CALLBACK (terminal_window_update_copy_sensitivity), window);
g_signal_connect (screen, "hyperlink-hover-uri-changed",
G_CALLBACK (screen_hyperlink_hover_uri_changed), window);
g_signal_connect (screen, "show-popup-menu",
G_CALLBACK (screen_show_popup_menu_cb), window);
g_signal_connect (screen, "match-clicked",
G_CALLBACK (screen_match_clicked_cb), window);
g_signal_connect (screen, "resize-window",
G_CALLBACK (screen_resize_window_cb), window);
g_signal_connect (screen, "close-screen",
G_CALLBACK (screen_close_cb), window);
terminal_window_update_tabs_actions_sensitivity (window);
terminal_window_update_search_sensitivity (screen, window);
terminal_window_update_paste_sensitivity (window);
#if 0
/* FIXMEchpe: wtf is this doing? */
/* If we have an active screen, match its size and zoom */
if (priv->active_screen)
{
int current_width, current_height;
double scale;
terminal_screen_get_size (priv->active_screen, ¤t_width, ¤t_height);
vte_terminal_set_size (VTE_TERMINAL (screen), current_width, current_height);
scale = terminal_screen_get_font_scale (priv->active_screen);
terminal_screen_set_font_scale (screen, scale);
}
#endif
if (priv->present_on_insert)
{
gtk_window_present_with_time (GTK_WINDOW (window), gtk_get_current_event_time ());
priv->present_on_insert = FALSE;
}
pages = terminal_mdi_container_get_n_screens (container);
if (pages == 2)
{
terminal_window_update_size (window);
}
}
static void
mdi_screen_removed_cb (TerminalMdiContainer *container,
TerminalScreen *screen,
TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
int pages;
if (priv->disposed)
return;
_terminal_debug_print (TERMINAL_DEBUG_MDI,
"[window %p] MDI: screen %p removed\n",
window, screen);
g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
G_CALLBACK (profile_set_cb),
window);
g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
G_CALLBACK (sync_screen_title),
window);
g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
G_CALLBACK (screen_font_any_changed_cb),
window);
g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
G_CALLBACK (terminal_window_update_copy_sensitivity),
window);
g_signal_handlers_disconnect_by_func (G_OBJECT (screen),
G_CALLBACK (screen_hyperlink_hover_uri_changed),
window);
g_signal_handlers_disconnect_by_func (screen,
G_CALLBACK (screen_show_popup_menu_cb),
window);
g_signal_handlers_disconnect_by_func (screen,
G_CALLBACK (screen_match_clicked_cb),
window);
g_signal_handlers_disconnect_by_func (screen,
G_CALLBACK (screen_resize_window_cb),
window);
g_signal_handlers_disconnect_by_func (screen,
G_CALLBACK (screen_close_cb),
window);
/* We already got a switch-page signal whose handler sets the active tab to the
* new active tab, unless this screen was the only one in the notebook, so
* priv->active_tab is valid here.
*/
pages = terminal_mdi_container_get_n_screens (container);
if (pages == 0)
{
priv->active_screen = NULL;
/* That was the last tab in the window; close it. */
gtk_widget_destroy (GTK_WIDGET (window));
return;
}
terminal_window_update_tabs_actions_sensitivity (window);
terminal_window_update_search_sensitivity (screen, window);
if (pages == 1)
{
TerminalScreen *active_screen = terminal_mdi_container_get_active_screen (container);
gtk_widget_grab_focus (GTK_WIDGET(active_screen)); /* bug 742422 */
terminal_window_update_size (window);
}
}
static void
mdi_screens_reordered_cb (TerminalMdiContainer *container,
TerminalWindow *window)
{
terminal_window_update_tabs_actions_sensitivity (window);
}
gboolean
terminal_window_parse_geometry (TerminalWindow *window,
const char *geometry)
{
TerminalWindowPrivate *priv = window->priv;
/* gtk_window_parse_geometry() needs to have the right base size
* and width/height increment to compute the window size from
* the geometry.
*/
terminal_window_update_geometry (window);
if (!gtk_window_parse_geometry (GTK_WINDOW (window), geometry))
return FALSE;
/* We won't actually get allocated at the size parsed out of the
* geometry until the window is shown. If terminal_window_update_size()
* is called between now and then, that could result in us getting
* snapped back to the old grid size. So we need to immediately
* update the size of the active terminal to grid size from the
* geometry.
*/
if (priv->active_screen)
{
int grid_width, grid_height;
/* After parse_geometry(), the default size is in units of the
* width/height increment, not a pixel size */
gtk_window_get_default_size (GTK_WINDOW (window), &grid_width, &grid_height);
vte_terminal_set_size (VTE_TERMINAL (priv->active_screen),
grid_width, grid_height);
}
return TRUE;
}
void
terminal_window_update_geometry (TerminalWindow *window)
{
TerminalWindowPrivate *priv = window->priv;
GtkWidget *widget;
GdkGeometry hints;
GtkBorder padding;
GtkRequisition vbox_request, widget_request;
int grid_width, grid_height;
int char_width, char_height;
int chrome_width, chrome_height;
int csd_width = 0, csd_height = 0;
if (gtk_widget_in_destruction (GTK_WIDGET (window)))
return;
if (priv->active_screen == NULL)
return;
widget = GTK_WIDGET (priv->active_screen);
/* We set geometry hints from the active term; best thing
* I can think of to do. Other option would be to try to
* get some kind of union of all hints from all terms in the
* window, but that doesn't make too much sense.
*/
terminal_screen_get_cell_size (priv->active_screen, &char_width, &char_height);
terminal_screen_get_size (priv->active_screen, &grid_width, &grid_height);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "%dx%d cells of %dx%d px = %dx%d px\n",
grid_width, grid_height, char_width, char_height,
char_width * grid_width, char_height * grid_height);
gtk_style_context_get_padding(gtk_widget_get_style_context(widget),
gtk_widget_get_state_flags(widget),
&padding);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "padding = %dx%d px\n",
padding.left + padding.right,
padding.top + padding.bottom);
gtk_widget_get_preferred_size (priv->main_vbox, NULL, &vbox_request);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "content area requests %dx%d px\n",
vbox_request.width, vbox_request.height);
chrome_width = vbox_request.width - (char_width * grid_width);
chrome_height = vbox_request.height - (char_height * grid_height);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "chrome: %dx%d px\n",
chrome_width, chrome_height);
if (priv->realized)
{
/* Only when having been realize the CSD can be calculated. Do this by
* using the actual allocation rather then the preferred size as the
* the preferred size takes the natural size of e.g. the title bar into
* account which can be far wider then the contents size when using a
* very long title */
GtkAllocation toplevel_allocation, vbox_allocation;
gtk_widget_get_allocation (GTK_WIDGET (priv->main_vbox), &vbox_allocation);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"terminal widget allocation %dx%d px\n",
vbox_allocation.width, vbox_allocation.height);
gtk_widget_get_allocation (GTK_WIDGET (window), &toplevel_allocation);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "window allocation %dx%d px\n",
toplevel_allocation.width, toplevel_allocation.height);
csd_width = toplevel_allocation.width - vbox_allocation.width;
csd_height = toplevel_allocation.height - vbox_allocation.height;
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "CSDs: %dx%d px\n",
csd_width, csd_height);
}
gtk_widget_get_preferred_size (widget, NULL, &widget_request);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "terminal widget requests %dx%d px\n",
widget_request.width, widget_request.height);
if (!priv->realized)
{
/* Don't actually set the geometry hints until we have been realized,
* because we don't know how large the client-side decorations are going
* to be. We also avoid setting priv->old_csd_width or
* priv->old_csd_height, so that next time through this function we'll
* definitely recalculate the hints.
*
* Similarly, the size request doesn't seem to include the padding
* until we've been redrawn at least once. Don't resize the window
* until we've done that. */
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY, "not realized yet\n");
}
else if (char_width != priv->old_char_width ||
char_height != priv->old_char_height ||
padding.left + padding.right != priv->old_padding_width ||
padding.top + padding.bottom != priv->old_padding_height ||
chrome_width != priv->old_chrome_width ||
chrome_height != priv->old_chrome_height ||
csd_width != priv->old_csd_width ||
csd_height != priv->old_csd_height ||
widget != (GtkWidget*) priv->old_geometry_widget)
{
hints.base_width = chrome_width + csd_width;
hints.base_height = chrome_height + csd_height;
hints.width_inc = char_width;
hints.height_inc = char_height;
/* min size is min size of the whole window, remember. */
hints.min_width = hints.base_width + hints.width_inc * MIN_WIDTH_CHARS;
hints.min_height = hints.base_height + hints.height_inc * MIN_HEIGHT_CHARS;
gtk_window_set_geometry_hints (GTK_WINDOW (window),
NULL,
&hints,
GDK_HINT_RESIZE_INC |
GDK_HINT_MIN_SIZE |
GDK_HINT_BASE_SIZE);
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] hints: base %dx%d min %dx%d inc %d %d\n",
window,
hints.base_width,
hints.base_height,
hints.min_width,
hints.min_height,
hints.width_inc,
hints.height_inc);
priv->old_csd_width = csd_width;
priv->old_csd_height = csd_height;
priv->old_geometry_widget = widget;
}
else
{
_terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
"[window %p] hints: increment unchanged, not setting\n",
window);
}
/* We need these for the size calculation in terminal_window_update_size()
* (at least under GTK >= 3.19), so we set them unconditionally. */
priv->old_char_width = char_width;
priv->old_char_height = char_height;
priv->old_chrome_width = chrome_width;
priv->old_chrome_height = chrome_height;
priv->old_padding_width = padding.left + padding.right;
priv->old_padding_height = padding.top + padding.bottom;
}
static void
confirm_close_response_cb (GtkWidget *dialog,
int response,
TerminalWindow *window)
{
TerminalScreen *screen;
screen = g_object_get_data (G_OBJECT (dialog), "close-screen");
gtk_widget_destroy (dialog);
if (response != GTK_RESPONSE_ACCEPT)
return;
if (screen)
terminal_window_remove_screen (window, screen);
else
gtk_widget_destroy (GTK_WIDGET (window));
}
/* Returns: TRUE if closing needs to wait until user confirmation;
* FALSE if the terminal or window can close immediately.
*/
static gboolean
confirm_close_window_or_tab (TerminalWindow *window,
TerminalScreen *screen)
{
TerminalWindowPrivate *priv = window->priv;
GtkWidget *dialog;
gboolean do_confirm;
int n_tabs;
if (priv->confirm_close_dialog)
{
/* WTF, already have one? It's modal, so how did that happen? */
gtk_dialog_response (GTK_DIALOG (priv->confirm_close_dialog),
GTK_RESPONSE_DELETE_EVENT);
}
do_confirm = g_settings_get_boolean (terminal_app_get_global_settings (terminal_app_get ()),
TERMINAL_SETTING_CONFIRM_CLOSE_KEY);
if (!do_confirm)
return FALSE;
if (screen)
{
do_confirm = terminal_screen_has_foreground_process (screen, NULL, NULL);
n_tabs = 1;
}
else
{
GList *tabs, *t;
do_confirm = FALSE;
tabs = terminal_window_list_screen_containers (window);
n_tabs = g_list_length (tabs);
for (t = tabs; t != NULL; t = t->next)
{
TerminalScreen *terminal_screen;
terminal_screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (t->data));
if (terminal_screen_has_foreground_process (terminal_screen, NULL, NULL))
{
do_confirm = TRUE;
break;
}
}
g_list_free (tabs);
}
if (!do_confirm)
return FALSE;
dialog = priv->confirm_close_dialog =
gtk_message_dialog_new (GTK_WINDOW (window),
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_WARNING,
GTK_BUTTONS_CANCEL,
"%s", n_tabs > 1 ? _("Close this window?") : _("Close this terminal?"));
if (n_tabs > 1)
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", _("There are still processes running in some terminals in this window. "
"Closing the window will kill all of them."));
else
gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
"%s", _("There is still a process running in this terminal. "
"Closing the terminal will kill it."));
gtk_window_set_title (GTK_WINDOW (dialog), "");
gtk_dialog_add_button (GTK_DIALOG (dialog), n_tabs > 1 ? _("C_lose Window") : _("C_lose Terminal"), GTK_RESPONSE_ACCEPT);
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
g_object_set_data (G_OBJECT (dialog), "close-screen", screen);
g_signal_connect (dialog, "destroy",
G_CALLBACK (gtk_widget_destroyed), &priv->confirm_close_dialog);
g_signal_connect (dialog, "response",
G_CALLBACK (confirm_close_response_cb), window);
gtk_window_present (GTK_WINDOW (dialog));
return TRUE;
}
void
terminal_window_request_close (TerminalWindow *window)
{
g_return_if_fail (TERMINAL_IS_WINDOW (window));
if (confirm_close_window_or_tab (window, NULL))
return;
gtk_widget_destroy (GTK_WIDGET (window));
}
const char *
terminal_window_get_uuid (TerminalWindow *window)
{
g_return_val_if_fail (TERMINAL_IS_WINDOW (window), NULL);
return window->priv->uuid;
}