From 0f7ab3dc0ed5cfddfc3002992f0525756b6b670e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 20:02:09 +0200 Subject: Adding upstream version 3.46.8. Signed-off-by: Daniel Baumann --- src/terminal-notebook.cc | 596 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 596 insertions(+) create mode 100644 src/terminal-notebook.cc (limited to 'src/terminal-notebook.cc') diff --git a/src/terminal-notebook.cc b/src/terminal-notebook.cc new file mode 100644 index 0000000..6686d34 --- /dev/null +++ b/src/terminal-notebook.cc @@ -0,0 +1,596 @@ +/* + * Copyright © 2001 Havoc Pennington + * Copyright © 2002 Red Hat, Inc. + * Copyright © 2008, 2010, 2011, 2012 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 + +#include "terminal-notebook.hh" + +#include + +#include "terminal-debug.hh" +#include "terminal-app.hh" +#include "terminal-intl.hh" +#include "terminal-mdi-container.hh" +#include "terminal-screen-container.hh" +#include "terminal-tab-label.hh" +#include "terminal-schemas.hh" +#include "terminal-libgsystem.hh" + +#define TERMINAL_NOTEBOOK_GET_PRIVATE(notebook)(G_TYPE_INSTANCE_GET_PRIVATE ((notebook), TERMINAL_TYPE_NOTEBOOK, TerminalNotebookPrivate)) + +struct _TerminalNotebookPrivate +{ + TerminalScreen *active_screen; + GtkPolicyType policy; +}; + +enum +{ + PROP_0, + PROP_ACTIVE_SCREEN, + PROP_TAB_POLICY +}; + +#define ACTION_AREA_BORDER_WIDTH (2) +#define ACTION_BUTTON_SPACING (6) + +/* helper functions */ + +static void +update_tab_visibility (TerminalNotebook *notebook, + int change) +{ + TerminalNotebookPrivate *priv = notebook->priv; + GtkNotebook *gtk_notebook = GTK_NOTEBOOK (notebook); + int new_n_pages; + gboolean show_tabs; + + if (gtk_widget_in_destruction (GTK_WIDGET (notebook))) + return; + + new_n_pages = gtk_notebook_get_n_pages (gtk_notebook) + change; + /* Don't do anything if we're going to have zero pages (and thus close the window) */ + if (new_n_pages == 0) + return; + + switch (priv->policy) { + case GTK_POLICY_ALWAYS: + show_tabs = TRUE; + break; + case GTK_POLICY_AUTOMATIC: + show_tabs = new_n_pages > 1; + break; + case GTK_POLICY_NEVER: + case GTK_POLICY_EXTERNAL: + default: + show_tabs = FALSE; + break; + } + + gtk_notebook_set_show_tabs (gtk_notebook, show_tabs); +} + +static void +close_button_clicked_cb (TerminalTabLabel *tab_label, + gpointer user_data) +{ + TerminalScreen *screen; + TerminalNotebook *notebook; + + screen = terminal_tab_label_get_screen (tab_label); + + /* notebook is not passed as user_data because it can change during DND + * and the close button is not notified about that, see bug 731998. + */ + notebook = TERMINAL_NOTEBOOK (gtk_widget_get_ancestor (GTK_WIDGET (screen), + TERMINAL_TYPE_NOTEBOOK)); + + if (notebook != nullptr) + g_signal_emit_by_name (notebook, "screen-close-request", screen); +} + + +static void +remove_reorder_bindings (GtkBindingSet *binding_set, + guint keysym) +{ + guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left; + gtk_binding_entry_skip (binding_set, keysym, GDK_MOD1_MASK); + gtk_binding_entry_skip (binding_set, keypad_keysym, GDK_MOD1_MASK); +} + +/* TerminalMdiContainer impl */ + +static void +terminal_notebook_add_screen (TerminalMdiContainer *container, + TerminalScreen *screen, + int position) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (container); + GtkNotebook *gtk_notebook = GTK_NOTEBOOK (notebook); + GtkWidget *screen_container, *tab_label; + + g_warn_if_fail (gtk_widget_get_parent (GTK_WIDGET (screen)) == nullptr); + + screen_container = terminal_screen_container_new (screen); + gtk_widget_show (screen_container); + + update_tab_visibility (notebook, +1); + + tab_label = terminal_tab_label_new (screen); + g_signal_connect (tab_label, "close-button-clicked", + G_CALLBACK (close_button_clicked_cb), nullptr); + + gtk_notebook_insert_page (gtk_notebook, + screen_container, + tab_label, + position); + gtk_container_child_set (GTK_CONTAINER (notebook), + screen_container, + "tab-expand", TRUE, + "tab-fill", TRUE, + nullptr); + gtk_notebook_set_tab_reorderable (gtk_notebook, screen_container, TRUE); +#if 0 + gtk_notebook_set_tab_detachable (gtk_notebook, screen_container, TRUE); +#endif +} + +static void +terminal_notebook_remove_screen (TerminalMdiContainer *container, + TerminalScreen *screen) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (container); + TerminalScreenContainer *screen_container; + + g_warn_if_fail (gtk_widget_is_ancestor (GTK_WIDGET (screen), GTK_WIDGET (notebook))); + + update_tab_visibility (notebook, -1); + + screen_container = terminal_screen_container_get_from_screen (screen); + gtk_container_remove (GTK_CONTAINER (notebook), + GTK_WIDGET (screen_container)); +} + +static TerminalScreen * +terminal_notebook_get_active_screen (TerminalMdiContainer *container) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (container); + GtkNotebook *gtk_notebook = GTK_NOTEBOOK (notebook); + GtkWidget *widget; + + widget = gtk_notebook_get_nth_page (gtk_notebook, gtk_notebook_get_current_page (gtk_notebook)); + return terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (widget)); +} + +static void +terminal_notebook_set_active_screen (TerminalMdiContainer *container, + TerminalScreen *screen) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (container); + GtkNotebook *gtk_notebook = GTK_NOTEBOOK (notebook); + TerminalScreenContainer *screen_container; + GtkWidget *widget; + + screen_container = terminal_screen_container_get_from_screen (screen); + widget = GTK_WIDGET (screen_container); + + gtk_notebook_set_current_page (gtk_notebook, + gtk_notebook_page_num (gtk_notebook, widget)); +} + +static GList * +terminal_notebook_list_screen_containers (TerminalMdiContainer *container) +{ + /* We are trusting that GtkNotebook will return pages in order */ + return gtk_container_get_children (GTK_CONTAINER (container)); +} + +static GList * +terminal_notebook_list_screens (TerminalMdiContainer *container) +{ + GList *list, *l; + + list = terminal_notebook_list_screen_containers (container); + for (l = list; l != nullptr; l = l->next) + l->data = terminal_screen_container_get_screen ((TerminalScreenContainer *) l->data); + + return list; +} + +static int +terminal_notebook_get_n_screens (TerminalMdiContainer *container) +{ + return gtk_notebook_get_n_pages (GTK_NOTEBOOK (container)); +} + +static int +terminal_notebook_get_active_screen_num (TerminalMdiContainer *container) +{ + return gtk_notebook_get_current_page (GTK_NOTEBOOK (container)); +} + +static void +terminal_notebook_set_active_screen_num (TerminalMdiContainer *container, + int position) +{ + GtkNotebook *gtk_notebook = GTK_NOTEBOOK (container); + + gtk_notebook_set_current_page (gtk_notebook, position); +} + +static void +terminal_notebook_reorder_screen (TerminalMdiContainer *container, + TerminalScreen *screen, + int new_position) +{ + GtkNotebook *notebook = GTK_NOTEBOOK (container); + GtkWidget *child; + int n, pos; + + g_return_if_fail (new_position == 1 || new_position == -1); + + child = GTK_WIDGET (terminal_screen_container_get_from_screen (screen)); + n = gtk_notebook_get_n_pages (notebook); + pos = gtk_notebook_page_num (notebook, child); + + pos += new_position; + gtk_notebook_reorder_child (notebook, child, + pos < 0 ? n - 1 : pos < n ? pos : 0); +} + +static void +terminal_notebook_mdi_iface_init (TerminalMdiContainerInterface *iface) +{ + iface->add_screen = terminal_notebook_add_screen; + iface->remove_screen = terminal_notebook_remove_screen; + iface->get_active_screen = terminal_notebook_get_active_screen; + iface->set_active_screen = terminal_notebook_set_active_screen; + iface->list_screens = terminal_notebook_list_screens; + iface->list_screen_containers = terminal_notebook_list_screen_containers; + iface->get_n_screens = terminal_notebook_get_n_screens; + iface->get_active_screen_num = terminal_notebook_get_active_screen_num; + iface->set_active_screen_num = terminal_notebook_set_active_screen_num; + iface->reorder_screen = terminal_notebook_reorder_screen; +} + +G_DEFINE_TYPE_WITH_CODE (TerminalNotebook, terminal_notebook, GTK_TYPE_NOTEBOOK, + G_IMPLEMENT_INTERFACE (TERMINAL_TYPE_MDI_CONTAINER, terminal_notebook_mdi_iface_init)) + +/* GtkNotebookClass impl */ + +static void +terminal_notebook_switch_page (GtkNotebook *gtk_notebook, + GtkWidget *child, + guint page_num) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (gtk_notebook); + TerminalNotebookPrivate *priv = notebook->priv; + TerminalScreen *screen, *old_active_screen; + + GTK_NOTEBOOK_CLASS (terminal_notebook_parent_class)->switch_page (gtk_notebook, child, page_num); + + screen = terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (child)); + + old_active_screen = priv->active_screen; + if (screen == old_active_screen) + return; + + /* Workaround to remove gtknotebook's feature of computing its size based on + * all pages. When the widget is hidden, its size will not be taken into + * account. + * FIXME! + */ +// if (old_active_screen) +// gtk_widget_hide (GTK_WIDGET (terminal_screen_container_get_from_screen (old_active_screen))); + /* Make sure that the widget is no longer hidden due to the workaround */ +// if (child) +// gtk_widget_show (child); + if (old_active_screen) + gtk_widget_hide (GTK_WIDGET (old_active_screen)); + if (screen) + gtk_widget_show (GTK_WIDGET (screen)); + + priv->active_screen = screen; + + g_signal_emit_by_name (notebook, "screen-switched", old_active_screen, screen); + g_object_notify (G_OBJECT (notebook), "active-screen"); +} + +static void +terminal_notebook_page_added (GtkNotebook *gtk_notebook, + GtkWidget *child, + guint page_num) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (gtk_notebook); + void (* page_added) (GtkNotebook *, GtkWidget *, guint) = + GTK_NOTEBOOK_CLASS (terminal_notebook_parent_class)->page_added; + + if (page_added) + page_added (gtk_notebook, child, page_num); + + update_tab_visibility (notebook, 0); + g_signal_emit_by_name (gtk_notebook, "screen-added", + terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (child))); +} + +static void +terminal_notebook_page_removed (GtkNotebook *gtk_notebook, + GtkWidget *child, + guint page_num) +{ + TerminalNotebook *notebook = TERMINAL_NOTEBOOK (gtk_notebook); + void (* page_removed) (GtkNotebook *, GtkWidget *, guint) = + GTK_NOTEBOOK_CLASS (terminal_notebook_parent_class)->page_removed; + + if (page_removed) + page_removed (gtk_notebook, child, page_num); + + update_tab_visibility (notebook, 0); + g_signal_emit_by_name (gtk_notebook, "screen-removed", + terminal_screen_container_get_screen (TERMINAL_SCREEN_CONTAINER (child))); +} + +static void +terminal_notebook_page_reordered (GtkNotebook *notebook, + GtkWidget *child, + guint page_num) +{ + void (* page_reordered) (GtkNotebook *, GtkWidget *, guint) = + GTK_NOTEBOOK_CLASS (terminal_notebook_parent_class)->page_reordered; + + if (page_reordered) + page_reordered (notebook, child, page_num); + + g_signal_emit_by_name (notebook, "screens-reordered"); +} + +static GtkNotebook * +terminal_notebook_create_window (GtkNotebook *notebook, + GtkWidget *page, + gint x, + gint y) +{ + return GTK_NOTEBOOK_CLASS (terminal_notebook_parent_class)->create_window (notebook, page, x, y); +} + +/* GtkWidgetClass impl */ + +static void +terminal_notebook_grab_focus (GtkWidget *widget) +{ + TerminalScreen *screen; + + screen = terminal_mdi_container_get_active_screen (TERMINAL_MDI_CONTAINER (widget)); + gtk_widget_grab_focus (GTK_WIDGET (screen)); +} + +/* GObjectClass impl */ + +static void +terminal_notebook_init (TerminalNotebook *notebook) +{ + TerminalNotebookPrivate *priv; + + priv = notebook->priv = TERMINAL_NOTEBOOK_GET_PRIVATE (notebook); + + priv->active_screen = nullptr; + priv->policy = GTK_POLICY_AUTOMATIC; +} + +static void +terminal_notebook_constructed (GObject *object) +{ + GSettings *settings; + GtkWidget *widget = GTK_WIDGET (object); + GtkNotebook *notebook = GTK_NOTEBOOK (object); + + G_OBJECT_CLASS (terminal_notebook_parent_class)->constructed (object); + + settings = terminal_app_get_global_settings (terminal_app_get ()); + + update_tab_visibility (TERMINAL_NOTEBOOK (notebook), 0); + g_settings_bind (settings, + TERMINAL_SETTING_TAB_POLICY_KEY, + object, + "tab-policy", + GSettingsBindFlags(G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_NO_SENSITIVITY)); + + g_settings_bind (settings, + TERMINAL_SETTING_TAB_POSITION_KEY, + object, + "tab-pos", + GSettingsBindFlags(G_SETTINGS_BIND_GET | + G_SETTINGS_BIND_NO_SENSITIVITY)); + + gtk_notebook_set_scrollable (notebook, TRUE); + gtk_notebook_set_show_border (notebook, FALSE); + gtk_notebook_set_group_name (notebook, I_("gnome-terminal-window")); + + /* Necessary for scroll events */ + gtk_widget_add_events (widget, GDK_SCROLL_MASK); +} + +static void +terminal_notebook_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + TerminalMdiContainer *mdi_container = TERMINAL_MDI_CONTAINER (object); + + switch (prop_id) { + case PROP_ACTIVE_SCREEN: + g_value_set_object (value, terminal_notebook_get_active_screen (mdi_container)); + break; + case PROP_TAB_POLICY: + g_value_set_enum (value, terminal_notebook_get_tab_policy (TERMINAL_NOTEBOOK (mdi_container))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_notebook_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + TerminalMdiContainer *mdi_container = TERMINAL_MDI_CONTAINER (object); + + switch (prop_id) { + case PROP_ACTIVE_SCREEN: + terminal_notebook_set_active_screen (mdi_container, (TerminalScreen*)g_value_get_object (value)); + break; + case PROP_TAB_POLICY: + terminal_notebook_set_tab_policy (TERMINAL_NOTEBOOK (mdi_container), GtkPolicyType(g_value_get_enum (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +terminal_notebook_class_init (TerminalNotebookClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkNotebookClass *notebook_class = GTK_NOTEBOOK_CLASS (klass); + + g_type_class_add_private (gobject_class, sizeof (TerminalNotebookPrivate)); + + gobject_class->constructed = terminal_notebook_constructed; + gobject_class->get_property = terminal_notebook_get_property; + gobject_class->set_property = terminal_notebook_set_property; + + g_object_class_override_property (gobject_class, PROP_ACTIVE_SCREEN, "active-screen"); + + widget_class->grab_focus = terminal_notebook_grab_focus; + + notebook_class->switch_page = terminal_notebook_switch_page; + notebook_class->create_window = terminal_notebook_create_window; + notebook_class->page_added = terminal_notebook_page_added; + notebook_class->page_removed = terminal_notebook_page_removed; + notebook_class->page_reordered = terminal_notebook_page_reordered; + + g_object_class_install_property + (gobject_class, + PROP_TAB_POLICY, + g_param_spec_enum ("tab-policy", nullptr, nullptr, + GTK_TYPE_POLICY_TYPE, + GTK_POLICY_AUTOMATIC, + GParamFlags(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + /* Remove unwanted and interfering keybindings */ + GtkBindingSet *binding_set = gtk_binding_set_by_class (terminal_notebook_parent_class); + gtk_binding_entry_skip (binding_set, GDK_KEY_Page_Up, GdkModifierType(GDK_CONTROL_MASK)); + gtk_binding_entry_skip (binding_set, GDK_KEY_Page_Up, GdkModifierType(GDK_CONTROL_MASK | GDK_MOD1_MASK)); + gtk_binding_entry_skip (binding_set, GDK_KEY_Page_Down, GdkModifierType(GDK_CONTROL_MASK)); + gtk_binding_entry_skip (binding_set, GDK_KEY_Page_Down, GdkModifierType(GDK_CONTROL_MASK | GDK_MOD1_MASK)); + remove_reorder_bindings (binding_set, GDK_KEY_Up); + remove_reorder_bindings (binding_set, GDK_KEY_Down); + remove_reorder_bindings (binding_set, GDK_KEY_Left); + remove_reorder_bindings (binding_set, GDK_KEY_Right); + remove_reorder_bindings (binding_set, GDK_KEY_Home); + remove_reorder_bindings (binding_set, GDK_KEY_Home); + remove_reorder_bindings (binding_set, GDK_KEY_End); + remove_reorder_bindings (binding_set, GDK_KEY_End); +} + +/* public API */ + +/** + * terminal_notebook_new: + * + * Returns: (transfer full): a new #TerminalNotebook + */ +GtkWidget * +terminal_notebook_new (void) +{ + return reinterpret_cast + (g_object_new (TERMINAL_TYPE_NOTEBOOK, nullptr)); +} + +void +terminal_notebook_set_tab_policy (TerminalNotebook *notebook, + GtkPolicyType policy) +{ + TerminalNotebookPrivate *priv = notebook->priv; + + if (priv->policy == policy) + return; + + priv->policy = policy; + update_tab_visibility (notebook, 0); + + g_object_notify (G_OBJECT (notebook), "tab-policy"); +} + +GtkPolicyType +terminal_notebook_get_tab_policy (TerminalNotebook *notebook) +{ + return notebook->priv->policy; +} + +GtkWidget * +terminal_notebook_get_action_box (TerminalNotebook *notebook, + GtkPackType pack_type) +{ + GtkNotebook *gtk_notebook; + GtkWidget *box, *inner_box; + + g_return_val_if_fail (TERMINAL_IS_NOTEBOOK (notebook), nullptr); + + gtk_notebook = GTK_NOTEBOOK (notebook); + box = gtk_notebook_get_action_widget (gtk_notebook, pack_type); + if (box != nullptr) { + gs_free_list GList *list; + + list = gtk_container_get_children (GTK_CONTAINER (box)); + g_assert (list->data != nullptr); + return (GtkWidget*)list->data; + } + + /* Create container for the buttons */ + box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_set_border_width (GTK_CONTAINER (box), ACTION_AREA_BORDER_WIDTH); + + inner_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, ACTION_BUTTON_SPACING); + gtk_box_pack_start (GTK_BOX (box), inner_box, TRUE, FALSE, 0); + gtk_widget_show (inner_box); + + gtk_notebook_set_action_widget (gtk_notebook, box, pack_type); + gtk_widget_show (box); + + /* FIXME: this appears to be necessary to make the icon buttons contained + * in the action area render the same way as buttons in the tab labels (e.g. + * the close button). gtk+ bug? + */ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS + gtk_style_context_add_region (gtk_widget_get_style_context (box), + GTK_STYLE_REGION_TAB, + pack_type == GTK_PACK_START ? GTK_REGION_FIRST : GTK_REGION_LAST); + G_GNUC_END_IGNORE_DEPRECATIONS + + return inner_box; +} -- cgit v1.2.3