Wrapped in gaffa

Programming, Art, Music, Philosophy, Politics

GtkNotebook Ramblings

The idea of tabs can be thought of as a stack of bricks. If you count the bricks from the bottom to the top and pull a brick out. The index of the new brick will be the same as the old. Top of the stack excluded. Notebook tabs should be the same and almost is in GtkNotebook.

Think of tabs in a notebook as a double linked-list namely GList.
typedef struct {
gpointer data;
GList *next;
GList *prev;
} GList;

Where data is a pointer to a GtkNotebookPage. When a page is removed you just update the next/prev pointers in the tabs around it – and we have a nice stack of bricks.
struct _GtkNotebookPage
{
GtkWidget *child;
 
GtkWidget *tab_label;
GtkWidget *menu_label;
GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
 
guint default_menu : 1; /* If true, we create the menu label ourself */
guint default_tab : 1; /* If true, we create the tab label ourself */
 
guint expand : 1;
guint fill : 1;
guint pack : 1;
 
guint reorderable : 1;
guint detachable : 1;
 
GtkRequisition requisition;
 
GtkAllocation allocation;
 
gulong mnemonic_activate_signal;
gulong notify_visible_handler;
};

The programmer using the API knows the tabs by index and calls the function gtk_notebook_remove_page which translate the index to a list element and passes it on to gtk_container_remove which emits a signal “remove”. The remove signal is caught by gtk_notebook_remove which calls gtk_notebook_real_remove

gtk_notebook_real_remove is called with a GList* list (a double linked-list of GtkNotebookPages) and a GtkNotebook* notebook.

This snippet find the new page in the GList. It gets the previous page unless the current page is the first tab.
next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
if (!next_list)
next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);

tab1
If we delete tab1 this would happen.

tab deleted old
Why not select Tab 1? Wouldn’t that be the natural thing to do if tabs are left aligned?

tab deleted new

It can be done by simply switching the order of STEP_PREV and STEP_NEXT
next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
if (!next_list)
next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);

Additionally #131920 causes the switch_page signal to report wrong index numbers as the signal is generated before the deleted page element is unlinked from the GList.

This can be fixed by simply calling g_list_remove_link as soon as we are done with the next/prev pointers of the list element and before signals are being sent. The element can still be used as usual until it is freed later.

I’m restriced in which file formats I can upload so here is how the patch looks like (against 2.10.6):
--- gtk+-2.10.6/gtk/gtknotebook.c 2006-09-10 08:33:15.000000000 +0200
+++ gtknotebook.c 2007-01-20 22:16:17.000000000 +0100
@@ -4148,9 +4148,11 @@
priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
destroying = GTK_OBJECT_FLAGS (notebook) & GTK_IN_DESTRUCTION;
 
- next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
+ next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
if (!next_list)
- next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
+ next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
+
+ notebook->children = g_list_remove_link (notebook->children, list);
 
if (notebook->cur_page == list->data)
{
@@ -4187,7 +4189,6 @@
if (!page->default_menu)
g_object_unref (page->menu_label);
 
- notebook->children = g_list_remove_link (notebook->children, list);
g_list_free (list);
 
if (page->last_focus_child)

Update:
Seems like my patch has been comitted in Gtk+ 2.13.3:

2008-06-08 Björn Lindqvist

Bug 131920 – gtkNotebook sends incorrect switch_page value

* gtk/gtknotebook.c (gtk_notebook_real_remove): Make switch-page
signal send correct page index when page index 0 is active and
removed. (#131920, Samuel Fogh)

Written by gaffa

2007-01-21 at 19:24

Posted in Gtk

Tagged with , , , ,

%d bloggers like this: