/* * Tabbed notebook * * Copyright (C) 2017 Patrick McDermott * * This file is part of Marquee. * * Marquee 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. * * Marquee 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 Marquee. If not, see . */ #include "notebook.h" #include #include #include "tab-label.h" #include "tab-page.h" #include "tree.h" #include "window.h" #define MQ_TAB_TREE(obj) ((MqTabTree *) (obj)) typedef struct { MqTree parent_instance; MqTabLabel *label; MqTabPage *page; } MqTabTree; struct _MqNotebook { GtkNotebook parent_instance; MqWindow *window; MqTabTree *tree; MqTabTree *found_node; MqTabPage *current_page; }; enum { PROP_WINDOW = 1, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = {NULL,}; struct _MqNotebookClass { GtkNotebookClass parent_class; }; G_DEFINE_TYPE(MqNotebook, mq_notebook, GTK_TYPE_NOTEBOOK) static void get_property(GObject *object, guint property_id, GValue *value, GParamSpec *param_spec) { MqNotebook *notebook; notebook = MQ_NOTEBOOK(object); switch (property_id) { case PROP_WINDOW: g_value_set_object(value, notebook->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *param_spec) { MqNotebook *notebook; notebook = MQ_NOTEBOOK(object); switch (property_id) { case PROP_WINDOW: notebook->window = g_value_get_object(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, param_spec); break; } } static void mq_notebook_class_init(MqNotebookClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->get_property = get_property; object_class->set_property = set_property; obj_properties[PROP_WINDOW] = g_param_spec_object( "window", "MqWindow", "The parent MqWindow instance", MQ_TYPE_WINDOW, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB); g_object_class_install_properties(object_class, N_PROPERTIES, obj_properties); } static void switch_page_cb(MqNotebook *notebook, MqTabPage *page, guint G_GNUC_UNUSED page_num) { notebook->current_page = page; mq_window_set_title(notebook->window, mq_tab_page_get_title(page)); } static void mq_notebook_init(MqNotebook *notebook) { notebook->tree = MQ_TAB_TREE(mq_tree_insert_root_allocated( MQ_TREE(g_new0(MqTabTree, 1)), NULL)); gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), FALSE); gtk_notebook_set_scrollable(GTK_NOTEBOOK(notebook), TRUE); gtk_notebook_set_group_name(GTK_NOTEBOOK(notebook), "mq-tabs"); gtk_widget_set_can_focus(GTK_WIDGET(notebook), FALSE); g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page_cb), NULL); } GtkWidget * mq_notebook_new(MqWindow *window) { return g_object_new(MQ_TYPE_NOTEBOOK, "window", window, NULL); } static gboolean find_node_compare(MqTree *node, va_list ap) { MqNotebook *notebook; MqTabPage *page; notebook = va_arg(ap, MqNotebook *); page = va_arg(ap, MqTabPage *); if (MQ_TAB_TREE(node)->page == page) { notebook->found_node = MQ_TAB_TREE(node); return MQ_TREE_STOP; } else { return MQ_TREE_CONTINUE; } } static void find_node(MqNotebook *notebook, MqTabPage *page) { notebook->found_node = NULL; mq_tree_foreach(MQ_TREE(notebook->tree), find_node_compare, notebook, page); g_assert(notebook->found_node); } static void insert_page(MqNotebook *notebook, MqTabTree *node, const gchar *uri) { node->page = mq_tab_page_new(notebook->window, uri); node->label = mq_tab_page_get_label(node->page); gtk_notebook_insert_page(GTK_NOTEBOOK(notebook), GTK_WIDGET(node->page), GTK_WIDGET(node->label), MQ_TREE(node)->position - 1); gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(notebook), GTK_WIDGET(node->page), TRUE); gtk_notebook_set_tab_detachable(GTK_NOTEBOOK(notebook), GTK_WIDGET(node->page), TRUE); gtk_widget_show_all(GTK_WIDGET(node->page)); gtk_widget_show_all(GTK_WIDGET(node->label)); mq_tab_page_set_position (node->page, MQ_TREE(node)->position); mq_tab_label_set_position(node->label, MQ_TREE(node)->position); } MqTabPage * mq_notebook_insert_top(MqNotebook *notebook, const gchar *uri) { MqTabTree *node; node = MQ_TAB_TREE(mq_tree_insert_child_allocated( MQ_TREE(g_new0(MqTabTree, 1)), MQ_TREE(notebook->tree), NULL)); insert_page(notebook, node, uri); return node->page; } MqTabPage * mq_notebook_insert_sibling(MqNotebook *notebook, const gchar *uri, MqTabPage *sibling) { MqTabTree *node; g_assert(sibling); find_node(notebook, sibling); node = MQ_TAB_TREE(mq_tree_insert_sibling_allocated( MQ_TREE(g_new0(MqTabTree, 1)), MQ_TREE(notebook->found_node), NULL)); insert_page(notebook, node, uri); return node->page; } MqTabPage * mq_notebook_insert_child(MqNotebook *notebook, const gchar *uri, MqTabPage *child) { MqTabTree *node; g_assert(child); find_node(notebook, child); node = MQ_TAB_TREE(mq_tree_insert_child_allocated( MQ_TREE(g_new0(MqTabTree, 1)), MQ_TREE(notebook->found_node), NULL)); insert_page(notebook, node, uri); return node->page; } void mq_notebook_update_tab_title(MqNotebook *notebook, MqTabPage *tab_page, const gchar *title) { if (tab_page == notebook->current_page) { mq_window_set_title(notebook->window, title); } }