diff options
author | Patrick McDermott <pj@pehjota.net> | 2017-10-22 19:09:22 (EDT) |
---|---|---|
committer | Patrick McDermott <pj@pehjota.net> | 2017-10-22 19:09:22 (EDT) |
commit | 8fc032f2f64f4b7eb022adb0b1255a5a68df109f (patch) | |
tree | 31094e678eba9fc4a828ddb8a2948d105ff898bc /src/tab.c | |
parent | 927c4ea37ca967e61831164bb4b7ea33e28e0d9d (diff) | |
parent | a0b9288a5e6fa943a6f2b7f418a12343649ed454 (diff) | |
download | marquee-8fc032f2f64f4b7eb022adb0b1255a5a68df109f.zip marquee-8fc032f2f64f4b7eb022adb0b1255a5a68df109f.tar.gz marquee-8fc032f2f64f4b7eb022adb0b1255a5a68df109f.tar.bz2 |
Merge branch 'gobjectification'
Diffstat (limited to 'src/tab.c')
-rw-r--r-- | src/tab.c | 648 |
1 files changed, 0 insertions, 648 deletions
diff --git a/src/tab.c b/src/tab.c deleted file mode 100644 index 3e70fe0..0000000 --- a/src/tab.c +++ /dev/null @@ -1,648 +0,0 @@ -/* - * Tab - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include <stdlib.h> - -#include <gtk/gtk.h> - -#include "tab.h" -#include "tab-chrome.h" -#include "tab-body.h" - -static void -foreach_tab(MqTab *node, void (*cb)(MqTab *node)) -{ - for (; node; node = node->next) { - cb(node); - foreach_tab(node->first_child, cb); - } -} - -static void -update_tab_image(MqTab *tab, GdkPixbuf *favicon) -{ - if (favicon) { - gtk_image_set_from_pixbuf(GTK_IMAGE(tab->tab_image), favicon); - } else { - gtk_image_set_from_icon_name(GTK_IMAGE(tab->tab_image), - "text-x-generic", GTK_ICON_SIZE_BUTTON); - } -} - -static void -update_tab_label(MqTab *tab) -{ - const gchar *title; - gchar *label; - - title = tab->scrolling ? tab->scrolled_title : tab->title; - label = g_strdup_printf("%d. %s", tab->position, title); - gtk_label_set_text(GTK_LABEL(tab->tab_label), label); - gtk_widget_set_tooltip_text(tab->tab, label); - g_free(label); -} - -static void -update_positions(MqTab *node, gint step) -{ - if (node) { - node->position += step; - update_tab_label(node); - if (node->next) { - update_positions(node->next, step); - } else if (node->parent && node->parent->next) { - update_positions(node->parent->next, step); - } - } -} - -static void -update_tree_sizes(MqTab *node, guint step) -{ - if (node) { - node->tree_size += step; - update_tree_sizes(node->parent, step); - } -} - -static void -append_child(MqTab *new_node, MqTab *parent) -{ - new_node->root = parent->root; - new_node->parent = parent; - new_node->next = NULL; - new_node->prev = parent->last_child; /* May be NULL */ - new_node->first_child = new_node->last_child = NULL; - new_node->tree_size = 0; /* Will be updated */ - if (parent->last_child) { - new_node->position = parent->last_child->position; - parent->last_child->next = new_node; - } else { - new_node->position = parent->position; - parent->first_child = new_node; - } - parent->last_child = new_node; - update_positions(new_node, 1); - update_tree_sizes(new_node, 1); -} - -static void -append_sibling(MqTab *new_node, MqTab *prev_sibling) -{ - new_node->root = prev_sibling->root; - new_node->parent = prev_sibling->parent; - new_node->prev = prev_sibling; - new_node->next = prev_sibling->next; /* May be NULL */ - new_node->first_child = new_node->last_child = NULL; - new_node->position = prev_sibling->position; /* Will be updated */ - new_node->tree_size = 0; /* Will be updated */ - if (prev_sibling->next) { - prev_sibling->next->prev = new_node; - } - prev_sibling->next = new_node; - update_positions(new_node, 1); - update_tree_sizes(new_node, 1); -} - -static void -reload_tab_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqTab *tab) -{ - webkit_web_view_reload(tab->web_view); - gtk_widget_hide(tab->popover); -} - -static void -new_tab_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqTab *tab) -{ - mq_tab_new(NULL, tab); - gtk_widget_hide(tab->popover); -} - -static void -new_window_clicked_cb(GtkWidget G_GNUC_UNUSED *button, MqTab *tab) -{ - mq_application_add_window(tab->application, NULL); - gtk_widget_hide(tab->popover); -} - -static void -tab_list_button_toggled_cb(GtkToggleButton *toggle_button, GtkWidget *tab_list) -{ - if (gtk_toggle_button_get_active(toggle_button)) { - gtk_widget_show(tab_list); - } else { - gtk_widget_hide(tab_list); - } -} - -static void -create_tree_model_recurse(MqTab *node, GtkTreeStore *tree_store, - GtkTreeIter *parent_tree_iter) -{ - GtkTreeIter tree_iter; - - for (; node; node = node->next) { - gtk_tree_store_append(tree_store, &tree_iter, parent_tree_iter); - gtk_tree_store_set(tree_store, &tree_iter, 0, node->title, -1); - create_tree_model_recurse(node->first_child, tree_store, - &tree_iter); - } -} - -static GtkTreeModel * -create_tree_model(MqTab *tab) -{ - GtkTreeStore *tree_store; - - tree_store = gtk_tree_store_new(1, G_TYPE_STRING); - - create_tree_model_recurse(tab->root->first_child, tree_store, NULL); - - return GTK_TREE_MODEL(tree_store); -} - -static void -row_activated_cb(GtkTreeView G_GNUC_UNUSED *tree_view, GtkTreePath *tree_path, - GtkTreeViewColumn G_GNUC_UNUSED *tree_view_column, MqTab *tab) -{ - gint *indices; - gint depth; - - indices = gtk_tree_path_get_indices_with_depth(tree_path, &depth); - g_assert(depth == 1); - mq_window_set_current_tab(tab->window, indices[0] + 1); - gtk_widget_hide(tab->popover); -} - -static GtkWidget * -create_tab_list(MqTab *tab) -{ - GtkWidget *tree_view; - GtkTreeSelection *tree_selection; - GtkCellRenderer *cell_renderer; - GtkWidget *scrolled_window; - - tree_view = gtk_tree_view_new_with_model(create_tree_model(tab)); - tree_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)); - gtk_tree_selection_set_mode(tree_selection, GTK_SELECTION_BROWSE); - gtk_tree_selection_select_path(tree_selection, - gtk_tree_path_new_from_indices( - mq_window_get_current_tab(tab->window) - 1, -1)); - gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), FALSE); - gtk_tree_view_set_activate_on_single_click(GTK_TREE_VIEW(tree_view), - TRUE); - gtk_tree_view_expand_all(GTK_TREE_VIEW(tree_view)); - gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_view), TRUE); - gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(tree_view), TRUE); - g_signal_connect(tree_view, "row-activated", - G_CALLBACK(row_activated_cb), tab); - - cell_renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree_view), - -1, NULL, cell_renderer, "text", 0, NULL); - - scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_width( - GTK_SCROLLED_WINDOW(scrolled_window), 400); - gtk_scrolled_window_set_min_content_height( - GTK_SCROLLED_WINDOW(scrolled_window), 200); - gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view); - - return scrolled_window; -} - -#define BUTTON_ROWS 2 -#define BUTTON_COLS 4 -#define NEW_BUTTON(Y, X, ICON, TOOLTIP) \ - do { \ - buttons[Y * BUTTON_COLS + X] = gtk_button_new_from_icon_name(\ - ICON, GTK_ICON_SIZE_BUTTON); \ - gtk_widget_set_tooltip_text(buttons[Y * BUTTON_COLS + X], \ - TOOLTIP); \ - gtk_widget_set_can_focus(buttons[Y * BUTTON_COLS + X], FALSE); \ - gtk_grid_attach(GTK_GRID(button_grid), \ - buttons[Y * BUTTON_COLS + X], X, Y, 1, 1); \ - } while (0) -#define NEW_TOGGLE(Y, X, ICON, TOOLTIP) \ - do { \ - buttons[Y * BUTTON_COLS + X] = gtk_toggle_button_new(); \ - gtk_button_set_image(GTK_BUTTON(buttons[Y * BUTTON_COLS + X]), \ - gtk_image_new_from_icon_name(ICON, \ - GTK_ICON_SIZE_BUTTON)); \ - gtk_widget_set_tooltip_text(buttons[Y * BUTTON_COLS + X], \ - TOOLTIP); \ - gtk_widget_set_can_focus(buttons[Y * BUTTON_COLS + X], FALSE); \ - gtk_grid_attach(GTK_GRID(button_grid), \ - buttons[Y * BUTTON_COLS + X], X, Y, 1, 1); \ - } while (0) -#define CLICKED_CB(Y, X, CB) \ - g_signal_connect(buttons[Y * BUTTON_COLS + X], "clicked", CB, tab) - -static void -create_tab_popover(GtkWidget *widget, MqTab *tab) -{ - GtkWidget *button_grid; - GtkWidget *buttons[BUTTON_ROWS * BUTTON_COLS]; - GtkWidget *tab_list; - GtkWidget *tab_list_scrolled_window; - GtkWidget *box; - - /* Set up button grid. */ - button_grid = gtk_grid_new(); - gtk_widget_set_halign(button_grid, GTK_ALIGN_CENTER); - - /* Set up buttons. */ - NEW_BUTTON(0, 0, "view-refresh", "Reload tab"); - NEW_BUTTON(0, 1, "edit-copy", "Duplicate tab"); - NEW_BUTTON(0, 2, "window-new", "Move tab to new window"); - NEW_BUTTON(0, 3, "window-close", "Close tab"); - NEW_BUTTON(1, 0, "tab-new-symbolic", "New tab"); - NEW_BUTTON(1, 1, "window-new", "New window"); - NEW_BUTTON(1, 2, "edit-undo", "Undo close tab"); - NEW_TOGGLE(1, 3, "view-list-symbolic", "Tab list..."); - - CLICKED_CB(0, 0, G_CALLBACK(reload_tab_clicked_cb)); - CLICKED_CB(1, 0, G_CALLBACK(new_tab_clicked_cb)); - CLICKED_CB(1, 1, G_CALLBACK(new_window_clicked_cb)); - - /* Set up the tab list. */ - tab_list = create_tab_list(tab); - - /* Set up the tab list scrolled window. - * - * The following GtkScrolledWindow widget has a hardcoded minimum size, - * because there seems to be (in GTK+ versions before 3.22) no way to - * set the natural size of GtkScrolledWindow and its GtkViewport. - * - * See tab-chrome.c for more information. */ - tab_list_scrolled_window = gtk_scrolled_window_new(NULL, NULL); - gtk_scrolled_window_set_min_content_width( - GTK_SCROLLED_WINDOW(tab_list_scrolled_window), 400); - gtk_scrolled_window_set_min_content_height( - GTK_SCROLLED_WINDOW(tab_list_scrolled_window), 200); - gtk_container_add(GTK_CONTAINER(tab_list_scrolled_window), tab_list); - - /* Add tab list toggle button handler. */ - g_signal_connect(buttons[1 * BUTTON_COLS + 3], "toggled", - G_CALLBACK(tab_list_button_toggled_cb), - tab_list_scrolled_window); - - /* Set up the button rows box. */ - box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_start(GTK_BOX(box), button_grid, - TRUE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(box), tab_list_scrolled_window, - TRUE, FALSE, 0); - - /* Set up the popover. */ - tab->popover = gtk_popover_new(widget); - gtk_container_add(GTK_CONTAINER(tab->popover), box); - - /* NB: gtk_popover_popup() is new in GTK+ 3.22. */ - gtk_widget_show_all(tab->popover); - gtk_widget_hide(tab_list_scrolled_window); -} - -#undef BUTTON_ROWS -#undef BUTTON_COLS -#undef NEW_BUTTON -#undef NEW_TOGGLE -#undef CLICKED_CB - -static gboolean -tab_label_button_press_cb(GtkWidget *widget, GdkEvent *event, MqTab *tab) -{ - /* Make sure this is a mouse button press event. */ - if (event->type != GDK_BUTTON_PRESS) { - return FALSE; - } - - /* Create a popover menu on right click. */ - if (event->button.button == 3) { - create_tab_popover(widget, tab); - } - - return FALSE; -} - -static void -mq_tab_populate_tab(MqTab *tab) -{ - GtkWidget *close_button; - GtkWidget *box; - - tab->title = "New tab"; - - /* Set up tab image. */ - tab->tab_image = gtk_image_new_from_icon_name("text-x-generic", - GTK_ICON_SIZE_BUTTON); - - /* Set up tab label. */ - tab->tab_label = gtk_label_new(NULL); - gtk_label_set_ellipsize(GTK_LABEL(tab->tab_label), - PANGO_ELLIPSIZE_END); - gtk_widget_set_hexpand(tab->tab_label, TRUE); - gtk_widget_set_size_request(tab->tab_label, 50, 1); - - /* Set up close button. */ - close_button = gtk_button_new_from_icon_name("window-close", - GTK_ICON_SIZE_BUTTON); - gtk_widget_set_tooltip_text(close_button, "Close tab"); - - /* Pack tab box. */ - box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(box), tab->tab_image, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(box), tab->tab_label, TRUE, TRUE, 0); - gtk_box_pack_start(GTK_BOX(box), close_button, FALSE, FALSE, 0); - gtk_widget_show_all(box); - - /* Set up event box. */ - tab->tab = gtk_event_box_new(); - g_signal_connect(tab->tab, "button-press-event", - G_CALLBACK(tab_label_button_press_cb), tab); - gtk_event_box_set_visible_window(GTK_EVENT_BOX(tab->tab), - FALSE); - gtk_container_add(GTK_CONTAINER(tab->tab), box); -} - -static void -favicon_cb(WebKitWebView G_GNUC_UNUSED *web_view, - GParamSpec G_GNUC_UNUSED *paramspec, MqTab *tab) -{ - cairo_surface_t *surface; - GdkPixbuf *pixbuf; - GdkPixbuf *scaled_pixbuf; - - surface = webkit_web_view_get_favicon(tab->web_view); - scaled_pixbuf = NULL; - if (surface) { - pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, - cairo_image_surface_get_width(surface), - cairo_image_surface_get_height(surface)); - if (pixbuf) { - scaled_pixbuf = gdk_pixbuf_scale_simple(pixbuf, 16, 16, - GDK_INTERP_BILINEAR); - g_object_unref(pixbuf); - } - } - - update_tab_image(tab, scaled_pixbuf); -} - -static void -title_cb(WebKitWebView G_GNUC_UNUSED *web_view, - GParamSpec G_GNUC_UNUSED *paramspec, MqTab *tab) -{ - tab->title = webkit_web_view_get_title(tab->web_view); - if (tab->scrolling) { - tab->scrolled_title = g_strdup_printf("%s ", tab->title); - } - update_tab_label(tab); - mq_window_update_tab_title(tab->window, tab->position, tab->title); -} - -static MqTab * -init_non_root(const gchar *uri, MqTab *source) -{ - MqTab *tab; - - tab = malloc(sizeof(*tab)); - tab->parent = NULL; - tab->prev = NULL; - tab->next = NULL; - tab->first_child = tab->last_child = NULL; - tab->tree_size = 1; - - tab->window = source->window; - tab->application = mq_window_get_application(tab->window); - - mq_tab_populate_tab(tab); - - tab->chrome = mq_tab_chrome_new(tab, uri); - tab->body = mq_tab_body_new(tab, uri); - tab->web_view = mq_tab_body_get_web_view(tab->body); - g_signal_connect(tab->web_view, "notify::favicon", - G_CALLBACK(favicon_cb), tab); - g_signal_connect(tab->web_view, "notify::title", - G_CALLBACK(title_cb), tab); - mq_tab_chrome_set_web_view(tab->chrome, tab->web_view); - - tab->container = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_start(GTK_BOX(tab->container), - mq_tab_chrome_get_container(tab->chrome), FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(tab->container), - mq_tab_body_get_container(tab->body), TRUE, TRUE, 0); - - return tab; -} - -static void -scroll_tab_label(MqTab *tab) -{ - gchar c[5]; /* Up to 4 bytes for a UTF-8 character, plus NUL */ - guint i; - guint j; - - /* Save the first (possibly multibyte) character. */ - c[0] = tab->scrolled_title[0]; - for (i = 1; tab->scrolled_title[i] & 0x80; ++i) { - c[i] = tab->scrolled_title[i]; - } - c[i] = '\0'; - - /* Shift all characters. */ - for (j = 0; tab->scrolled_title[i]; ++i, ++j) { - tab->scrolled_title[j] = tab->scrolled_title[i]; - } - - /* Set the last (possibly multibyte) character. */ - for (--j, i = 0; c[i]; ++i, ++j) { - tab->scrolled_title[j] = c[i]; - } - - update_tab_label(tab); -} - -static void -begin_scrolling_tab_label(MqTab *tab) -{ - PangoFontDescription *font_desc; - - tab->scrolling = TRUE; - tab->scrolled_title = g_strdup_printf("%s ", tab->title); - - font_desc = pango_font_description_new(); - pango_font_description_set_family_static(font_desc, "monospace"); - gtk_widget_override_font(tab->tab_label, font_desc); -} - -static void -end_scrolling_tab_label(MqTab *tab) -{ - tab->scrolling = FALSE; - - gtk_widget_override_font(tab->tab_label, NULL); - - update_tab_label(tab); -} - -MqTab * -mq_tab_new(const gchar *uri, MqTab *source) -{ - MqTab *tab; - - tab = init_non_root(uri, source); - - if (mq_application_marquee_mode_on(tab->application)) { - begin_scrolling_tab_label(tab); - } else { - tab->scrolling = FALSE; - } - - append_sibling(tab, source); - - mq_window_insert_tab(tab->window, tab->container, tab->tab, - tab->position); - - return tab; -} - -MqTab * -mq_tab_new_relative(const gchar *uri, MqTab *source) -{ - MqTab *tab; - - tab = init_non_root(uri, source); - - if (mq_application_marquee_mode_on(tab->application)) { - begin_scrolling_tab_label(tab); - } else { - tab->scrolling = FALSE; - } - - append_child(tab, source); - - mq_window_insert_tab(tab->window, tab->container, tab->tab, - tab->position); - - return tab; -} - -MqTab * -mq_tab_new_root(MqWindow *window) -{ - MqTab *tab; - - tab = malloc(sizeof(*tab)); - tab->root = tab; - tab->parent = NULL; - tab->prev = NULL; - tab->next = NULL; - tab->first_child = tab->last_child = NULL; - tab->position = 0; - tab->tree_size = 1; - tab->window = window; - - return tab; -} - -void -mq_tab_quit(MqTab *tab) -{ - mq_window_quit(tab->window); -} - -MqApplication * -mq_tab_get_application(MqTab *tab) -{ - return tab->application; -} - -MqWindow * -mq_tab_get_window(MqTab *tab) -{ - return tab->window; -} - -void -mq_tab_update_position(MqTab *tab, guint position) -{ - tab->position = position; - update_tab_label(tab); -} - -guint -mq_tab_get_position(MqTab *tab) -{ - return tab->position; -} - -guint -mq_tab_get_tree_size(MqTab *tab) -{ - return tab->tree_size; -} - -const gchar * -mq_tab_get_title(MqTab *tab) -{ - return tab->title; -} - -MqTab * -mq_tab_seek(MqTab *node, guint position) -{ - /* Skip forward to the containing subtree. */ - while (node && node->position + node->tree_size <= position) { - node = node->next; - } - - /* Check whether we've gone past the end of the tree. */ - if (!node) { - return NULL; - } - - /* Check whether the sibling we've reached is the node we want. */ - if (node->position == position) { - return node; - } - - /* Recurse down the subtree. */ - return mq_tab_seek(node->first_child, position); -} - -void -mq_tab_scroll_tab_labels(MqTab *root) -{ - foreach_tab(root->first_child, scroll_tab_label); -} - -void -mq_tab_begin_scrolling_tab_labels(MqTab *root) -{ - foreach_tab(root->first_child, begin_scrolling_tab_label); -} - -void -mq_tab_end_scrolling_tab_labels(MqTab *root) -{ - foreach_tab(root->first_child, end_scrolling_tab_label); -} |