summaryrefslogtreecommitdiffstats
path: root/src/terminal-notebook.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/terminal-notebook.cc')
-rw-r--r--src/terminal-notebook.cc596
1 files changed, 596 insertions, 0 deletions
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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "terminal-notebook.hh"
+
+#include <gtk/gtk.h>
+
+#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<GtkWidget*>
+ (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;
+}