/* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "gtknotebook.h" #include "gtksignal.h" #define CHILD_SPACING 2 #define TAB_OVERLAP 2 #define TAB_CURVATURE 1 enum { SWITCH_PAGE, LAST_SIGNAL }; typedef void (*GtkNotebookSignal) (GtkObject *object, gpointer arg1, gpointer data); static void gtk_notebook_class_init (GtkNotebookClass *klass); static void gtk_notebook_init (GtkNotebook *notebook); static void gtk_notebook_destroy (GtkObject *object); static void gtk_notebook_map (GtkWidget *widget); static void gtk_notebook_unmap (GtkWidget *widget); static void gtk_notebook_realize (GtkWidget *widget); static void gtk_notebook_unrealize (GtkWidget *widget); static void gtk_notebook_size_request (GtkWidget *widget, GtkRequisition *requisition); static void gtk_notebook_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_notebook_paint (GtkWidget *widget, GdkRectangle *area); static void gtk_notebook_draw (GtkWidget *widget, GdkRectangle *area); static gint gtk_notebook_expose (GtkWidget *widget, GdkEventExpose *event); static gint gtk_notebook_button_press (GtkWidget *widget, GdkEventButton *event); static void gtk_notebook_add (GtkContainer *container, GtkWidget *widget); static void gtk_notebook_remove (GtkContainer *container, GtkWidget *widget); static void gtk_notebook_foreach (GtkContainer *container, GtkCallback callback, gpointer callback_data); static void gtk_notebook_switch_page (GtkNotebook *notebook, GtkNotebookPage *page); static void gtk_notebook_draw_tab (GtkNotebook *notebook, GtkNotebookPage *page, GdkRectangle *area); static void gtk_notebook_pages_allocate (GtkNotebook *notebook, GtkAllocation *allocation); static void gtk_notebook_page_allocate (GtkNotebook *notebook, GtkNotebookPage *page, GtkAllocation *allocation); static void gtk_real_notebook_switch_page (GtkNotebook *notebook, GtkNotebookPage *page); static void gtk_notebook_marshal_signal (GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *args); static GtkContainerClass *parent_class = NULL; static gint notebook_signals[LAST_SIGNAL] = { 0 }; guint gtk_notebook_get_type () { static guint notebook_type = 0; if (!notebook_type) { GtkTypeInfo notebook_info = { "GtkNotebook", sizeof (GtkNotebook), sizeof (GtkNotebookClass), (GtkClassInitFunc) gtk_notebook_class_init, (GtkObjectInitFunc) gtk_notebook_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; notebook_type = gtk_type_unique (gtk_container_get_type (), ¬ebook_info); } return notebook_type; } static void gtk_notebook_class_init (GtkNotebookClass *class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkContainerClass *container_class; object_class = (GtkObjectClass*) class; widget_class = (GtkWidgetClass*) class; container_class = (GtkContainerClass*) class; parent_class = gtk_type_class (gtk_container_get_type ()); notebook_signals[SWITCH_PAGE] = gtk_signal_new ("switch_page", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GtkNotebookClass, switch_page), gtk_notebook_marshal_signal, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_object_class_add_signals (object_class, notebook_signals, LAST_SIGNAL); object_class->destroy = gtk_notebook_destroy; widget_class->map = gtk_notebook_map; widget_class->unmap = gtk_notebook_unmap; widget_class->realize = gtk_notebook_realize; widget_class->unrealize = gtk_notebook_unrealize; widget_class->size_request = gtk_notebook_size_request; widget_class->size_allocate = gtk_notebook_size_allocate; widget_class->draw = gtk_notebook_draw; widget_class->expose_event = gtk_notebook_expose; widget_class->button_press_event = gtk_notebook_button_press; container_class->add = gtk_notebook_add; container_class->remove = gtk_notebook_remove; container_class->foreach = gtk_notebook_foreach; class->switch_page = gtk_real_notebook_switch_page; } static void gtk_notebook_init (GtkNotebook *notebook) { notebook->cur_page = NULL; notebook->children = NULL; notebook->show_tabs = TRUE; notebook->show_border = TRUE; notebook->tab_pos = GTK_POS_TOP; } GtkWidget* gtk_notebook_new () { return GTK_WIDGET (gtk_type_new (gtk_notebook_get_type ())); } void gtk_notebook_append_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label) { g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); g_return_if_fail (child != NULL); g_return_if_fail (tab_label != NULL); gtk_notebook_insert_page (notebook, child, tab_label, -1); } void gtk_notebook_prepend_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label) { g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); g_return_if_fail (child != NULL); g_return_if_fail (tab_label != NULL); gtk_notebook_insert_page (notebook, child, tab_label, 0); } void gtk_notebook_insert_page (GtkNotebook *notebook, GtkWidget *child, GtkWidget *tab_label, gint position) { GtkNotebookPage *page; gint nchildren; g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); g_return_if_fail (child != NULL); g_return_if_fail (tab_label != NULL); page = g_new (GtkNotebookPage, 1); page->child = child; page->tab_label = tab_label; page->requisition.width = 0; page->requisition.height = 0; page->allocation.x = 0; page->allocation.y = 0; page->allocation.width = 0; page->allocation.height = 0; nchildren = g_list_length (notebook->children); if ((position < 0) || (position > nchildren)) position = nchildren; notebook->children = g_list_insert (notebook->children, page, position); if (!notebook->cur_page) notebook->cur_page = page; gtk_widget_show (tab_label); gtk_widget_set_parent (child, GTK_WIDGET (notebook)); gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook)); if (GTK_WIDGET_VISIBLE (notebook)) { if (GTK_WIDGET_REALIZED (notebook) && !GTK_WIDGET_REALIZED (child)) gtk_widget_realize (child); if (GTK_WIDGET_MAPPED (notebook) && !GTK_WIDGET_MAPPED (child) && notebook->cur_page == page) gtk_widget_map (child); if (GTK_WIDGET_REALIZED (notebook) && !GTK_WIDGET_REALIZED (tab_label)) gtk_widget_realize (tab_label); if (GTK_WIDGET_MAPPED (notebook) && !GTK_WIDGET_MAPPED (tab_label)) gtk_widget_map (tab_label); } if (GTK_WIDGET_VISIBLE (child) && GTK_WIDGET_VISIBLE (notebook)) gtk_widget_queue_resize (child); } void gtk_notebook_remove_page (GtkNotebook *notebook, gint page_num) { GtkNotebookPage *page; GList *tmp_list; g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); tmp_list = g_list_nth (notebook->children, page_num); if (tmp_list) { page = tmp_list->data; if (notebook->cur_page == page) gtk_notebook_prev_page (notebook); if (notebook->cur_page == page) notebook->cur_page = NULL; notebook->children = g_list_remove_link (notebook->children, tmp_list); g_list_free (tmp_list); g_free (page); } } gint gtk_notebook_current_page (GtkNotebook *notebook) { GList *children; gint cur_page; g_return_val_if_fail (notebook != NULL, -1); g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1); if (notebook->cur_page) { cur_page = 0; children = notebook->children; while (children) { if (children->data == notebook->cur_page) break; children = children->next; cur_page += 1; } if (!children) cur_page = -1; } else { cur_page = -1; } return cur_page; } void gtk_notebook_set_page (GtkNotebook *notebook, gint page_num) { GtkNotebookPage *page; GList *tmp_list; g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); tmp_list = g_list_nth (notebook->children, page_num); if (tmp_list) { page = tmp_list->data; gtk_notebook_switch_page (notebook, page); } } void gtk_notebook_next_page (GtkNotebook *notebook) { GtkNotebookPage *page; GList *children; g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); children = notebook->children; while (children) { page = children->data; if (notebook->cur_page == page) { children = children->next; if (!children) children = notebook->children; page = children->data; gtk_notebook_switch_page (notebook, page); } children = children->next; } } void gtk_notebook_prev_page (GtkNotebook *notebook) { GtkNotebookPage *page; GList *children; g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); children = notebook->children; while (children) { page = children->data; if (notebook->cur_page == page) { children = children->prev; if (!children) children = g_list_last (notebook->children); page = children->data; gtk_notebook_switch_page (notebook, page); } children = children->next; } } void gtk_notebook_set_tab_pos (GtkNotebook *notebook, GtkPositionType pos) { g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); if (notebook->tab_pos != pos) { notebook->tab_pos = pos; if (GTK_WIDGET_VISIBLE (notebook)) gtk_widget_queue_resize (GTK_WIDGET (notebook)); } } void gtk_notebook_set_show_tabs (GtkNotebook *notebook, gint show_tabs) { g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); if (notebook->show_tabs != show_tabs) { notebook->show_tabs = show_tabs; if (GTK_WIDGET_VISIBLE (notebook)) gtk_widget_queue_resize (GTK_WIDGET (notebook)); } } void gtk_notebook_set_show_border (GtkNotebook *notebook, gint show_border) { g_return_if_fail (notebook != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (notebook)); if (notebook->show_border != show_border) { notebook->show_border = show_border; if (GTK_WIDGET_VISIBLE (notebook)) gtk_widget_queue_resize (GTK_WIDGET (notebook)); } } static void gtk_notebook_destroy (GtkObject *object) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (object)); notebook = GTK_NOTEBOOK (object); children = notebook->children; while (children) { page = children->data; children = children->next; page->child->parent = NULL; page->tab_label->parent = NULL; gtk_object_unref (GTK_OBJECT (page->child)); gtk_object_unref (GTK_OBJECT (page->tab_label)); gtk_widget_destroy (page->child); gtk_widget_destroy (page->tab_label); g_free (page); } g_list_free (notebook->children); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); } static void gtk_notebook_map (GtkWidget *widget) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED); gdk_window_show (widget->window); notebook = GTK_NOTEBOOK (widget); if (notebook->cur_page && GTK_WIDGET_VISIBLE (notebook->cur_page->child) && !GTK_WIDGET_MAPPED (notebook->cur_page->child)) gtk_widget_map (notebook->cur_page->child); children = notebook->children; while (children) { page = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (page->child) && !GTK_WIDGET_MAPPED (page->tab_label)) gtk_widget_map (page->tab_label); } } static void gtk_notebook_unmap (GtkWidget *widget) { g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED); gdk_window_hide (widget->window); } static void gtk_notebook_realize (GtkWidget *widget) { GtkNotebook *notebook; GdkWindowAttr attributes; gint attributes_mask; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); notebook = GTK_NOTEBOOK (widget); GTK_WIDGET_SET_FLAGS (notebook, GTK_REALIZED); attributes.window_type = GDK_WINDOW_CHILD; attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.event_mask = gtk_widget_get_events (widget); attributes.event_mask |= GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, notebook); widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); } static void gtk_notebook_unrealize (GtkWidget *widget) { g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); GTK_WIDGET_UNSET_FLAGS (widget, GTK_REALIZED | GTK_MAPPED); gtk_style_detach (widget->style); gdk_window_destroy (widget->window); widget->window = NULL; } static void gtk_notebook_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; gint tab_width; gint tab_height; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); g_return_if_fail (requisition != NULL); notebook = GTK_NOTEBOOK (widget); widget->requisition.width = 0; widget->requisition.height = 0; children = notebook->children; while (children) { page = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (page->child)) { gtk_widget_size_request (page->child, &page->child->requisition); widget->requisition.width = MAX (widget->requisition.width, page->child->requisition.width); widget->requisition.height = MAX (widget->requisition.height, page->child->requisition.height); } } widget->requisition.width += GTK_CONTAINER (widget)->border_width * 2; widget->requisition.height += GTK_CONTAINER (widget)->border_width * 2; if (notebook->show_tabs) { widget->requisition.width += GTK_WIDGET (widget)->style->klass->xthickness * 2; widget->requisition.height += GTK_WIDGET (widget)->style->klass->ythickness * 2; tab_width = 0; tab_height = 0; children = notebook->children; while (children) { page = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (page->child)) { gtk_widget_size_request (page->tab_label, &page->tab_label->requisition); page->requisition.width = (page->tab_label->requisition.width + (GTK_WIDGET (widget)->style->klass->xthickness + CHILD_SPACING) * 2); page->requisition.height = (page->tab_label->requisition.height + (GTK_WIDGET (widget)->style->klass->ythickness + CHILD_SPACING) * 2); switch (notebook->tab_pos) { case GTK_POS_TOP: case GTK_POS_BOTTOM: page->requisition.width -= TAB_OVERLAP; page->requisition.height -= GTK_WIDGET (widget)->style->klass->ythickness; tab_width += page->requisition.width; tab_height = MAX (tab_height, page->requisition.height); break; case GTK_POS_LEFT: case GTK_POS_RIGHT: page->requisition.width -= GTK_WIDGET (widget)->style->klass->xthickness; page->requisition.height -= TAB_OVERLAP; tab_width = MAX (tab_width, page->requisition.width); tab_height += page->requisition.height; break; } } } children = notebook->children; while (children) { page = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (page->child)) { if ((notebook->tab_pos == GTK_POS_TOP) || (notebook->tab_pos == GTK_POS_BOTTOM)) page->requisition.height = tab_height; else page->requisition.width = tab_width; } } switch (notebook->tab_pos) { case GTK_POS_TOP: case GTK_POS_BOTTOM: tab_width += GTK_WIDGET (widget)->style->klass->xthickness; widget->requisition.width = MAX (widget->requisition.width, tab_width); widget->requisition.height += tab_height; break; case GTK_POS_LEFT: case GTK_POS_RIGHT: tab_height += GTK_WIDGET (widget)->style->klass->ythickness; widget->requisition.width += tab_width; widget->requisition.height = MAX (widget->requisition.height, tab_height); break; } } else if (notebook->show_border) { widget->requisition.width += GTK_WIDGET (widget)->style->klass->xthickness * 2; widget->requisition.height += GTK_WIDGET (widget)->style->klass->ythickness * 2; } } static void gtk_notebook_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkNotebook *notebook; GtkNotebookPage *page; GtkAllocation child_allocation; GList *children; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); g_return_if_fail (allocation != NULL); widget->allocation = *allocation; if (GTK_WIDGET_REALIZED (widget)) gdk_window_move_resize (widget->window, allocation->x, allocation->y, allocation->width, allocation->height); notebook = GTK_NOTEBOOK (widget); if (notebook->children) { child_allocation.x = GTK_CONTAINER (widget)->border_width; child_allocation.y = GTK_CONTAINER (widget)->border_width; child_allocation.width = allocation->width - child_allocation.x * 2; child_allocation.height = allocation->height - child_allocation.y * 2; if (notebook->show_tabs || notebook->show_border) { child_allocation.x += GTK_WIDGET (widget)->style->klass->xthickness; child_allocation.y += GTK_WIDGET (widget)->style->klass->ythickness; child_allocation.width -= GTK_WIDGET (widget)->style->klass->xthickness * 2; child_allocation.height -= GTK_WIDGET (widget)->style->klass->ythickness * 2; if (notebook->show_tabs && notebook->children) { switch (notebook->tab_pos) { case GTK_POS_TOP: child_allocation.y += notebook->cur_page->requisition.height; case GTK_POS_BOTTOM: child_allocation.height -= notebook->cur_page->requisition.height; break; case GTK_POS_LEFT: child_allocation.x += notebook->cur_page->requisition.width; case GTK_POS_RIGHT: child_allocation.width -= notebook->cur_page->requisition.width; break; } } } children = notebook->children; while (children) { page = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (page->child)) gtk_widget_size_allocate (page->child, &child_allocation); } if (notebook->show_tabs && notebook->children) gtk_notebook_pages_allocate (notebook, allocation); } } static void gtk_notebook_paint (GtkWidget *widget, GdkRectangle *area) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; GdkPoint points[6]; gint width, height; gint x, y; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); g_return_if_fail (area != NULL); if (GTK_WIDGET_DRAWABLE (widget)) { notebook = GTK_NOTEBOOK (widget); gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height); if (notebook->show_tabs || notebook->show_border) { x = GTK_CONTAINER (widget)->border_width; y = GTK_CONTAINER (widget)->border_width; width = widget->allocation.width - x * 2; height = widget->allocation.height - y * 2; if (notebook->show_tabs && notebook->children) { switch (notebook->tab_pos) { case GTK_POS_TOP: y += notebook->cur_page->allocation.height; case GTK_POS_BOTTOM: height -= notebook->cur_page->allocation.height; break; case GTK_POS_LEFT: x += notebook->cur_page->allocation.width; case GTK_POS_RIGHT: width -= notebook->cur_page->allocation.width; break; } switch (notebook->tab_pos) { case GTK_POS_TOP: points[0].x = notebook->cur_page->allocation.x; points[0].y = y; points[1].x = x; points[1].y = y; points[2].x = x; points[2].y = y + height - 1; points[3].x = x + width - 1; points[3].y = y + height - 1; points[4].x = x + width - 1; points[4].y = y; points[5].x = (notebook->cur_page->allocation.x + notebook->cur_page->allocation.width - GTK_WIDGET (notebook)->style->klass->xthickness); points[5].y = y; if (points[5].x == (x + width)) points[5].x -= 1; break; case GTK_POS_BOTTOM: points[0].x = (notebook->cur_page->allocation.x + notebook->cur_page->allocation.width - GTK_WIDGET (notebook)->style->klass->xthickness); points[0].y = y + height - 1; points[1].x = x + width - 1; points[1].y = y + height - 1; points[2].x = x + width - 1; points[2].y = y; points[3].x = x; points[3].y = y; points[4].x = x; points[4].y = y + height - 1; points[5].x = notebook->cur_page->allocation.x; points[5].y = y + height - 1; if (points[0].x == (x + width)) points[0].x -= 1; break; case GTK_POS_LEFT: points[0].x = x; points[0].y = (notebook->cur_page->allocation.y + notebook->cur_page->allocation.height - GTK_WIDGET (notebook)->style->klass->ythickness); points[1].x = x; points[1].y = y + height - 1; points[2].x = x + width - 1; points[2].y = y + height - 1; points[3].x = x + width - 1; points[3].y = y; points[4].x = x; points[4].y = y; points[5].x = x; points[5].y = notebook->cur_page->allocation.y; if (points[0].y == (y + height)) points[0].y -= 1; break; case GTK_POS_RIGHT: points[0].x = x + width - 1; points[0].y = notebook->cur_page->allocation.y; points[1].x = x + width - 1; points[1].y = y; points[2].x = x; points[2].y = y; points[3].x = x; points[3].y = y + height - 1; points[4].x = x + width - 1; points[4].y = y + height - 1; points[5].x = x + width - 1; points[5].y = (notebook->cur_page->allocation.y + notebook->cur_page->allocation.height - GTK_WIDGET (notebook)->style->klass->ythickness); if (points[5].y == (y + height)) points[5].y -= 1; break; } gtk_draw_polygon (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, points, 6, FALSE); children = g_list_last (notebook->children); while (children) { page = children->data; children = children->prev; if (notebook->cur_page != page) gtk_notebook_draw_tab (notebook, page, area); } if (notebook->cur_page) gtk_notebook_draw_tab (notebook, notebook->cur_page, area); } else if (notebook->show_border) { gtk_draw_shadow (widget->style, widget->window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, x, y, width, height); } } } } static void gtk_notebook_draw (GtkWidget *widget, GdkRectangle *area) { GtkNotebook *notebook; GdkRectangle child_area; g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (widget)); g_return_if_fail (area != NULL); if (GTK_WIDGET_DRAWABLE (widget)) { notebook = GTK_NOTEBOOK (widget); gtk_notebook_paint (widget, area); if (notebook->cur_page && gtk_widget_intersect (notebook->cur_page->child, area, &child_area)) gtk_widget_draw (notebook->cur_page->child, &child_area); } } static gint gtk_notebook_expose (GtkWidget *widget, GdkEventExpose *event) { GtkNotebook *notebook; GdkEventExpose child_event; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (GTK_WIDGET_DRAWABLE (widget)) { notebook = GTK_NOTEBOOK (widget); gtk_notebook_paint (widget, &event->area); child_event = *event; if (notebook->cur_page && GTK_WIDGET_NO_WINDOW (notebook->cur_page->child) && gtk_widget_intersect (notebook->cur_page->child, &event->area, &child_event.area)) gtk_widget_event (notebook->cur_page->child, (GdkEvent*) &child_event); } return FALSE; } static gint gtk_notebook_button_press (GtkWidget *widget, GdkEventButton *event) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; g_return_val_if_fail (widget != NULL, FALSE); g_return_val_if_fail (GTK_IS_NOTEBOOK (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); if ((event->type != GDK_BUTTON_PRESS) || (event->window != widget->window)) return FALSE; notebook = GTK_NOTEBOOK (widget); children = notebook->children; while (children) { page = children->data; if (GTK_WIDGET_VISIBLE (page->child) && (event->x >= page->allocation.x) && (event->y >= page->allocation.y) && (event->x <= (page->allocation.x + page->allocation.width)) && (event->y <= (page->allocation.y + page->allocation.height))) { gtk_notebook_switch_page (notebook, page); break; } children = children->next; } return FALSE; } static void gtk_notebook_add (GtkContainer *container, GtkWidget *widget) { g_warning ("gtk_notebook_add: use gtk_notebook_{append,prepend}_page instead\n"); } static void gtk_notebook_remove (GtkContainer *container, GtkWidget *widget) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; g_return_if_fail (container != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (container)); g_return_if_fail (widget != NULL); notebook = GTK_NOTEBOOK (container); children = notebook->children; while (children) { page = children->data; if (page->child == widget) { gtk_widget_unparent (page->child); gtk_widget_unparent (page->tab_label); notebook->children = g_list_remove_link (notebook->children, children); g_list_free (children); g_free (page); if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (container)) gtk_widget_queue_resize (GTK_WIDGET (container)); break; } children = children->next; } } static void gtk_notebook_foreach (GtkContainer *container, GtkCallback callback, gpointer callback_data) { GtkNotebook *notebook; GtkNotebookPage *page; GList *children; g_return_if_fail (container != NULL); g_return_if_fail (GTK_IS_NOTEBOOK (container)); g_return_if_fail (callback != NULL); notebook = GTK_NOTEBOOK (container); children = notebook->children; while (children) { page = children->data; children = children->next; (* callback) (page->child, callback_data); } } static void gtk_real_notebook_switch_page (GtkNotebook *notebook, GtkNotebookPage *page) { g_return_if_fail (notebook != NULL); g_return_if_fail (page != NULL); if (notebook->cur_page != page) { if (notebook->cur_page && GTK_WIDGET_MAPPED (notebook->cur_page->child)) gtk_widget_unmap (notebook->cur_page->child); notebook->cur_page = page; gtk_notebook_pages_allocate (notebook, >K_WIDGET (notebook)->allocation); if (GTK_WIDGET_MAPPED (notebook)) { if (GTK_WIDGET_REALIZED (notebook->cur_page->child)) { gtk_widget_map (notebook->cur_page->child); } else { gtk_widget_map (notebook->cur_page->child); gtk_widget_size_allocate (GTK_WIDGET (notebook), >K_WIDGET (notebook)->allocation); } } if (GTK_WIDGET_DRAWABLE (notebook)) gtk_widget_queue_draw (GTK_WIDGET (notebook)); } } static void gtk_notebook_draw_tab (GtkNotebook *notebook, GtkNotebookPage *page, GdkRectangle *area) { GdkRectangle child_area; GdkRectangle page_area; GtkStateType state_type; GdkPoint points[6]; gint n; g_return_if_fail (notebook != NULL); g_return_if_fail (page != NULL); g_return_if_fail (area != NULL); page_area.x = page->allocation.x; page_area.y = page->allocation.y; page_area.width = page->allocation.width; page_area.height = page->allocation.height; if (gdk_rectangle_intersect (&page_area, area, &child_area)) { GtkWidget *widget; switch (notebook->tab_pos) { case GTK_POS_TOP: if( child_area.x + child_area.width > page->allocation.x + page->allocation.width - TAB_OVERLAP ) { points[0].x = page->allocation.x + page->allocation.width - 1; points[0].y = page->allocation.y + page->allocation.height - 1; points[1].x = page->allocation.x + page->allocation.width - 1; points[1].y = page->allocation.y + TAB_CURVATURE; points[2].x = page->allocation.x + page->allocation.width - TAB_CURVATURE - 1; points[2].y = page->allocation.y; n = 3; } else { points[0].x = page->allocation.x + page->allocation.width - TAB_OVERLAP - 1; points[0].y = page->allocation.y; n = 1; } if( (child_area.x < page->allocation.x + TAB_OVERLAP) && (page == notebook->cur_page || page == (GtkNotebookPage *)(notebook->children->data)) ) { points[n].x = page->allocation.x + TAB_CURVATURE; points[n++].y = page->allocation.y; points[n].x = page->allocation.x; points[n++].y = page->allocation.y + TAB_CURVATURE; points[n].x = page->allocation.x; points[n++].y = page->allocation.y + page->allocation.height - 1; } else { points[n].x = page->allocation.x + TAB_OVERLAP; points[n++].y = page->allocation.y; } break; case GTK_POS_BOTTOM: if( (child_area.x < page->allocation.x + TAB_OVERLAP) && (page == notebook->cur_page || page == (GtkNotebookPage *)(notebook->children->data)) ) { points[0].x = page->allocation.x; points[0].y = page->allocation.y; points[1].x = page->allocation.x; points[1].y = page->allocation.y + page->allocation.height - TAB_CURVATURE - 1; points[2].x = page->allocation.x + TAB_CURVATURE; points[2].y = page->allocation.y + page->allocation.height - 1; n = 3; } else { points[0].x = page->allocation.x + TAB_OVERLAP; points[0].y = page->allocation.y + page->allocation.height - 1; n = 1; } if( child_area.x + child_area.width > page->allocation.x + page->allocation.width - TAB_OVERLAP ) { points[n].x = page->allocation.x + page->allocation.width - TAB_CURVATURE - 1; points[n++].y = page->allocation.y + page->allocation.height - 1; points[n].x = page->allocation.x + page->allocation.width - 1; points[n++].y = page->allocation.y + page->allocation.height - TAB_CURVATURE - 1; points[n].x = page->allocation.x + page->allocation.width - 1; points[n++].y = page->allocation.y; } else { points[n].x = page->allocation.x + page->allocation.width - TAB_OVERLAP - 1; points[n++].y = page->allocation.y + page->allocation.height - 1; } break; case GTK_POS_LEFT: if( (child_area.y < page->allocation.y + TAB_OVERLAP) && (page == notebook->cur_page || page == (GtkNotebookPage *)(notebook->children->data)) ) { points[0].x = page->allocation.x + page->allocation.width - 1; points[0].y = page->allocation.y; points[1].x = page->allocation.x + TAB_CURVATURE; points[1].y = page->allocation.y; points[2].x = page->allocation.x; points[2].y = page->allocation.y + TAB_CURVATURE; n = 3; } else { points[0].x = page->allocation.x; points[0].y = page->allocation.y + TAB_OVERLAP; n = 1; } if( child_area.y + child_area.height > page->allocation.y + page->allocation.height - TAB_OVERLAP ) { points[n].x = page->allocation.x; points[n++].y = page->allocation.y + page->allocation.height - TAB_CURVATURE - 1; points[n].x = page->allocation.x + TAB_CURVATURE; points[n++].y = page->allocation.y + page->allocation.height - 1; points[n].x = page->allocation.x + page->allocation.width - 1; points[n++].y = page->allocation.y + page->allocation.height - 1; } else { points[n].x = page->allocation.x; points[n++].y = page->allocation.y + page->allocation.height - TAB_OVERLAP - 1; } break; case GTK_POS_RIGHT: if( child_area.y + child_area.height > page->allocation.y + page->allocation.height - TAB_OVERLAP ) { points[0].x = page->allocation.x; points[0].y = page->allocation.y + page->allocation.height - 1; points[1].x = page->allocation.x + page->allocation.width - TAB_CURVATURE - 1; points[1].y = page->allocation.y + page->allocation.height - 1; points[2].x = page->allocation.x + page->allocation.width - 1; points[2].y = page->allocation.y + page->allocation.height - TAB_CURVATURE - 1; n = 3; } else { points[0].x = page->allocation.x + page->allocation.width - 1; points[0].y = page->allocation.y + page->allocation.height - TAB_OVERLAP - 1; n = 1; } if( (child_area.y < page->allocation.y + TAB_OVERLAP) && (page == notebook->cur_page || page == (GtkNotebookPage *)(notebook->children->data)) ) { points[n].x = page->allocation.x + page->allocation.width - 1; points[n++].y = page->allocation.y + TAB_CURVATURE; points[n].x = page->allocation.x + page->allocation.width - TAB_CURVATURE - 1; points[n++].y = page->allocation.y; points[n].x = page->allocation.x; points[n++].y = page->allocation.y; } else { points[n].x = page->allocation.x + page->allocation.width - 1; points[n++].y = page->allocation.y + TAB_OVERLAP; } break; } widget = GTK_WIDGET(notebook); if (notebook->cur_page == page) state_type = GTK_STATE_NORMAL; else { state_type = GTK_STATE_ACTIVE; gdk_draw_rectangle (widget->window, widget->style->bg_gc[state_type], TRUE, child_area.x, child_area.y, child_area.width, child_area.height); } gtk_draw_polygon (widget->style, widget->window, state_type, GTK_SHADOW_OUT, points, n, FALSE); if (gtk_widget_intersect (page->tab_label, area, &child_area)) gtk_widget_draw (page->tab_label, &child_area); } } static void gtk_notebook_pages_allocate (GtkNotebook *notebook, GtkAllocation *allocation) { GtkNotebookPage *page; GtkAllocation child_allocation; GList *children; if (notebook->show_tabs && notebook->children) { child_allocation.x = GTK_CONTAINER (notebook)->border_width; child_allocation.y = GTK_CONTAINER (notebook)->border_width; switch (notebook->tab_pos) { case GTK_POS_BOTTOM: child_allocation.y = allocation->height - notebook->cur_page->requisition.height; case GTK_POS_TOP: child_allocation.height = notebook->cur_page->requisition.height; break; case GTK_POS_RIGHT: child_allocation.x = allocation->width - notebook->cur_page->requisition.width; case GTK_POS_LEFT: child_allocation.width = notebook->cur_page->requisition.width; break; } children = notebook->children; while (children) { page = children->data; children = children->next; if (GTK_WIDGET_VISIBLE (page->child)) { switch (notebook->tab_pos) { case GTK_POS_TOP: case GTK_POS_BOTTOM: child_allocation.width = page->requisition.width + TAB_OVERLAP; break; case GTK_POS_LEFT: case GTK_POS_RIGHT: child_allocation.height = page->requisition.height + TAB_OVERLAP; break; } gtk_notebook_page_allocate (notebook, page, &child_allocation); switch (notebook->tab_pos) { case GTK_POS_TOP: case GTK_POS_BOTTOM: child_allocation.x += child_allocation.width - TAB_OVERLAP; break; case GTK_POS_LEFT: case GTK_POS_RIGHT: child_allocation.y += child_allocation.height - TAB_OVERLAP; break; } } } } } static void gtk_notebook_page_allocate (GtkNotebook *notebook, GtkNotebookPage *page, GtkAllocation *allocation) { GtkAllocation child_allocation; gint xthickness, ythickness; g_return_if_fail (notebook != NULL); g_return_if_fail (page != NULL); g_return_if_fail (allocation != NULL); page->allocation = *allocation; xthickness = GTK_WIDGET (notebook)->style->klass->xthickness; ythickness = GTK_WIDGET (notebook)->style->klass->ythickness; if (notebook->cur_page != page) { switch (notebook->tab_pos) { case GTK_POS_TOP: page->allocation.y += ythickness; case GTK_POS_BOTTOM: page->allocation.height -= ythickness; break; case GTK_POS_LEFT: page->allocation.x += xthickness; case GTK_POS_RIGHT: page->allocation.width -= xthickness; break; } } switch (notebook->tab_pos) { case GTK_POS_TOP: child_allocation.x = xthickness + CHILD_SPACING; child_allocation.y = ythickness + CHILD_SPACING; child_allocation.width = page->allocation.width - child_allocation.x * 2; child_allocation.height = page->allocation.height - child_allocation.y; child_allocation.x += page->allocation.x; child_allocation.y += page->allocation.y; break; case GTK_POS_BOTTOM: child_allocation.x = xthickness + CHILD_SPACING; child_allocation.y = ythickness + CHILD_SPACING; child_allocation.width = page->allocation.width - child_allocation.x * 2; child_allocation.height = page->allocation.height - child_allocation.y; child_allocation.x += page->allocation.x; child_allocation.y = page->allocation.y; break; case GTK_POS_LEFT: child_allocation.x = xthickness + CHILD_SPACING; child_allocation.y = ythickness + CHILD_SPACING; child_allocation.width = page->allocation.width - child_allocation.x; child_allocation.height = page->allocation.height - child_allocation.y * 2; child_allocation.x += page->allocation.x; child_allocation.y += page->allocation.y; break; case GTK_POS_RIGHT: child_allocation.x = xthickness + CHILD_SPACING; child_allocation.y = ythickness + CHILD_SPACING; child_allocation.width = page->allocation.width - child_allocation.x; child_allocation.height = page->allocation.height - child_allocation.y * 2; child_allocation.x = page->allocation.x; child_allocation.y += page->allocation.y; break; } gtk_widget_size_allocate (page->tab_label, &child_allocation); } static void gtk_notebook_switch_page (GtkNotebook *notebook, GtkNotebookPage *page) { gtk_signal_emit (GTK_OBJECT (notebook), notebook_signals[SWITCH_PAGE], page); } static void gtk_notebook_marshal_signal (GtkObject *object, GtkSignalFunc func, gpointer func_data, GtkArg *args) { GtkNotebookSignal rfunc; rfunc = (GtkNotebookSignal) func; (* rfunc) (object, GTK_VALUE_OBJECT (args[0]), func_data); }