/*
* 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
new_tab_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqNotebook *notebook)
{
mq_notebook_insert_sibling(notebook, NULL, notebook->current_page);
}
static void
mq_notebook_init(MqNotebook *notebook)
{
GtkWidget *new_tab_button;
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);
new_tab_button = gtk_button_new_from_icon_name("tab-new-symbolic",
GTK_ICON_SIZE_BUTTON);
gtk_button_set_relief(GTK_BUTTON(new_tab_button), GTK_RELIEF_NONE);
gtk_widget_set_tooltip_text(new_tab_button, "New tab");
gtk_notebook_set_action_widget(GTK_NOTEBOOK(notebook), new_tab_button,
GTK_PACK_START);
gtk_widget_show_all(new_tab_button);
g_signal_connect(new_tab_button, "clicked",
G_CALLBACK(new_tab_clicked_cb), notebook);
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;
}
gint
mq_notebook_get_n_pages(MqNotebook *notebook)
{
return mq_tree_size(MQ_TREE(notebook->tree)) - 1;
}
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);
}
}