1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp:ftp.gtk.org/pub/gtk/.
31 #include "gtknotebook.h"
35 #include "gtkmenuitem.h"
38 #include "gtkmarshalers.h"
39 #include "gtkbindings.h"
40 #include "gtkprivate.h"
42 #include "gtkbuildable.h"
43 #include "gtktypebuiltins.h"
44 #include "gtkwidgetpath.h"
45 #include "gtkwidgetprivate.h"
46 #include "a11y/gtknotebookaccessible.h"
51 * @Short_description: A tabbed notebook container
53 * @See_also: #GtkContainer
55 * The #GtkNotebook widget is a #GtkContainer whose children are pages that
56 * can be switched between using tab labels along one edge.
58 * There are many configuration options for GtkNotebook. Among other
59 * things, you can choose on which edge the tabs appear
60 * (see gtk_notebook_set_tab_pos()), whether, if there are too many
61 * tabs to fit the notebook should be made bigger or scrolling
62 * arrows added (see gtk_notebook_set_scrollable()), and whether there
63 * will be a popup menu allowing the users to switch pages.
64 * (see gtk_notebook_popup_enable(), gtk_notebook_popup_disable())
66 * <refsect2 id="GtkNotebook-BUILDER-UI">
67 * <title>GtkNotebook as GtkBuildable</title>
69 * The GtkNotebook implementation of the #GtkBuildable interface
70 * supports placing children into tabs by specifying "tab" as the
71 * "type" attribute of a <child> element. Note that the content
72 * of the tab must be created before the tab can be filled.
73 * A tab child can be specified without specifying a <child>
76 * To add a child widget in the notebooks action area, specify
77 * "action-start" or "action-end" as the "type" attribute of the <child>
81 * <title>A UI definition fragment with GtkNotebook</title>
82 * <programlisting><![CDATA[
83 * <object class="GtkNotebook">
85 * <object class="GtkLabel" id="notebook-content">
86 * <property name="label">Content</property>
90 * <object class="GtkLabel" id="notebook-tab">
91 * <property name="label">Tab</property>
95 * ]]></programlisting>
101 #define SCROLL_DELAY_FACTOR 5
102 #define SCROLL_THRESHOLD 12
103 #define DND_THRESHOLD_MULTIPLIER 4
104 #define FRAMES_PER_SECOND 45
105 #define MSECS_BETWEEN_UPDATES (1000 / FRAMES_PER_SECOND)
107 typedef struct _GtkNotebookPage GtkNotebookPage;
112 DRAG_OPERATION_REORDER,
113 DRAG_OPERATION_DETACH
114 } GtkNotebookDragOperation;
122 struct _GtkNotebookPrivate
124 GtkNotebookDragOperation operation;
125 GtkNotebookPage *cur_page;
126 GtkNotebookPage *detached_tab;
127 GtkTargetList *source_targets;
128 GtkWidget *action_widget[N_ACTION_WIDGETS];
129 GtkWidget *dnd_window;
132 GdkWindow *drag_window;
133 GdkWindow *event_window;
136 GList *first_tab; /* The first tab visible (for scrolling notebooks) */
152 guint switch_tab_timer;
158 guint child_has_focus : 1;
159 guint click_child : 3;
160 guint during_detach : 1;
161 guint during_reorder : 1;
162 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
163 guint has_scrolled : 1;
165 guint need_timer : 1;
166 guint show_border : 1;
168 guint scrollable : 1;
171 guint has_before_previous : 1;
172 guint has_before_next : 1;
173 guint has_after_previous : 1;
174 guint has_after_next : 1;
210 } GtkNotebookPointerPosition;
212 #define ARROW_IS_LEFT(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
213 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
228 CHILD_PROP_TAB_LABEL,
229 CHILD_PROP_MENU_LABEL,
231 CHILD_PROP_TAB_EXPAND,
233 CHILD_PROP_REORDERABLE,
234 CHILD_PROP_DETACHABLE
237 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
239 /* some useful defines for calculating coords */
240 #define PAGE_LEFT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x)
241 #define PAGE_RIGHT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
242 #define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
243 #define PAGE_TOP_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y)
244 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
245 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
246 #define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent (((GtkNotebookPage *) (_page_))->tab_label) == ((GtkWidget *) (_notebook_)))
248 struct _GtkNotebookPage
251 GtkWidget *tab_label;
252 GtkWidget *menu_label;
253 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
255 guint default_menu : 1; /* If true, we create the menu label ourself */
256 guint default_tab : 1; /* If true, we create the tab label ourself */
259 guint reorderable : 1;
260 guint detachable : 1;
262 /* if true, the tab label was visible on last allocation; we track this so
263 * that we know to redraw the tab area if a tab label was hidden then shown
264 * without changing position */
265 guint tab_allocated_visible : 1;
267 GtkRequisition requisition;
268 GtkAllocation allocation;
270 gulong mnemonic_activate_signal;
271 gulong notify_visible_handler;
274 static const GtkTargetEntry notebook_targets [] = {
275 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
278 #ifdef G_DISABLE_CHECKS
279 #define CHECK_FIND_CHILD(notebook, child) \
280 gtk_notebook_find_child (notebook, child, G_STRLOC)
282 #define CHECK_FIND_CHILD(notebook, child) \
283 gtk_notebook_find_child (notebook, child, NULL)
286 /*** GtkNotebook Methods ***/
287 static gboolean gtk_notebook_select_page (GtkNotebook *notebook,
288 gboolean move_focus);
289 static gboolean gtk_notebook_focus_tab (GtkNotebook *notebook,
290 GtkNotebookTab type);
291 static gboolean gtk_notebook_change_current_page (GtkNotebook *notebook,
293 static void gtk_notebook_move_focus_out (GtkNotebook *notebook,
294 GtkDirectionType direction_type);
295 static gboolean gtk_notebook_reorder_tab (GtkNotebook *notebook,
296 GtkDirectionType direction_type,
297 gboolean move_to_last);
298 static void gtk_notebook_remove_tab_label (GtkNotebook *notebook,
299 GtkNotebookPage *page);
300 static void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
304 static void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
309 /*** GObject Methods ***/
310 static void gtk_notebook_set_property (GObject *object,
314 static void gtk_notebook_get_property (GObject *object,
319 /*** GtkWidget Methods ***/
320 static void gtk_notebook_destroy (GtkWidget *widget);
321 static void gtk_notebook_map (GtkWidget *widget);
322 static void gtk_notebook_unmap (GtkWidget *widget);
323 static void gtk_notebook_realize (GtkWidget *widget);
324 static void gtk_notebook_unrealize (GtkWidget *widget);
325 static void gtk_notebook_get_preferred_width (GtkWidget *widget,
328 static void gtk_notebook_get_preferred_height(GtkWidget *widget,
331 static void gtk_notebook_get_preferred_width_for_height
336 static void gtk_notebook_get_preferred_height_for_width
341 static void gtk_notebook_size_allocate (GtkWidget *widget,
342 GtkAllocation *allocation);
343 static gint gtk_notebook_draw (GtkWidget *widget,
345 static gint gtk_notebook_button_press (GtkWidget *widget,
346 GdkEventButton *event);
347 static gint gtk_notebook_button_release (GtkWidget *widget,
348 GdkEventButton *event);
349 static gboolean gtk_notebook_popup_menu (GtkWidget *widget);
350 static gint gtk_notebook_leave_notify (GtkWidget *widget,
351 GdkEventCrossing *event);
352 static gint gtk_notebook_motion_notify (GtkWidget *widget,
353 GdkEventMotion *event);
354 static gint gtk_notebook_focus_in (GtkWidget *widget,
355 GdkEventFocus *event);
356 static gint gtk_notebook_focus_out (GtkWidget *widget,
357 GdkEventFocus *event);
358 static void gtk_notebook_grab_notify (GtkWidget *widget,
359 gboolean was_grabbed);
360 static void gtk_notebook_state_flags_changed (GtkWidget *widget,
361 GtkStateFlags previous_state);
362 static gint gtk_notebook_focus (GtkWidget *widget,
363 GtkDirectionType direction);
364 static void gtk_notebook_style_updated (GtkWidget *widget);
366 /*** Drag and drop Methods ***/
367 static void gtk_notebook_drag_begin (GtkWidget *widget,
368 GdkDragContext *context);
369 static void gtk_notebook_drag_end (GtkWidget *widget,
370 GdkDragContext *context);
371 static gboolean gtk_notebook_drag_failed (GtkWidget *widget,
372 GdkDragContext *context,
373 GtkDragResult result);
374 static gboolean gtk_notebook_drag_motion (GtkWidget *widget,
375 GdkDragContext *context,
379 static void gtk_notebook_drag_leave (GtkWidget *widget,
380 GdkDragContext *context,
382 static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
383 GdkDragContext *context,
387 static void gtk_notebook_drag_data_get (GtkWidget *widget,
388 GdkDragContext *context,
389 GtkSelectionData *data,
392 static void gtk_notebook_drag_data_received (GtkWidget *widget,
393 GdkDragContext *context,
396 GtkSelectionData *data,
400 /*** GtkContainer Methods ***/
401 static void gtk_notebook_set_child_property (GtkContainer *container,
406 static void gtk_notebook_get_child_property (GtkContainer *container,
411 static void gtk_notebook_add (GtkContainer *container,
413 static void gtk_notebook_remove (GtkContainer *container,
415 static void gtk_notebook_set_focus_child (GtkContainer *container,
417 static GType gtk_notebook_child_type (GtkContainer *container);
418 static void gtk_notebook_forall (GtkContainer *container,
419 gboolean include_internals,
420 GtkCallback callback,
421 gpointer callback_data);
422 static GtkWidgetPath * gtk_notebook_get_path_for_child (GtkContainer *container,
425 /*** GtkNotebook Methods ***/
426 static gint gtk_notebook_real_insert_page (GtkNotebook *notebook,
428 GtkWidget *tab_label,
429 GtkWidget *menu_label,
432 static GtkNotebook *gtk_notebook_create_window (GtkNotebook *notebook,
437 /*** GtkNotebook Private Functions ***/
438 static void gtk_notebook_redraw_tabs (GtkNotebook *notebook);
439 static void gtk_notebook_redraw_tabs_junction (GtkNotebook *notebook);
440 static void gtk_notebook_redraw_arrows (GtkNotebook *notebook);
441 static void gtk_notebook_real_remove (GtkNotebook *notebook,
443 static void gtk_notebook_update_labels (GtkNotebook *notebook);
444 static gint gtk_notebook_timer (GtkNotebook *notebook);
445 static void gtk_notebook_set_scroll_timer (GtkNotebook *notebook);
446 static gint gtk_notebook_page_compare (gconstpointer a,
448 static GList* gtk_notebook_find_child (GtkNotebook *notebook,
450 const gchar *function);
451 static GList * gtk_notebook_search_page (GtkNotebook *notebook,
454 gboolean find_visible);
455 static void gtk_notebook_child_reordered (GtkNotebook *notebook,
456 GtkNotebookPage *page);
458 /*** GtkNotebook Drawing Functions ***/
459 static void gtk_notebook_paint (GtkWidget *widget,
461 static void gtk_notebook_draw_tab (GtkNotebook *notebook,
462 GtkNotebookPage *page,
465 static void gtk_notebook_draw_arrow (GtkNotebook *notebook,
467 GtkNotebookArrow arrow);
469 /*** GtkNotebook Size Allocate Functions ***/
470 static void gtk_notebook_pages_allocate (GtkNotebook *notebook);
471 static gboolean gtk_notebook_page_allocate (GtkNotebook *notebook,
472 GtkNotebookPage *page);
473 static void gtk_notebook_calc_tabs (GtkNotebook *notebook,
479 /*** GtkNotebook Page Switch Methods ***/
480 static void gtk_notebook_real_switch_page (GtkNotebook *notebook,
484 /*** GtkNotebook Page Switch Functions ***/
485 static void gtk_notebook_switch_page (GtkNotebook *notebook,
486 GtkNotebookPage *page);
487 static gint gtk_notebook_page_select (GtkNotebook *notebook,
488 gboolean move_focus);
489 static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
491 static void gtk_notebook_menu_switch_page (GtkWidget *widget,
492 GtkNotebookPage *page);
494 /*** GtkNotebook Menu Functions ***/
495 static void gtk_notebook_menu_item_create (GtkNotebook *notebook,
497 static void gtk_notebook_menu_label_unparent (GtkWidget *widget,
499 static void gtk_notebook_menu_detacher (GtkWidget *widget,
502 /*** GtkNotebook Private Setters ***/
503 static void gtk_notebook_update_tab_states (GtkNotebook *notebook);
504 static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
508 static gboolean focus_tabs_in (GtkNotebook *notebook);
509 static gboolean focus_child_in (GtkNotebook *notebook,
510 GtkDirectionType direction);
512 static void stop_scrolling (GtkNotebook *notebook);
513 static void do_detach_tab (GtkNotebook *from,
520 static void gtk_notebook_buildable_init (GtkBuildableIface *iface);
521 static void gtk_notebook_buildable_add_child (GtkBuildable *buildable,
526 static guint notebook_signals[LAST_SIGNAL] = { 0 };
528 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
529 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
530 gtk_notebook_buildable_init))
533 add_tab_bindings (GtkBindingSet *binding_set,
534 GdkModifierType modifiers,
535 GtkDirectionType direction)
537 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
539 GTK_TYPE_DIRECTION_TYPE, direction);
540 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
542 GTK_TYPE_DIRECTION_TYPE, direction);
546 add_arrow_bindings (GtkBindingSet *binding_set,
548 GtkDirectionType direction)
550 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
552 gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
554 GTK_TYPE_DIRECTION_TYPE, direction);
555 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
557 GTK_TYPE_DIRECTION_TYPE, direction);
561 add_reorder_bindings (GtkBindingSet *binding_set,
563 GtkDirectionType direction,
564 gboolean move_to_last)
566 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
568 gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
570 GTK_TYPE_DIRECTION_TYPE, direction,
571 G_TYPE_BOOLEAN, move_to_last);
572 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
574 GTK_TYPE_DIRECTION_TYPE, direction,
575 G_TYPE_BOOLEAN, move_to_last);
579 gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
581 const GValue *handler_return,
584 gboolean continue_emission;
587 object = g_value_get_object (handler_return);
588 g_value_set_object (return_accu, object);
589 continue_emission = !object;
591 return continue_emission;
595 gtk_notebook_compute_expand (GtkWidget *widget,
599 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
600 GtkNotebookPrivate *priv = notebook->priv;
604 GtkNotebookPage *page;
609 for (list = priv->children; list; list = list->next)
614 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_HORIZONTAL);
617 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);
619 if (hexpand & vexpand)
623 *hexpand_p = hexpand;
624 *vexpand_p = vexpand;
628 gtk_notebook_class_init (GtkNotebookClass *class)
630 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
631 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
632 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
633 GtkBindingSet *binding_set;
635 gobject_class->set_property = gtk_notebook_set_property;
636 gobject_class->get_property = gtk_notebook_get_property;
638 widget_class->destroy = gtk_notebook_destroy;
639 widget_class->map = gtk_notebook_map;
640 widget_class->unmap = gtk_notebook_unmap;
641 widget_class->realize = gtk_notebook_realize;
642 widget_class->unrealize = gtk_notebook_unrealize;
643 widget_class->get_preferred_width = gtk_notebook_get_preferred_width;
644 widget_class->get_preferred_height = gtk_notebook_get_preferred_height;
645 widget_class->get_preferred_width_for_height = gtk_notebook_get_preferred_width_for_height;
646 widget_class->get_preferred_height_for_width = gtk_notebook_get_preferred_height_for_width;
647 widget_class->size_allocate = gtk_notebook_size_allocate;
648 widget_class->draw = gtk_notebook_draw;
649 widget_class->button_press_event = gtk_notebook_button_press;
650 widget_class->button_release_event = gtk_notebook_button_release;
651 widget_class->popup_menu = gtk_notebook_popup_menu;
652 widget_class->leave_notify_event = gtk_notebook_leave_notify;
653 widget_class->motion_notify_event = gtk_notebook_motion_notify;
654 widget_class->grab_notify = gtk_notebook_grab_notify;
655 widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
656 widget_class->focus_in_event = gtk_notebook_focus_in;
657 widget_class->focus_out_event = gtk_notebook_focus_out;
658 widget_class->focus = gtk_notebook_focus;
659 widget_class->style_updated = gtk_notebook_style_updated;
660 widget_class->drag_begin = gtk_notebook_drag_begin;
661 widget_class->drag_end = gtk_notebook_drag_end;
662 widget_class->drag_motion = gtk_notebook_drag_motion;
663 widget_class->drag_leave = gtk_notebook_drag_leave;
664 widget_class->drag_drop = gtk_notebook_drag_drop;
665 widget_class->drag_data_get = gtk_notebook_drag_data_get;
666 widget_class->drag_data_received = gtk_notebook_drag_data_received;
667 widget_class->drag_failed = gtk_notebook_drag_failed;
668 widget_class->compute_expand = gtk_notebook_compute_expand;
670 container_class->add = gtk_notebook_add;
671 container_class->remove = gtk_notebook_remove;
672 container_class->forall = gtk_notebook_forall;
673 container_class->set_focus_child = gtk_notebook_set_focus_child;
674 container_class->get_child_property = gtk_notebook_get_child_property;
675 container_class->set_child_property = gtk_notebook_set_child_property;
676 container_class->child_type = gtk_notebook_child_type;
677 container_class->get_path_for_child = gtk_notebook_get_path_for_child;
679 class->switch_page = gtk_notebook_real_switch_page;
680 class->insert_page = gtk_notebook_real_insert_page;
682 class->focus_tab = gtk_notebook_focus_tab;
683 class->select_page = gtk_notebook_select_page;
684 class->change_current_page = gtk_notebook_change_current_page;
685 class->move_focus_out = gtk_notebook_move_focus_out;
686 class->reorder_tab = gtk_notebook_reorder_tab;
687 class->create_window = gtk_notebook_create_window;
689 g_object_class_install_property (gobject_class,
691 g_param_spec_int ("page",
693 P_("The index of the current page"),
697 GTK_PARAM_READWRITE));
698 g_object_class_install_property (gobject_class,
700 g_param_spec_enum ("tab-pos",
702 P_("Which side of the notebook holds the tabs"),
703 GTK_TYPE_POSITION_TYPE,
705 GTK_PARAM_READWRITE));
706 g_object_class_install_property (gobject_class,
708 g_param_spec_boolean ("show-tabs",
710 P_("Whether tabs should be shown"),
712 GTK_PARAM_READWRITE));
713 g_object_class_install_property (gobject_class,
715 g_param_spec_boolean ("show-border",
717 P_("Whether the border should be shown"),
719 GTK_PARAM_READWRITE));
720 g_object_class_install_property (gobject_class,
722 g_param_spec_boolean ("scrollable",
724 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
726 GTK_PARAM_READWRITE));
727 g_object_class_install_property (gobject_class,
729 g_param_spec_boolean ("enable-popup",
731 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
733 GTK_PARAM_READWRITE));
736 * GtkNotebook:group-name:
738 * Group name for tab drag and drop.
742 g_object_class_install_property (gobject_class,
744 g_param_spec_string ("group-name",
746 P_("Group name for tab drag and drop"),
748 GTK_PARAM_READWRITE));
750 gtk_container_class_install_child_property (container_class,
751 CHILD_PROP_TAB_LABEL,
752 g_param_spec_string ("tab-label",
754 P_("The string displayed on the child's tab label"),
756 GTK_PARAM_READWRITE));
757 gtk_container_class_install_child_property (container_class,
758 CHILD_PROP_MENU_LABEL,
759 g_param_spec_string ("menu-label",
761 P_("The string displayed in the child's menu entry"),
763 GTK_PARAM_READWRITE));
764 gtk_container_class_install_child_property (container_class,
766 g_param_spec_int ("position",
768 P_("The index of the child in the parent"),
770 GTK_PARAM_READWRITE));
771 gtk_container_class_install_child_property (container_class,
772 CHILD_PROP_TAB_EXPAND,
773 g_param_spec_boolean ("tab-expand",
775 P_("Whether to expand the child's tab"),
777 GTK_PARAM_READWRITE));
778 gtk_container_class_install_child_property (container_class,
780 g_param_spec_boolean ("tab-fill",
782 P_("Whether the child's tab should fill the allocated area"),
784 GTK_PARAM_READWRITE));
786 gtk_container_class_install_child_property (container_class,
787 CHILD_PROP_REORDERABLE,
788 g_param_spec_boolean ("reorderable",
789 P_("Tab reorderable"),
790 P_("Whether the tab is reorderable by user action"),
792 GTK_PARAM_READWRITE));
793 gtk_container_class_install_child_property (container_class,
794 CHILD_PROP_DETACHABLE,
795 g_param_spec_boolean ("detachable",
796 P_("Tab detachable"),
797 P_("Whether the tab is detachable"),
799 GTK_PARAM_READWRITE));
802 * GtkNotebook:has-secondary-backward-stepper:
804 * The "has-secondary-backward-stepper" property determines whether
805 * a second backward arrow button is displayed on the opposite end
810 gtk_widget_class_install_style_property (widget_class,
811 g_param_spec_boolean ("has-secondary-backward-stepper",
812 P_("Secondary backward stepper"),
813 P_("Display a second backward arrow button on the opposite end of the tab area"),
815 GTK_PARAM_READABLE));
818 * GtkNotebook:has-secondary-forward-stepper:
820 * The "has-secondary-forward-stepper" property determines whether
821 * a second forward arrow button is displayed on the opposite end
826 gtk_widget_class_install_style_property (widget_class,
827 g_param_spec_boolean ("has-secondary-forward-stepper",
828 P_("Secondary forward stepper"),
829 P_("Display a second forward arrow button on the opposite end of the tab area"),
831 GTK_PARAM_READABLE));
834 * GtkNotebook:has-backward-stepper:
836 * The "has-backward-stepper" property determines whether
837 * the standard backward arrow button is displayed.
841 gtk_widget_class_install_style_property (widget_class,
842 g_param_spec_boolean ("has-backward-stepper",
843 P_("Backward stepper"),
844 P_("Display the standard backward arrow button"),
846 GTK_PARAM_READABLE));
849 * GtkNotebook:has-forward-stepper:
851 * The "has-forward-stepper" property determines whether
852 * the standard forward arrow button is displayed.
856 gtk_widget_class_install_style_property (widget_class,
857 g_param_spec_boolean ("has-forward-stepper",
858 P_("Forward stepper"),
859 P_("Display the standard forward arrow button"),
861 GTK_PARAM_READABLE));
864 * GtkNotebook:tab-overlap:
866 * The "tab-overlap" property defines size of tab overlap
871 gtk_widget_class_install_style_property (widget_class,
872 g_param_spec_int ("tab-overlap",
874 P_("Size of tab overlap area"),
878 GTK_PARAM_READABLE));
881 * GtkNotebook:tab-curvature:
883 * The "tab-curvature" property defines size of tab curvature.
887 gtk_widget_class_install_style_property (widget_class,
888 g_param_spec_int ("tab-curvature",
890 P_("Size of tab curvature"),
894 GTK_PARAM_READABLE));
897 * GtkNotebook:arrow-spacing:
899 * The "arrow-spacing" property defines the spacing between the scroll
900 * arrows and the tabs.
904 gtk_widget_class_install_style_property (widget_class,
905 g_param_spec_int ("arrow-spacing",
907 P_("Scroll arrow spacing"),
911 GTK_PARAM_READABLE));
914 * GtkNotebook:initial-gap:
916 * The "initial-gap" property defines the minimum size for the initial
917 * gap between the first tab.
921 gtk_widget_class_install_style_property (widget_class,
922 g_param_spec_int ("initial-gap",
924 P_("Initial gap before the first tab"),
928 GTK_PARAM_READABLE));
931 * GtkNotebook::switch-page:
932 * @notebook: the object which received the signal.
933 * @page: the new current page
934 * @page_num: the index of the page
936 * Emitted when the user or a function changes the current page.
938 notebook_signals[SWITCH_PAGE] =
939 g_signal_new (I_("switch-page"),
940 G_TYPE_FROM_CLASS (gobject_class),
942 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
944 _gtk_marshal_VOID__OBJECT_UINT,
948 notebook_signals[FOCUS_TAB] =
949 g_signal_new (I_("focus-tab"),
950 G_TYPE_FROM_CLASS (gobject_class),
951 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
952 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
954 _gtk_marshal_BOOLEAN__ENUM,
956 GTK_TYPE_NOTEBOOK_TAB);
957 notebook_signals[SELECT_PAGE] =
958 g_signal_new (I_("select-page"),
959 G_TYPE_FROM_CLASS (gobject_class),
960 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
961 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
963 _gtk_marshal_BOOLEAN__BOOLEAN,
966 notebook_signals[CHANGE_CURRENT_PAGE] =
967 g_signal_new (I_("change-current-page"),
968 G_TYPE_FROM_CLASS (gobject_class),
969 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
970 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
972 _gtk_marshal_BOOLEAN__INT,
975 notebook_signals[MOVE_FOCUS_OUT] =
976 g_signal_new (I_("move-focus-out"),
977 G_TYPE_FROM_CLASS (gobject_class),
978 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
979 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
981 _gtk_marshal_VOID__ENUM,
983 GTK_TYPE_DIRECTION_TYPE);
984 notebook_signals[REORDER_TAB] =
985 g_signal_new (I_("reorder-tab"),
986 G_TYPE_FROM_CLASS (gobject_class),
987 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
988 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
990 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
992 GTK_TYPE_DIRECTION_TYPE,
995 * GtkNotebook::page-reordered:
996 * @notebook: the #GtkNotebook
997 * @child: the child #GtkWidget affected
998 * @page_num: the new page number for @child
1000 * the ::page-reordered signal is emitted in the notebook
1001 * right after a page has been reordered.
1005 notebook_signals[PAGE_REORDERED] =
1006 g_signal_new (I_("page-reordered"),
1007 G_TYPE_FROM_CLASS (gobject_class),
1009 G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1011 _gtk_marshal_VOID__OBJECT_UINT,
1016 * GtkNotebook::page-removed:
1017 * @notebook: the #GtkNotebook
1018 * @child: the child #GtkWidget affected
1019 * @page_num: the @child page number
1021 * the ::page-removed signal is emitted in the notebook
1022 * right after a page is removed from the notebook.
1026 notebook_signals[PAGE_REMOVED] =
1027 g_signal_new (I_("page-removed"),
1028 G_TYPE_FROM_CLASS (gobject_class),
1030 G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1032 _gtk_marshal_VOID__OBJECT_UINT,
1037 * GtkNotebook::page-added:
1038 * @notebook: the #GtkNotebook
1039 * @child: the child #GtkWidget affected
1040 * @page_num: the new page number for @child
1042 * the ::page-added signal is emitted in the notebook
1043 * right after a page is added to the notebook.
1047 notebook_signals[PAGE_ADDED] =
1048 g_signal_new (I_("page-added"),
1049 G_TYPE_FROM_CLASS (gobject_class),
1051 G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1053 _gtk_marshal_VOID__OBJECT_UINT,
1059 * GtkNotebook::create-window:
1060 * @notebook: the #GtkNotebook emitting the signal
1061 * @page: the tab of @notebook that is being detached
1062 * @x: the X coordinate where the drop happens
1063 * @y: the Y coordinate where the drop happens
1065 * The ::create-window signal is emitted when a detachable
1066 * tab is dropped on the root window.
1068 * A handler for this signal can create a window containing
1069 * a notebook where the tab will be attached. It is also
1070 * responsible for moving/resizing the window and adding the
1071 * necessary properties to the notebook (e.g. the
1072 * #GtkNotebook:group-name ).
1074 * Returns: (transfer none): a #GtkNotebook that @page should be
1075 * added to, or %NULL.
1079 notebook_signals[CREATE_WINDOW] =
1080 g_signal_new (I_("create-window"),
1081 G_TYPE_FROM_CLASS (gobject_class),
1083 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1084 gtk_object_handled_accumulator, NULL,
1085 _gtk_marshal_OBJECT__OBJECT_INT_INT,
1086 GTK_TYPE_NOTEBOOK, 3,
1087 GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
1089 binding_set = gtk_binding_set_by_class (class);
1090 gtk_binding_entry_add_signal (binding_set,
1093 G_TYPE_BOOLEAN, FALSE);
1094 gtk_binding_entry_add_signal (binding_set,
1095 GDK_KEY_KP_Space, 0,
1097 G_TYPE_BOOLEAN, FALSE);
1099 gtk_binding_entry_add_signal (binding_set,
1102 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1103 gtk_binding_entry_add_signal (binding_set,
1106 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1107 gtk_binding_entry_add_signal (binding_set,
1110 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1111 gtk_binding_entry_add_signal (binding_set,
1114 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1116 gtk_binding_entry_add_signal (binding_set,
1117 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1118 "change-current-page", 1,
1120 gtk_binding_entry_add_signal (binding_set,
1121 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1122 "change-current-page", 1,
1125 gtk_binding_entry_add_signal (binding_set,
1126 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1127 "change-current-page", 1,
1129 gtk_binding_entry_add_signal (binding_set,
1130 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1131 "change-current-page", 1,
1134 add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1135 add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1136 add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1137 add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1139 add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1140 add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1141 add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1142 add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1143 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1144 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1145 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1146 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1148 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1149 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1151 g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1153 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_NOTEBOOK_ACCESSIBLE);
1157 gtk_notebook_init (GtkNotebook *notebook)
1159 GtkNotebookPrivate *priv;
1160 GtkStyleContext *context;
1162 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1163 gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1165 notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1167 GtkNotebookPrivate);
1168 priv = notebook->priv;
1170 priv->cur_page = NULL;
1171 priv->children = NULL;
1172 priv->first_tab = NULL;
1173 priv->focus_tab = NULL;
1174 priv->event_window = NULL;
1177 priv->show_tabs = TRUE;
1178 priv->show_border = TRUE;
1179 priv->tab_pos = GTK_POS_TOP;
1180 priv->scrollable = FALSE;
1182 priv->click_child = 0;
1184 priv->need_timer = 0;
1185 priv->child_has_focus = FALSE;
1186 priv->focus_out = FALSE;
1188 priv->has_before_previous = 1;
1189 priv->has_before_next = 0;
1190 priv->has_after_previous = 0;
1191 priv->has_after_next = 1;
1194 priv->pressed_button = -1;
1195 priv->dnd_timer = 0;
1196 priv->switch_tab_timer = 0;
1197 priv->source_targets = gtk_target_list_new (notebook_targets,
1198 G_N_ELEMENTS (notebook_targets));
1199 priv->operation = DRAG_OPERATION_NONE;
1200 priv->detached_tab = NULL;
1201 priv->during_detach = FALSE;
1202 priv->has_scrolled = FALSE;
1204 gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1205 notebook_targets, G_N_ELEMENTS (notebook_targets),
1208 gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1210 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
1211 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
1215 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1217 iface->add_child = gtk_notebook_buildable_add_child;
1221 gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1222 GtkBuilder *builder,
1226 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1228 if (type && strcmp (type, "tab") == 0)
1232 page = gtk_notebook_get_nth_page (notebook, -1);
1233 /* To set the tab label widget, we must have already a child
1234 * inside the tab container. */
1235 g_assert (page != NULL);
1236 /* warn when Glade tries to overwrite label */
1237 if (gtk_notebook_get_tab_label (notebook, page))
1238 g_warning ("Overriding tab label for notebook");
1239 gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1241 else if (type && strcmp (type, "action-start") == 0)
1243 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1245 else if (type && strcmp (type, "action-end") == 0)
1247 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1250 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1252 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1256 gtk_notebook_select_page (GtkNotebook *notebook,
1257 gboolean move_focus)
1259 GtkNotebookPrivate *priv = notebook->priv;
1261 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1263 gtk_notebook_page_select (notebook, move_focus);
1271 gtk_notebook_focus_tab (GtkNotebook *notebook,
1272 GtkNotebookTab type)
1274 GtkNotebookPrivate *priv = notebook->priv;
1277 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1281 case GTK_NOTEBOOK_TAB_FIRST:
1282 list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1284 gtk_notebook_switch_focus_tab (notebook, list);
1286 case GTK_NOTEBOOK_TAB_LAST:
1287 list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1289 gtk_notebook_switch_focus_tab (notebook, list);
1300 gtk_notebook_change_current_page (GtkNotebook *notebook,
1303 GtkNotebookPrivate *priv = notebook->priv;
1304 GList *current = NULL;
1306 if (!priv->show_tabs)
1310 current = g_list_find (priv->children, priv->cur_page);
1314 current = gtk_notebook_search_page (notebook, current,
1315 offset < 0 ? STEP_PREV : STEP_NEXT,
1320 gboolean wrap_around;
1322 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1323 "gtk-keynav-wrap-around", &wrap_around,
1327 current = gtk_notebook_search_page (notebook, NULL,
1328 offset < 0 ? STEP_PREV : STEP_NEXT,
1334 offset += offset < 0 ? 1 : -1;
1338 gtk_notebook_switch_page (notebook, current->data);
1340 gtk_widget_error_bell (GTK_WIDGET (notebook));
1345 static GtkDirectionType
1346 get_effective_direction (GtkNotebook *notebook,
1347 GtkDirectionType direction)
1349 GtkNotebookPrivate *priv = notebook->priv;
1351 /* Remap the directions into the effective direction it would be for a
1352 * GTK_POS_TOP notebook
1355 #define D(rest) GTK_DIR_##rest
1357 static const GtkDirectionType translate_direction[2][4][6] = {
1358 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1359 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1360 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1361 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1362 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1363 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1364 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1365 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1370 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1372 return translate_direction[text_dir][priv->tab_pos][direction];
1376 get_effective_tab_pos (GtkNotebook *notebook)
1378 GtkNotebookPrivate *priv = notebook->priv;
1380 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1382 switch (priv->tab_pos)
1385 return GTK_POS_RIGHT;
1387 return GTK_POS_LEFT;
1392 return priv->tab_pos;
1396 get_tab_gap_pos (GtkNotebook *notebook)
1398 gint tab_pos = get_effective_tab_pos (notebook);
1399 gint gap_side = GTK_POS_BOTTOM;
1404 gap_side = GTK_POS_BOTTOM;
1406 case GTK_POS_BOTTOM:
1407 gap_side = GTK_POS_TOP;
1410 gap_side = GTK_POS_RIGHT;
1413 gap_side = GTK_POS_LEFT;
1421 gtk_notebook_move_focus_out (GtkNotebook *notebook,
1422 GtkDirectionType direction_type)
1424 GtkNotebookPrivate *priv = notebook->priv;
1425 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1426 GtkWidget *toplevel;
1428 if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1429 if (focus_tabs_in (notebook))
1431 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1432 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1435 /* At this point, we know we should be focusing out of the notebook entirely. We
1436 * do this by setting a flag, then propagating the focus motion to the notebook.
1438 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1439 if (!gtk_widget_is_toplevel (toplevel))
1442 g_object_ref (notebook);
1444 priv->focus_out = TRUE;
1445 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1446 priv->focus_out = FALSE;
1448 g_object_unref (notebook);
1452 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1454 GtkNotebookPrivate *priv = notebook->priv;
1457 if (position == tab)
1458 return g_list_position (priv->children, tab);
1460 /* check that we aren't inserting the tab in the
1461 * same relative position, taking packing into account */
1462 elem = (position) ? position->prev : g_list_last (priv->children);
1465 return g_list_position (priv->children, tab);
1467 /* now actually reorder the tab */
1468 if (priv->first_tab == tab)
1469 priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1472 priv->children = g_list_remove_link (priv->children, tab);
1475 elem = g_list_last (priv->children);
1478 elem = position->prev;
1479 position->prev = tab;
1485 priv->children = tab;
1488 tab->next = position;
1490 return g_list_position (priv->children, tab);
1494 gtk_notebook_reorder_tab (GtkNotebook *notebook,
1495 GtkDirectionType direction_type,
1496 gboolean move_to_last)
1498 GtkNotebookPrivate *priv = notebook->priv;
1499 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1500 GList *last, *child;
1503 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1506 if (!priv->cur_page ||
1507 !priv->cur_page->reorderable)
1510 if (effective_direction != GTK_DIR_LEFT &&
1511 effective_direction != GTK_DIR_RIGHT)
1516 child = priv->focus_tab;
1521 child = gtk_notebook_search_page (notebook, last,
1522 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1530 child = gtk_notebook_search_page (notebook, priv->focus_tab,
1531 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1534 if (!child || child->data == priv->cur_page)
1537 if (effective_direction == GTK_DIR_RIGHT)
1538 page_num = reorder_tab (notebook, child->next, priv->focus_tab);
1540 page_num = reorder_tab (notebook, child, priv->focus_tab);
1542 gtk_notebook_pages_allocate (notebook);
1544 g_signal_emit (notebook,
1545 notebook_signals[PAGE_REORDERED],
1547 ((GtkNotebookPage *) priv->focus_tab->data)->child,
1556 * Creates a new #GtkNotebook widget with no pages.
1558 * Return value: the newly created #GtkNotebook
1561 gtk_notebook_new (void)
1563 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1566 /* Private GObject Methods :
1568 * gtk_notebook_set_property
1569 * gtk_notebook_get_property
1572 gtk_notebook_set_property (GObject *object,
1574 const GValue *value,
1577 GtkNotebook *notebook;
1579 notebook = GTK_NOTEBOOK (object);
1583 case PROP_SHOW_TABS:
1584 gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1586 case PROP_SHOW_BORDER:
1587 gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1589 case PROP_SCROLLABLE:
1590 gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1592 case PROP_ENABLE_POPUP:
1593 if (g_value_get_boolean (value))
1594 gtk_notebook_popup_enable (notebook);
1596 gtk_notebook_popup_disable (notebook);
1599 gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1602 gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1604 case PROP_GROUP_NAME:
1605 gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1608 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1614 gtk_notebook_get_property (GObject *object,
1619 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1620 GtkNotebookPrivate *priv = notebook->priv;
1624 case PROP_SHOW_TABS:
1625 g_value_set_boolean (value, priv->show_tabs);
1627 case PROP_SHOW_BORDER:
1628 g_value_set_boolean (value, priv->show_border);
1630 case PROP_SCROLLABLE:
1631 g_value_set_boolean (value, priv->scrollable);
1633 case PROP_ENABLE_POPUP:
1634 g_value_set_boolean (value, priv->menu != NULL);
1637 g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1640 g_value_set_enum (value, priv->tab_pos);
1642 case PROP_GROUP_NAME:
1643 g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1646 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1651 /* Private GtkWidget Methods :
1653 * gtk_notebook_destroy
1655 * gtk_notebook_unmap
1656 * gtk_notebook_realize
1657 * gtk_notebook_size_request
1658 * gtk_notebook_size_allocate
1660 * gtk_notebook_scroll
1661 * gtk_notebook_button_press
1662 * gtk_notebook_button_release
1663 * gtk_notebook_popup_menu
1664 * gtk_notebook_leave_notify
1665 * gtk_notebook_motion_notify
1666 * gtk_notebook_focus_in
1667 * gtk_notebook_focus_out
1668 * gtk_notebook_style_updated
1669 * gtk_notebook_drag_begin
1670 * gtk_notebook_drag_end
1671 * gtk_notebook_drag_failed
1672 * gtk_notebook_drag_motion
1673 * gtk_notebook_drag_drop
1674 * gtk_notebook_drag_data_get
1675 * gtk_notebook_drag_data_received
1678 gtk_notebook_destroy (GtkWidget *widget)
1680 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1681 GtkNotebookPrivate *priv = notebook->priv;
1683 if (priv->action_widget[GTK_PACK_START])
1685 gtk_widget_unparent (priv->action_widget[GTK_PACK_START]);
1686 priv->action_widget[GTK_PACK_START] = NULL;
1689 if (priv->action_widget[GTK_PACK_END])
1691 gtk_widget_unparent (priv->action_widget[GTK_PACK_END]);
1692 priv->action_widget[GTK_PACK_END] = NULL;
1696 gtk_notebook_popup_disable (notebook);
1698 if (priv->source_targets)
1700 gtk_target_list_unref (priv->source_targets);
1701 priv->source_targets = NULL;
1704 if (priv->switch_tab_timer)
1706 g_source_remove (priv->switch_tab_timer);
1707 priv->switch_tab_timer = 0;
1710 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->destroy (widget);
1714 gtk_notebook_get_event_window_position (GtkNotebook *notebook,
1715 GdkRectangle *rectangle)
1717 GtkNotebookPrivate *priv = notebook->priv;
1718 GtkAllocation allocation, action_allocation;
1719 GtkWidget *widget = GTK_WIDGET (notebook);
1720 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1721 GtkNotebookPage *visible_page = NULL;
1723 gint tab_pos = get_effective_tab_pos (notebook);
1727 for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1729 GtkNotebookPage *page = tmp_list->data;
1730 if (gtk_widget_get_visible (page->child))
1732 visible_page = page;
1737 if (priv->show_tabs && visible_page)
1741 gtk_widget_get_allocation (widget, &allocation);
1743 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1744 rectangle->x = allocation.x + border_width;
1745 rectangle->y = allocation.y + border_width;
1750 case GTK_POS_BOTTOM:
1751 rectangle->width = allocation.width - 2 * border_width;
1752 rectangle->height = visible_page->requisition.height;
1753 if (tab_pos == GTK_POS_BOTTOM)
1754 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1756 for (i = 0; i < N_ACTION_WIDGETS; i++)
1758 if (priv->action_widget[i] &&
1759 gtk_widget_get_visible (priv->action_widget[i]))
1761 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1763 rectangle->width -= action_allocation.width;
1764 if ((!is_rtl && i == ACTION_WIDGET_START) ||
1765 (is_rtl && i == ACTION_WIDGET_END))
1766 rectangle->x += action_allocation.width;
1772 rectangle->width = visible_page->requisition.width;
1773 rectangle->height = allocation.height - 2 * border_width;
1774 if (tab_pos == GTK_POS_RIGHT)
1775 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1777 for (i = 0; i < N_ACTION_WIDGETS; i++)
1779 if (priv->action_widget[i] &&
1780 gtk_widget_get_visible (priv->action_widget[i]))
1782 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1784 rectangle->height -= action_allocation.height;
1786 if (i == ACTION_WIDGET_START)
1787 rectangle->y += action_allocation.height;
1800 rectangle->x = rectangle->y = 0;
1801 rectangle->width = rectangle->height = 10;
1809 gtk_notebook_map (GtkWidget *widget)
1811 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1812 GtkNotebookPrivate *priv = notebook->priv;
1813 GtkNotebookPage *page;
1817 gtk_widget_set_mapped (widget, TRUE);
1819 if (priv->cur_page &&
1820 gtk_widget_get_visible (priv->cur_page->child) &&
1821 !gtk_widget_get_mapped (priv->cur_page->child))
1822 gtk_widget_map (priv->cur_page->child);
1824 for (i = 0; i < N_ACTION_WIDGETS; i++)
1826 if (priv->action_widget[i] &&
1827 gtk_widget_get_visible (priv->action_widget[i]) &&
1828 gtk_widget_get_child_visible (priv->action_widget[i]) &&
1829 !gtk_widget_get_mapped (priv->action_widget[i]))
1830 gtk_widget_map (priv->action_widget[i]);
1833 if (priv->scrollable)
1834 gtk_notebook_pages_allocate (notebook);
1837 children = priv->children;
1841 page = children->data;
1842 children = children->next;
1844 if (page->tab_label &&
1845 gtk_widget_get_visible (page->tab_label) &&
1846 !gtk_widget_get_mapped (page->tab_label))
1847 gtk_widget_map (page->tab_label);
1851 if (gtk_notebook_get_event_window_position (notebook, NULL))
1852 gdk_window_show_unraised (priv->event_window);
1856 gtk_notebook_unmap (GtkWidget *widget)
1858 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1859 GtkNotebookPrivate *priv = notebook->priv;
1861 stop_scrolling (notebook);
1863 gtk_widget_set_mapped (widget, FALSE);
1865 gdk_window_hide (priv->event_window);
1867 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1871 gtk_notebook_realize (GtkWidget *widget)
1873 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1874 GtkNotebookPrivate *priv = notebook->priv;
1876 GdkWindowAttr attributes;
1877 gint attributes_mask;
1878 GdkRectangle event_window_pos;
1880 gtk_widget_set_realized (widget, TRUE);
1882 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1884 window = gtk_widget_get_parent_window (widget);
1885 gtk_widget_set_window (widget, window);
1886 g_object_ref (window);
1888 attributes.window_type = GDK_WINDOW_CHILD;
1889 attributes.x = event_window_pos.x;
1890 attributes.y = event_window_pos.y;
1891 attributes.width = event_window_pos.width;
1892 attributes.height = event_window_pos.height;
1893 attributes.wclass = GDK_INPUT_ONLY;
1894 attributes.event_mask = gtk_widget_get_events (widget);
1895 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1896 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1897 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1898 attributes_mask = GDK_WA_X | GDK_WA_Y;
1900 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1901 &attributes, attributes_mask);
1902 gdk_window_set_user_data (priv->event_window, notebook);
1906 gtk_notebook_unrealize (GtkWidget *widget)
1908 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1909 GtkNotebookPrivate *priv = notebook->priv;
1911 gdk_window_set_user_data (priv->event_window, NULL);
1912 gdk_window_destroy (priv->event_window);
1913 priv->event_window = NULL;
1915 if (priv->drag_window)
1917 gdk_window_set_user_data (priv->drag_window, NULL);
1918 gdk_window_destroy (priv->drag_window);
1919 priv->drag_window = NULL;
1922 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1925 static GtkRegionFlags
1926 _gtk_notebook_get_tab_flags (GtkNotebook *notebook,
1927 GtkNotebookPage *page)
1929 GtkNotebookPrivate *priv = notebook->priv;
1930 gint i = 0, page_num = -1;
1931 GtkRegionFlags flags = 0;
1932 gboolean is_last = FALSE;
1935 for (pages = priv->children; pages; pages = pages->next)
1937 GtkNotebookPage *p = pages->data;
1939 if (!p->tab_label || !gtk_widget_get_visible (p->tab_label))
1944 /* No need to keep counting tabs after it */
1948 is_last = pages->next == NULL;
1956 if ((page_num) % 2 == 0)
1957 flags |= GTK_REGION_EVEN;
1959 flags |= GTK_REGION_ODD;
1962 flags |= GTK_REGION_FIRST;
1965 flags |= GTK_REGION_LAST;
1970 static GtkStateFlags
1971 notebook_tab_prepare_style_context (GtkNotebook *notebook,
1972 GtkNotebookPage *page,
1973 GtkStyleContext *context,
1976 gint tab_pos = get_effective_tab_pos (notebook);
1977 GtkRegionFlags flags = 0;
1978 GtkStateFlags state = gtk_style_context_get_state (context);
1981 page == notebook->priv->cur_page)
1982 state |= GTK_STATE_FLAG_ACTIVE;
1984 gtk_style_context_set_state (context, state);
1986 if (use_flags && (page != NULL))
1987 flags = _gtk_notebook_get_tab_flags (notebook, page);
1989 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB, flags);
1994 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
1996 case GTK_POS_BOTTOM:
1997 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
2000 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
2003 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
2013 gtk_notebook_get_preferred_tabs_size (GtkNotebook *notebook,
2014 GtkRequisition *requisition)
2016 GtkNotebookPrivate *priv;
2019 gint tab_height = 0;
2023 gint action_width = 0;
2024 gint action_height = 0;
2025 guint vis_pages = 0;
2027 GtkNotebookPage *page;
2028 GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
2029 GtkRequisition child_requisition;
2030 GtkStyleContext *context;
2037 gint scroll_arrow_hlength;
2038 gint scroll_arrow_vlength;
2040 priv = notebook->priv;
2041 widget = GTK_WIDGET (notebook);
2042 context = gtk_widget_get_style_context (widget);
2043 gtk_widget_style_get (widget,
2044 "focus-line-width", &focus_width,
2045 "focus-padding", &focus_pad,
2046 "initial-gap", &initial_gap,
2047 "tab-overlap", &tab_overlap,
2048 "tab-curvature", &tab_curvature,
2049 "arrow-spacing", &arrow_spacing,
2050 "scroll-arrow-hlength", &scroll_arrow_hlength,
2051 "scroll-arrow-vlength", &scroll_arrow_vlength,
2054 for (children = priv->children; children;
2055 children = children->next)
2057 page = children->data;
2059 if (gtk_widget_get_visible (page->child))
2061 GtkBorder tab_padding;
2062 GtkStateFlags state;
2066 if (!gtk_widget_get_visible (page->tab_label))
2067 gtk_widget_show (page->tab_label);
2069 gtk_widget_get_preferred_size (page->tab_label,
2070 &child_requisition, NULL);
2072 /* Get border/padding for tab */
2073 gtk_style_context_save (context);
2074 state = notebook_tab_prepare_style_context (notebook, page, context, TRUE);
2075 gtk_style_context_get_padding (context, state, &tab_padding);
2076 gtk_style_context_restore (context);
2078 page->requisition.width = child_requisition.width +
2079 tab_padding.left + tab_padding.right + 2 * (focus_width + focus_pad);
2081 page->requisition.height = child_requisition.height +
2082 tab_padding.top + tab_padding.bottom + 2 * (focus_width + focus_pad);
2084 switch (priv->tab_pos)
2087 case GTK_POS_BOTTOM:
2088 tab_height = MAX (tab_height, page->requisition.height);
2089 tab_max = MAX (tab_max, page->requisition.width);
2093 tab_width = MAX (tab_width, page->requisition.width);
2094 tab_max = MAX (tab_max, page->requisition.height);
2098 else if (gtk_widget_get_visible (page->tab_label))
2099 gtk_widget_hide (page->tab_label);
2102 children = priv->children;
2106 for (i = 0; i < N_ACTION_WIDGETS; i++)
2108 if (priv->action_widget[i])
2110 gtk_widget_get_preferred_size (priv->action_widget[i],
2111 &action_widget_requisition[i], NULL);
2115 switch (priv->tab_pos)
2118 case GTK_POS_BOTTOM:
2119 if (tab_height == 0)
2122 if (priv->scrollable)
2123 tab_height = MAX (tab_height, scroll_arrow_hlength);
2125 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
2126 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
2128 padding = 2 * tab_curvature - tab_overlap;
2132 page = children->data;
2133 children = children->next;
2135 if (!gtk_widget_get_visible (page->child))
2138 page->requisition.width += padding;
2140 tab_width += page->requisition.width;
2141 page->requisition.height = tab_height;
2144 if (priv->scrollable)
2145 tab_width = MIN (tab_width,
2146 tab_max + 2 * (scroll_arrow_hlength + arrow_spacing));
2148 action_width += action_widget_requisition[ACTION_WIDGET_START].width;
2149 action_width += action_widget_requisition[ACTION_WIDGET_END].width;
2150 requisition->width = tab_width + tab_overlap + action_width + initial_gap;
2152 requisition->height = tab_height;
2159 if (priv->scrollable)
2160 tab_width = MAX (tab_width, arrow_spacing + 2 * scroll_arrow_vlength);
2162 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2163 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2165 padding = 2 * tab_curvature - tab_overlap;
2170 page = children->data;
2171 children = children->next;
2173 if (!gtk_widget_get_visible (page->child))
2176 page->requisition.width = tab_width;
2178 page->requisition.height += padding;
2180 tab_height += page->requisition.height;
2183 if (priv->scrollable)
2184 tab_height = MIN (tab_height,
2185 tab_max + (2 * scroll_arrow_vlength + arrow_spacing));
2186 action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2187 action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2189 requisition->height = tab_height + tab_overlap + action_height + initial_gap;
2191 requisition->height = MAX (requisition->height, tab_max + tab_overlap);
2193 requisition->width = tab_width;
2196 g_assert_not_reached ();
2197 requisition->width = 0;
2198 requisition->height = 0;
2203 requisition->width = 0;
2204 requisition->height = 0;
2209 get_preferred_size_for_size (GtkWidget *widget,
2210 GtkOrientation orientation,
2215 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2217 gtk_widget_get_preferred_width (widget, minimum, natural);
2219 gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
2222 gtk_widget_get_preferred_height (widget, minimum, natural);
2224 gtk_widget_get_preferred_height_for_width (widget, size, minimum, natural);
2228 get_padding_and_border (GtkNotebook *notebook,
2231 GtkStyleContext *context;
2233 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
2234 gtk_style_context_get_padding (context, 0, border);
2236 if (notebook->priv->show_border || notebook->priv->show_tabs)
2240 gtk_style_context_get_border (context, 0, &tmp);
2241 border->top += tmp.top;
2242 border->right += tmp.right;
2243 border->bottom += tmp.bottom;
2244 border->left += tmp.left;
2249 gtk_notebook_size_request (GtkWidget *widget,
2250 GtkOrientation orientation,
2255 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2256 GtkNotebookPrivate *priv = notebook->priv;
2257 GtkNotebookPage *page;
2259 gint child_minimum, child_natural;
2260 gboolean switch_page = FALSE;
2267 for (children = priv->children, vis_pages = 0; children;
2268 children = children->next)
2271 page = children->data;
2273 if (gtk_widget_get_visible (page->child))
2276 get_preferred_size_for_size (page->child,
2282 *minimum = MAX (*minimum, child_minimum);
2283 *natural = MAX (*natural, child_natural);
2285 if (priv->menu && page->menu_label)
2287 parent = gtk_widget_get_parent (page->menu_label);
2288 if (parent && !gtk_widget_get_visible (parent))
2289 gtk_widget_show (parent);
2294 if (page == priv->cur_page)
2297 if (priv->menu && page->menu_label)
2299 parent = gtk_widget_get_parent (page->menu_label);
2300 if (parent && gtk_widget_get_visible (parent))
2301 gtk_widget_hide (parent);
2306 if (priv->show_border || priv->show_tabs)
2308 GtkBorder notebook_padding;
2310 get_padding_and_border (notebook, ¬ebook_padding);
2312 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2314 *minimum += notebook_padding.left + notebook_padding.right;
2315 *natural += notebook_padding.left + notebook_padding.right;
2319 *minimum += notebook_padding.top + notebook_padding.bottom;
2320 *natural += notebook_padding.top + notebook_padding.bottom;
2323 if (priv->show_tabs)
2325 GtkRequisition tabs_requisition = { 0, 0 };
2327 gtk_notebook_get_preferred_tabs_size (notebook, &tabs_requisition);
2328 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2330 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
2332 *minimum = MAX (*minimum, tabs_requisition.width);
2333 *natural = MAX (*minimum, *natural);
2337 *minimum += tabs_requisition.width;
2338 *natural += tabs_requisition.width;
2343 if (priv->tab_pos == GTK_POS_LEFT || priv->tab_pos == GTK_POS_RIGHT)
2345 *minimum = MAX (*minimum, tabs_requisition.height);
2346 *natural = MAX (*minimum, *natural);
2350 *minimum += tabs_requisition.height;
2351 *natural += tabs_requisition.height;
2357 for (children = priv->children; children;
2358 children = children->next)
2360 page = children->data;
2362 if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2363 gtk_widget_hide (page->tab_label);
2368 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2370 *minimum += border_width * 2;
2371 *natural += border_width * 2;
2377 for (children = priv->children; children;
2378 children = children->next)
2380 page = children->data;
2381 if (gtk_widget_get_visible (page->child))
2383 gtk_notebook_switch_page (notebook, page);
2388 else if (gtk_widget_get_visible (widget))
2390 *minimum = border_width * 2;
2393 if (vis_pages && !priv->cur_page)
2395 children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2398 priv->first_tab = children;
2399 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2405 gtk_notebook_get_preferred_width_for_height (GtkWidget *widget,
2410 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
2414 gtk_notebook_get_preferred_height_for_width (GtkWidget *widget,
2419 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
2423 gtk_notebook_get_preferred_width (GtkWidget *widget,
2427 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural);
2431 gtk_notebook_get_preferred_height (GtkWidget *widget,
2435 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural);
2439 gtk_notebook_size_allocate (GtkWidget *widget,
2440 GtkAllocation *allocation)
2442 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2443 GtkNotebookPrivate *priv = notebook->priv;
2444 gint tab_pos = get_effective_tab_pos (notebook);
2448 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2450 gtk_widget_set_allocation (widget, allocation);
2452 if (gtk_widget_get_realized (widget))
2454 GdkRectangle position;
2456 if (gtk_notebook_get_event_window_position (notebook, &position))
2458 gdk_window_move_resize (priv->event_window,
2459 position.x, position.y,
2460 position.width, position.height);
2461 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2462 gdk_window_show_unraised (priv->event_window);
2465 gdk_window_hide (priv->event_window);
2470 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2471 GtkNotebookPage *page;
2472 GtkAllocation child_allocation;
2476 child_allocation.x = allocation->x + border_width;
2477 child_allocation.y = allocation->y + border_width;
2478 child_allocation.width = MAX (1, allocation->width - border_width * 2);
2479 child_allocation.height = MAX (1, allocation->height - border_width * 2);
2481 if (priv->show_tabs || priv->show_border)
2485 get_padding_and_border (notebook, &padding);
2487 child_allocation.x += padding.left;
2488 child_allocation.y += padding.top;
2489 child_allocation.width = MAX (1, child_allocation.width - padding.left - padding.right);
2490 child_allocation.height = MAX (1, child_allocation.height - padding.top - padding.bottom);
2492 if (priv->show_tabs && priv->children && priv->cur_page)
2497 child_allocation.y += priv->cur_page->requisition.height;
2498 case GTK_POS_BOTTOM:
2499 child_allocation.height =
2500 MAX (1, child_allocation.height -
2501 priv->cur_page->requisition.height);
2504 child_allocation.x += priv->cur_page->requisition.width;
2506 child_allocation.width =
2507 MAX (1, child_allocation.width -
2508 priv->cur_page->requisition.width);
2512 for (i = 0; i < N_ACTION_WIDGETS; i++)
2514 GtkAllocation widget_allocation;
2515 GtkRequisition requisition;
2517 if (!priv->action_widget[i])
2520 widget_allocation.x = allocation->x + border_width;
2521 widget_allocation.y = allocation->y + border_width;
2522 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2524 gtk_widget_get_preferred_size (priv->action_widget[i],
2525 &requisition, NULL);
2529 case GTK_POS_BOTTOM:
2530 widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2533 widget_allocation.width = requisition.width;
2534 widget_allocation.height = priv->cur_page->requisition.height - padding.top;
2536 if ((i == ACTION_WIDGET_START && is_rtl) ||
2537 (i == ACTION_WIDGET_END && !is_rtl))
2538 widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2539 if (tab_pos == GTK_POS_TOP) /* no fall through */
2540 widget_allocation.y += 2 * focus_width;
2543 widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2546 widget_allocation.height = requisition.height;
2547 widget_allocation.width = priv->cur_page->requisition.width - padding.left;
2549 if (i == ACTION_WIDGET_END)
2550 widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2551 if (tab_pos == GTK_POS_LEFT) /* no fall through */
2552 widget_allocation.x += 2 * focus_width;
2556 gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2561 children = priv->children;
2564 page = children->data;
2565 children = children->next;
2567 if (gtk_widget_get_visible (page->child))
2568 gtk_widget_size_allocate (page->child, &child_allocation);
2571 gtk_notebook_pages_allocate (notebook);
2576 gtk_notebook_draw (GtkWidget *widget,
2579 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2580 GtkNotebookPrivate *priv = notebook->priv;
2581 GtkAllocation allocation;
2585 gtk_widget_get_allocation (widget, &allocation);
2587 window = gtk_widget_get_window (widget);
2588 if (gtk_cairo_should_draw_window (cr, window))
2592 cairo_translate (cr, -allocation.x, -allocation.y);
2593 gtk_notebook_paint (widget, cr);
2597 if (priv->show_tabs)
2599 GtkNotebookPage *page;
2602 for (pages = priv->children; pages; pages = pages->next)
2604 page = GTK_NOTEBOOK_PAGE (pages);
2606 if (gtk_widget_get_parent (page->tab_label) == widget)
2607 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2608 page->tab_label, cr);
2612 if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2613 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2614 priv->cur_page->child,
2616 if (priv->show_tabs)
2618 for (i = 0; i < N_ACTION_WIDGETS; i++)
2620 if (priv->action_widget[i])
2621 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2622 priv->action_widget[i], cr);
2627 if (priv->operation == DRAG_OPERATION_REORDER &&
2628 gtk_cairo_should_draw_window (cr, priv->drag_window))
2631 gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2633 gtk_notebook_draw_tab (notebook,
2639 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2640 priv->cur_page->tab_label, cr);
2647 gtk_notebook_show_arrows (GtkNotebook *notebook)
2649 GtkNotebookPrivate *priv = notebook->priv;
2650 gboolean show_arrow = FALSE;
2653 if (!priv->scrollable)
2656 children = priv->children;
2659 GtkNotebookPage *page = children->data;
2661 if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2664 children = children->next;
2671 gtk_notebook_get_arrow_rect (GtkNotebook *notebook,
2672 GdkRectangle *rectangle,
2673 GtkNotebookArrow arrow)
2675 GtkNotebookPrivate *priv = notebook->priv;
2676 GdkRectangle event_window_pos;
2677 gboolean before = ARROW_IS_BEFORE (arrow);
2678 gboolean left = ARROW_IS_LEFT (arrow);
2680 if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2682 gint scroll_arrow_hlength;
2683 gint scroll_arrow_vlength;
2685 gtk_widget_style_get (GTK_WIDGET (notebook),
2686 "scroll-arrow-hlength", &scroll_arrow_hlength,
2687 "scroll-arrow-vlength", &scroll_arrow_vlength,
2690 switch (priv->tab_pos)
2694 rectangle->width = scroll_arrow_vlength;
2695 rectangle->height = scroll_arrow_vlength;
2697 if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2698 (!before && (priv->has_after_previous != priv->has_after_next)))
2699 rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2701 rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2703 rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2704 rectangle->y = event_window_pos.y;
2706 rectangle->y += event_window_pos.height - rectangle->height;
2710 case GTK_POS_BOTTOM:
2711 rectangle->width = scroll_arrow_hlength;
2712 rectangle->height = scroll_arrow_hlength;
2716 if (left || !priv->has_before_previous)
2717 rectangle->x = event_window_pos.x;
2719 rectangle->x = event_window_pos.x + rectangle->width;
2723 if (!left || !priv->has_after_next)
2724 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2726 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2728 rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2734 static GtkNotebookArrow
2735 gtk_notebook_get_arrow (GtkNotebook *notebook,
2739 GtkNotebookPrivate *priv = notebook->priv;
2740 GdkRectangle arrow_rect;
2741 GdkRectangle event_window_pos;
2744 GtkNotebookArrow arrow[4];
2746 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2747 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2748 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2749 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2751 if (gtk_notebook_show_arrows (notebook))
2753 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2754 for (i = 0; i < 4; i++)
2756 if (arrow[i] == ARROW_NONE)
2759 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2761 x0 = x - arrow_rect.x;
2762 y0 = y - arrow_rect.y;
2764 if (y0 >= 0 && y0 < arrow_rect.height &&
2765 x0 >= 0 && x0 < arrow_rect.width)
2774 gtk_notebook_do_arrow (GtkNotebook *notebook,
2775 GtkNotebookArrow arrow)
2777 GtkNotebookPrivate *priv = notebook->priv;
2778 GtkWidget *widget = GTK_WIDGET (notebook);
2779 gboolean is_rtl, left;
2781 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2782 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2783 (!ARROW_IS_LEFT (arrow) && is_rtl);
2785 if (!priv->focus_tab ||
2786 gtk_notebook_search_page (notebook, priv->focus_tab,
2787 left ? STEP_PREV : STEP_NEXT,
2790 gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2791 gtk_widget_grab_focus (widget);
2796 gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2797 GtkNotebookArrow arrow,
2800 GtkNotebookPrivate *priv = notebook->priv;
2801 GtkWidget *widget = GTK_WIDGET (notebook);
2802 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2803 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2804 (!ARROW_IS_LEFT (arrow) && is_rtl);
2806 if (!gtk_widget_has_focus (widget))
2807 gtk_widget_grab_focus (widget);
2809 priv->button = button;
2810 priv->click_child = arrow;
2812 if (button == GDK_BUTTON_PRIMARY)
2814 gtk_notebook_do_arrow (notebook, arrow);
2815 gtk_notebook_set_scroll_timer (notebook);
2817 else if (button == GDK_BUTTON_MIDDLE)
2818 gtk_notebook_page_select (notebook, TRUE);
2819 else if (button == GDK_BUTTON_SECONDARY)
2820 gtk_notebook_switch_focus_tab (notebook,
2821 gtk_notebook_search_page (notebook,
2823 left ? STEP_NEXT : STEP_PREV,
2825 gtk_notebook_redraw_arrows (notebook);
2831 get_widget_coordinates (GtkWidget *widget,
2836 GdkWindow *window = ((GdkEventAny *)event)->window;
2839 if (!gdk_event_get_coords (event, &tx, &ty))
2842 while (window && window != gtk_widget_get_window (widget))
2844 gint window_x, window_y;
2846 gdk_window_get_position (window, &window_x, &window_y);
2850 window = gdk_window_get_parent (window);
2865 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2867 GtkNotebookPrivate *priv = notebook->priv;
2868 GtkNotebookPage *page;
2871 children = priv->children;
2874 page = children->data;
2876 if (gtk_widget_get_visible (page->child) &&
2877 page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2878 (x >= page->allocation.x) &&
2879 (y >= page->allocation.y) &&
2880 (x <= (page->allocation.x + page->allocation.width)) &&
2881 (y <= (page->allocation.y + page->allocation.height)))
2884 children = children->next;
2891 gtk_notebook_button_press (GtkWidget *widget,
2892 GdkEventButton *event)
2894 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2895 GtkNotebookPrivate *priv = notebook->priv;
2896 GtkNotebookPage *page;
2898 GtkNotebookArrow arrow;
2901 if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2905 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2908 arrow = gtk_notebook_get_arrow (notebook, x, y);
2910 return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2912 if (priv->menu && gdk_event_triggers_context_menu ((GdkEvent *) event))
2914 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2915 NULL, NULL, 3, event->time);
2919 if (event->button != GDK_BUTTON_PRIMARY)
2922 priv->button = event->button;
2924 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2926 gboolean page_changed, was_focus;
2929 page_changed = page != priv->cur_page;
2930 was_focus = gtk_widget_is_focus (widget);
2932 gtk_notebook_switch_focus_tab (notebook, tab);
2933 gtk_widget_grab_focus (widget);
2935 if (page_changed && !was_focus)
2936 gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2938 /* save press to possibly begin a drag */
2939 if (page->reorderable || page->detachable)
2941 priv->during_detach = FALSE;
2942 priv->during_reorder = FALSE;
2943 priv->pressed_button = event->button;
2948 priv->drag_begin_x = priv->mouse_x;
2949 priv->drag_begin_y = priv->mouse_y;
2950 priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2951 priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2959 popup_position_func (GtkMenu *menu,
2965 GtkNotebook *notebook = data;
2966 GtkNotebookPrivate *priv = notebook->priv;
2967 GtkAllocation allocation;
2969 GtkRequisition requisition;
2971 if (priv->focus_tab)
2973 GtkNotebookPage *page;
2975 page = priv->focus_tab->data;
2976 w = page->tab_label;
2980 w = GTK_WIDGET (notebook);
2983 gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2985 gtk_widget_get_allocation (w, &allocation);
2986 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2987 &requisition, NULL);
2989 if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
2990 *x += allocation.x + allocation.width - requisition.width;
2994 *y += allocation.y + allocation.height;
3000 gtk_notebook_popup_menu (GtkWidget *widget)
3002 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3003 GtkNotebookPrivate *priv = notebook->priv;
3007 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
3008 popup_position_func, notebook,
3009 0, gtk_get_current_event_time ());
3010 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
3018 stop_scrolling (GtkNotebook *notebook)
3020 GtkNotebookPrivate *priv = notebook->priv;
3024 g_source_remove (priv->timer);
3026 priv->need_timer = FALSE;
3028 priv->click_child = 0;
3030 gtk_notebook_redraw_arrows (notebook);
3034 get_drop_position (GtkNotebook *notebook)
3036 GtkNotebookPrivate *priv = notebook->priv;
3037 GList *children, *last_child;
3038 GtkNotebookPage *page;
3045 is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
3046 children = priv->children;
3051 page = children->data;
3053 if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
3054 gtk_widget_get_visible (page->child) &&
3056 gtk_widget_get_mapped (page->tab_label))
3058 switch (priv->tab_pos)
3061 case GTK_POS_BOTTOM:
3064 if (PAGE_MIDDLE_X (page) > x)
3069 if (PAGE_MIDDLE_X (page) < x)
3076 if (PAGE_MIDDLE_Y (page) > y)
3082 last_child = children->next;
3085 children = children->next;
3092 show_drag_window (GtkNotebook *notebook,
3093 GtkNotebookPrivate *priv,
3094 GtkNotebookPage *page,
3097 GtkWidget *widget = GTK_WIDGET (notebook);
3099 if (!priv->drag_window)
3101 GdkWindowAttr attributes;
3102 guint attributes_mask;
3103 GdkRGBA transparent = {0, 0, 0, 0};
3105 attributes.x = page->allocation.x;
3106 attributes.y = page->allocation.y;
3107 attributes.width = page->allocation.width;
3108 attributes.height = page->allocation.height;
3109 attributes.window_type = GDK_WINDOW_CHILD;
3110 attributes.wclass = GDK_INPUT_OUTPUT;
3111 attributes.visual = gtk_widget_get_visual (widget);
3112 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3113 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3115 priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
3118 gdk_window_set_user_data (priv->drag_window, widget);
3119 gdk_window_set_background_rgba (priv->drag_window, &transparent);
3122 g_object_ref (page->tab_label);
3123 gtk_widget_unparent (page->tab_label);
3124 gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
3125 gtk_widget_set_parent (page->tab_label, widget);
3126 g_object_unref (page->tab_label);
3128 gdk_window_show (priv->drag_window);
3130 /* the grab will dissapear when the window is hidden */
3131 gdk_device_grab (device, priv->drag_window,
3132 GDK_OWNERSHIP_WINDOW, FALSE,
3133 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3134 NULL, GDK_CURRENT_TIME);
3137 /* This function undoes the reparenting that happens both when drag_window
3138 * is shown for reordering and when the DnD icon is shown for detaching
3141 hide_drag_window (GtkNotebook *notebook,
3142 GtkNotebookPrivate *priv,
3143 GtkNotebookPage *page)
3145 GtkWidget *widget = GTK_WIDGET (notebook);
3146 GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
3148 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
3149 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
3151 g_object_ref (page->tab_label);
3153 if (GTK_IS_WINDOW (parent)) /* parent widget is the drag window */
3154 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
3156 gtk_widget_unparent (page->tab_label);
3158 gtk_widget_set_parent (page->tab_label, widget);
3159 g_object_unref (page->tab_label);
3162 if (priv->drag_window &&
3163 gdk_window_is_visible (priv->drag_window))
3164 gdk_window_hide (priv->drag_window);
3168 gtk_notebook_stop_reorder (GtkNotebook *notebook)
3170 GtkNotebookPrivate *priv = notebook->priv;
3171 GtkNotebookPage *page;
3173 if (priv->operation == DRAG_OPERATION_DETACH)
3174 page = priv->detached_tab;
3176 page = priv->cur_page;
3178 if (!page || !page->tab_label)
3181 priv->pressed_button = -1;
3183 if (page->reorderable || page->detachable)
3185 if (priv->during_reorder)
3187 gint old_page_num, page_num, i;
3190 element = get_drop_position (notebook);
3191 old_page_num = g_list_position (priv->children, priv->focus_tab);
3192 page_num = reorder_tab (notebook, element, priv->focus_tab);
3193 gtk_notebook_child_reordered (notebook, page);
3195 if (priv->has_scrolled || old_page_num != page_num)
3197 for (element = priv->children, i = 0; element; element = element->next, i++)
3199 if (MIN (old_page_num, page_num) <= i && i <= MAX (old_page_num, page_num))
3200 gtk_widget_child_notify (((GtkNotebookPage *) element->data)->child, "position");
3202 g_signal_emit (notebook,
3203 notebook_signals[PAGE_REORDERED], 0,
3204 page->child, page_num);
3207 priv->has_scrolled = FALSE;
3208 priv->during_reorder = FALSE;
3211 hide_drag_window (notebook, priv, page);
3213 priv->operation = DRAG_OPERATION_NONE;
3214 gtk_notebook_pages_allocate (notebook);
3216 if (priv->dnd_timer)
3218 g_source_remove (priv->dnd_timer);
3219 priv->dnd_timer = 0;
3225 gtk_notebook_button_release (GtkWidget *widget,
3226 GdkEventButton *event)
3228 GtkNotebook *notebook;
3229 GtkNotebookPrivate *priv;
3230 GtkNotebookPage *page;
3232 if (event->type != GDK_BUTTON_RELEASE)
3235 notebook = GTK_NOTEBOOK (widget);
3236 priv = notebook->priv;
3238 page = priv->cur_page;
3240 if (!priv->during_detach &&
3241 page->reorderable &&
3242 event->button == priv->pressed_button)
3243 gtk_notebook_stop_reorder (notebook);
3245 if (event->button == priv->button)
3247 stop_scrolling (notebook);
3255 gtk_notebook_leave_notify (GtkWidget *widget,
3256 GdkEventCrossing *event)
3258 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3259 GtkNotebookPrivate *priv = notebook->priv;
3262 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
3268 gtk_notebook_redraw_arrows (notebook);
3274 static GtkNotebookPointerPosition
3275 get_pointer_position (GtkNotebook *notebook)
3277 GtkNotebookPrivate *priv = notebook->priv;
3278 GtkWidget *widget = GTK_WIDGET (notebook);
3279 gint wx, wy, width, height;
3282 if (!priv->scrollable)
3283 return POINTER_BETWEEN;
3285 gdk_window_get_position (priv->event_window, &wx, &wy);
3286 width = gdk_window_get_width (priv->event_window);
3287 height = gdk_window_get_height (priv->event_window);
3289 if (priv->tab_pos == GTK_POS_TOP ||
3290 priv->tab_pos == GTK_POS_BOTTOM)
3294 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3295 x = priv->mouse_x - wx;
3297 if (x > width - SCROLL_THRESHOLD)
3298 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
3299 else if (x < SCROLL_THRESHOLD)
3300 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
3302 return POINTER_BETWEEN;
3308 y = priv->mouse_y - wy;
3309 if (y > height - SCROLL_THRESHOLD)
3310 return POINTER_AFTER;
3311 else if (y < SCROLL_THRESHOLD)
3312 return POINTER_BEFORE;
3314 return POINTER_BETWEEN;
3319 scroll_notebook_timer (gpointer data)
3321 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3322 GtkNotebookPrivate *priv = notebook->priv;
3323 GtkNotebookPointerPosition pointer_position;
3324 GList *element, *first_tab;
3326 pointer_position = get_pointer_position (notebook);
3328 element = get_drop_position (notebook);
3329 reorder_tab (notebook, element, priv->focus_tab);
3330 first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3331 (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3335 priv->first_tab = first_tab;
3336 gtk_notebook_pages_allocate (notebook);
3338 gdk_window_move_resize (priv->drag_window,
3339 priv->drag_window_x,
3340 priv->drag_window_y,
3341 priv->cur_page->allocation.width,
3342 priv->cur_page->allocation.height);
3343 gdk_window_raise (priv->drag_window);
3350 check_threshold (GtkNotebook *notebook,
3354 GtkNotebookPrivate *priv = notebook->priv;
3356 GdkRectangle rectangle = { 0, }; /* shut up gcc */
3357 GtkSettings *settings;
3359 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3360 g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3362 /* we want a large threshold */
3363 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3365 gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3366 rectangle.width = gdk_window_get_width (priv->event_window);
3367 rectangle.height = gdk_window_get_height (priv->event_window);
3369 rectangle.x -= dnd_threshold;
3370 rectangle.width += 2 * dnd_threshold;
3371 rectangle.y -= dnd_threshold;
3372 rectangle.height += 2 * dnd_threshold;
3374 return (current_x < rectangle.x ||
3375 current_x > rectangle.x + rectangle.width ||
3376 current_y < rectangle.y ||
3377 current_y > rectangle.y + rectangle.height);
3381 gtk_notebook_motion_notify (GtkWidget *widget,
3382 GdkEventMotion *event)
3384 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3385 GtkNotebookPrivate *priv = notebook->priv;
3386 GtkNotebookPage *page;
3387 GtkNotebookArrow arrow;
3388 GtkNotebookPointerPosition pointer_position;
3389 GtkSettings *settings;
3393 page = priv->cur_page;
3398 if (!(event->state & GDK_BUTTON1_MASK) &&
3399 priv->pressed_button != -1)
3401 gtk_notebook_stop_reorder (notebook);
3402 stop_scrolling (notebook);
3405 if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3408 priv->timestamp = event->time;
3410 /* While animating the move, event->x is relative to the flying tab
3411 * (priv->drag_window has a pointer grab), but we need coordinates relative to
3412 * the notebook widget.
3414 gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3415 priv->mouse_x = event->x_root - x_win;
3416 priv->mouse_y = event->y_root - y_win;
3418 arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3419 if (arrow != priv->in_child)
3421 priv->in_child = arrow;
3422 gtk_notebook_redraw_arrows (notebook);
3425 if (priv->pressed_button == -1)
3428 if (page->detachable &&
3429 check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3431 priv->detached_tab = priv->cur_page;
3432 priv->during_detach = TRUE;
3434 gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3435 priv->pressed_button, (GdkEvent*) event);
3439 if (page->reorderable &&
3440 (priv->during_reorder ||
3441 gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3443 priv->during_reorder = TRUE;
3444 pointer_position = get_pointer_position (notebook);
3446 if (event->window == priv->drag_window &&
3447 pointer_position != POINTER_BETWEEN &&
3448 gtk_notebook_show_arrows (notebook))
3451 if (!priv->dnd_timer)
3453 priv->has_scrolled = TRUE;
3454 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3455 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3457 priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3458 scroll_notebook_timer,
3459 (gpointer) notebook);
3464 if (priv->dnd_timer)
3466 g_source_remove (priv->dnd_timer);
3467 priv->dnd_timer = 0;
3471 if (event->window == priv->drag_window ||
3472 priv->operation != DRAG_OPERATION_REORDER)
3474 /* the drag operation is beginning, create the window */
3475 if (priv->operation != DRAG_OPERATION_REORDER)
3477 priv->operation = DRAG_OPERATION_REORDER;
3478 show_drag_window (notebook, priv, page, event->device);
3481 gtk_notebook_pages_allocate (notebook);
3482 gdk_window_move_resize (priv->drag_window,
3483 priv->drag_window_x,
3484 priv->drag_window_y,
3485 page->allocation.width,
3486 page->allocation.height);
3488 gtk_notebook_redraw_tabs_junction (notebook);
3496 gtk_notebook_grab_notify (GtkWidget *widget,
3497 gboolean was_grabbed)
3499 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3503 gtk_notebook_stop_reorder (notebook);
3504 stop_scrolling (notebook);
3509 gtk_notebook_state_flags_changed (GtkWidget *widget,
3510 GtkStateFlags previous_state)
3512 if (!gtk_widget_is_sensitive (widget))
3513 stop_scrolling (GTK_NOTEBOOK (widget));
3517 gtk_notebook_focus_in (GtkWidget *widget,
3518 GdkEventFocus *event)
3520 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3526 gtk_notebook_focus_out (GtkWidget *widget,
3527 GdkEventFocus *event)
3529 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3535 gtk_notebook_style_updated (GtkWidget *widget)
3537 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3538 GtkNotebookPrivate *priv = notebook->priv;
3540 gboolean has_before_previous;
3541 gboolean has_before_next;
3542 gboolean has_after_previous;
3543 gboolean has_after_next;
3545 gtk_widget_style_get (widget,
3546 "has-backward-stepper", &has_before_previous,
3547 "has-secondary-forward-stepper", &has_before_next,
3548 "has-secondary-backward-stepper", &has_after_previous,
3549 "has-forward-stepper", &has_after_next,
3552 priv->has_before_previous = has_before_previous;
3553 priv->has_before_next = has_before_next;
3554 priv->has_after_previous = has_after_previous;
3555 priv->has_after_next = has_after_next;
3557 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_updated (widget);
3561 on_drag_icon_draw (GtkWidget *widget,
3565 GtkWidget *notebook, *child;
3566 GtkRequisition requisition;
3567 GtkStyleContext *context;
3570 notebook = GTK_WIDGET (data);
3571 child = gtk_bin_get_child (GTK_BIN (widget));
3572 context = gtk_widget_get_style_context (widget);
3574 gtk_style_context_save (context);
3575 notebook_tab_prepare_style_context (GTK_NOTEBOOK (notebook), NULL, context, FALSE);
3577 gtk_widget_get_preferred_size (widget,
3578 &requisition, NULL);
3579 gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3581 gtk_render_extension (context, cr, 0, 0,
3582 requisition.width, requisition.height,
3586 gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3588 gtk_style_context_restore (context);
3594 gtk_notebook_drag_begin (GtkWidget *widget,
3595 GdkDragContext *context)
3597 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3598 GtkNotebookPrivate *priv = notebook->priv;
3599 GtkWidget *tab_label;
3601 if (priv->dnd_timer)
3603 g_source_remove (priv->dnd_timer);
3604 priv->dnd_timer = 0;
3607 priv->operation = DRAG_OPERATION_DETACH;
3608 gtk_notebook_pages_allocate (notebook);
3610 tab_label = priv->detached_tab->tab_label;
3612 hide_drag_window (notebook, priv, priv->cur_page);
3613 g_object_ref (tab_label);
3614 gtk_widget_unparent (tab_label);
3616 priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3617 gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3618 gtk_widget_get_screen (widget));
3619 gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3620 gtk_widget_set_size_request (priv->dnd_window,
3621 priv->detached_tab->allocation.width,
3622 priv->detached_tab->allocation.height);
3623 g_object_unref (tab_label);
3625 g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3626 G_CALLBACK (on_drag_icon_draw), notebook);
3628 gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3632 gtk_notebook_drag_end (GtkWidget *widget,
3633 GdkDragContext *context)
3635 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3636 GtkNotebookPrivate *priv = notebook->priv;
3638 gtk_notebook_stop_reorder (notebook);
3640 if (priv->detached_tab)
3641 gtk_notebook_switch_page (notebook, priv->detached_tab);
3643 _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3644 gtk_widget_destroy (priv->dnd_window);
3645 priv->dnd_window = NULL;
3647 priv->operation = DRAG_OPERATION_NONE;
3650 static GtkNotebook *
3651 gtk_notebook_create_window (GtkNotebook *notebook,
3660 gtk_notebook_drag_failed (GtkWidget *widget,
3661 GdkDragContext *context,
3662 GtkDragResult result)
3664 if (result == GTK_DRAG_RESULT_NO_TARGET)
3666 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3667 GtkNotebookPrivate *priv = notebook->priv;
3668 GtkNotebook *dest_notebook = NULL;
3671 gdk_device_get_position (gdk_drag_context_get_device (context),
3674 g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3675 priv->detached_tab->child, x, y, &dest_notebook);
3678 do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3687 gtk_notebook_switch_tab_timeout (gpointer data)
3689 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3690 GtkNotebookPrivate *priv = notebook->priv;
3694 priv->switch_tab_timer = 0;
3698 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
3700 /* FIXME: hack, we don't want the
3701 * focus to move fom the source widget
3703 priv->child_has_focus = FALSE;
3704 gtk_notebook_switch_focus_tab (notebook, tab);
3711 gtk_notebook_drag_motion (GtkWidget *widget,
3712 GdkDragContext *context,
3717 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3718 GtkNotebookPrivate *priv = notebook->priv;
3719 GtkAllocation allocation;
3720 GdkRectangle position;
3721 GtkSettings *settings;
3722 GtkNotebookArrow arrow;
3724 GdkAtom target, tab_target;
3726 gtk_widget_get_allocation (widget, &allocation);
3728 arrow = gtk_notebook_get_arrow (notebook,
3733 priv->click_child = arrow;
3734 gtk_notebook_set_scroll_timer (notebook);
3735 gdk_drag_status (context, 0, time);
3739 stop_scrolling (notebook);
3740 target = gtk_drag_dest_find_target (widget, context, NULL);
3741 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3743 if (target == tab_target)
3745 GQuark group, source_group;
3746 GtkNotebook *source;
3747 GtkWidget *source_child;
3749 source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3750 source_child = source->priv->cur_page->child;
3752 group = notebook->priv->group;
3753 source_group = source->priv->group;
3755 if (group != 0 && group == source_group &&
3756 !(widget == source_child ||
3757 gtk_widget_is_ancestor (widget, source_child)))
3759 gdk_drag_status (context, GDK_ACTION_MOVE, time);
3764 /* it's a tab, but doesn't share
3765 * ID with this notebook */
3766 gdk_drag_status (context, 0, time);
3773 if (gtk_notebook_get_event_window_position (notebook, &position) &&
3774 x >= position.x && x <= position.x + position.width &&
3775 y >= position.y && y <= position.y + position.height)
3780 if (!priv->switch_tab_timer)
3782 settings = gtk_widget_get_settings (widget);
3784 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3785 priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3786 gtk_notebook_switch_tab_timeout,
3792 if (priv->switch_tab_timer)
3794 g_source_remove (priv->switch_tab_timer);
3795 priv->switch_tab_timer = 0;
3799 return (target == tab_target) ? TRUE : FALSE;
3803 gtk_notebook_drag_leave (GtkWidget *widget,
3804 GdkDragContext *context,
3807 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3808 GtkNotebookPrivate *priv = notebook->priv;
3810 if (priv->switch_tab_timer)
3812 g_source_remove (priv->switch_tab_timer);
3813 priv->switch_tab_timer = 0;
3816 stop_scrolling (GTK_NOTEBOOK (widget));
3820 gtk_notebook_drag_drop (GtkWidget *widget,
3821 GdkDragContext *context,
3826 GdkAtom target, tab_target;
3828 target = gtk_drag_dest_find_target (widget, context, NULL);
3829 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3831 if (target == tab_target)
3833 gtk_drag_get_data (widget, context, target, time);
3841 do_detach_tab (GtkNotebook *from,
3847 GtkNotebookPrivate *to_priv = to->priv;
3848 GtkAllocation to_allocation;
3849 GtkWidget *tab_label, *menu_label;
3850 gboolean tab_expand, tab_fill, reorderable, detachable;
3854 menu_label = gtk_notebook_get_menu_label (from, child);
3857 g_object_ref (menu_label);
3859 tab_label = gtk_notebook_get_tab_label (from, child);
3862 g_object_ref (tab_label);
3864 g_object_ref (child);
3866 gtk_container_child_get (GTK_CONTAINER (from),
3868 "tab-expand", &tab_expand,
3869 "tab-fill", &tab_fill,
3870 "reorderable", &reorderable,
3871 "detachable", &detachable,
3874 gtk_container_remove (GTK_CONTAINER (from), child);
3876 gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3877 to_priv->mouse_x = x + to_allocation.x;
3878 to_priv->mouse_y = y + to_allocation.y;
3880 element = get_drop_position (to);
3881 page_num = g_list_position (to_priv->children, element);
3882 gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3884 gtk_container_child_set (GTK_CONTAINER (to), child,
3885 "tab-expand", tab_expand,
3886 "tab-fill", tab_fill,
3887 "reorderable", reorderable,
3888 "detachable", detachable,
3891 g_object_unref (child);
3894 g_object_unref (tab_label);
3897 g_object_unref (menu_label);
3899 gtk_notebook_set_current_page (to, page_num);
3903 gtk_notebook_drag_data_get (GtkWidget *widget,
3904 GdkDragContext *context,
3905 GtkSelectionData *data,
3911 target = gtk_selection_data_get_target (data);
3912 if (target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3914 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3915 GtkNotebookPrivate *priv = notebook->priv;
3917 gtk_selection_data_set (data,
3920 (void*) &priv->detached_tab->child,
3926 gtk_notebook_drag_data_received (GtkWidget *widget,
3927 GdkDragContext *context,
3930 GtkSelectionData *data,
3934 GtkNotebook *notebook;
3935 GtkWidget *source_widget;
3938 notebook = GTK_NOTEBOOK (widget);
3939 source_widget = gtk_drag_get_source_widget (context);
3941 if (source_widget &&
3942 gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3944 child = (void*) gtk_selection_data_get_data (data);
3946 do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3947 gtk_drag_finish (context, TRUE, FALSE, time);
3950 gtk_drag_finish (context, FALSE, FALSE, time);
3953 /* Private GtkContainer Methods :
3955 * gtk_notebook_set_child_arg
3956 * gtk_notebook_get_child_arg
3958 * gtk_notebook_remove
3959 * gtk_notebook_focus
3960 * gtk_notebook_set_focus_child
3961 * gtk_notebook_child_type
3962 * gtk_notebook_forall
3965 gtk_notebook_set_child_property (GtkContainer *container,
3968 const GValue *value,
3974 /* not finding child's page is valid for menus or labels */
3975 if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3978 switch (property_id)
3980 case CHILD_PROP_TAB_LABEL:
3981 /* a NULL pointer indicates a default_tab setting, otherwise
3982 * we need to set the associated label
3984 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
3985 g_value_get_string (value));
3987 case CHILD_PROP_MENU_LABEL:
3988 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
3989 g_value_get_string (value));
3991 case CHILD_PROP_POSITION:
3992 gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
3993 g_value_get_int (value));
3995 case CHILD_PROP_TAB_EXPAND:
3996 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3998 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3999 g_value_get_boolean (value),
4002 case CHILD_PROP_TAB_FILL:
4003 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4005 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
4007 g_value_get_boolean (value));
4009 case CHILD_PROP_REORDERABLE:
4010 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
4011 g_value_get_boolean (value));
4013 case CHILD_PROP_DETACHABLE:
4014 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
4015 g_value_get_boolean (value));
4018 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
4024 gtk_notebook_get_child_property (GtkContainer *container,
4030 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4031 GtkNotebookPrivate *priv = notebook->priv;
4037 /* not finding child's page is valid for menus or labels */
4038 list = gtk_notebook_find_child (notebook, child, NULL);
4041 /* nothing to set on labels or menus */
4042 g_param_value_set_default (pspec, value);
4046 switch (property_id)
4048 case CHILD_PROP_TAB_LABEL:
4049 label = gtk_notebook_get_tab_label (notebook, child);
4051 if (GTK_IS_LABEL (label))
4052 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4054 g_value_set_string (value, NULL);
4056 case CHILD_PROP_MENU_LABEL:
4057 label = gtk_notebook_get_menu_label (notebook, child);
4059 if (GTK_IS_LABEL (label))
4060 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4062 g_value_set_string (value, NULL);
4064 case CHILD_PROP_POSITION:
4065 g_value_set_int (value, g_list_position (priv->children, list));
4067 case CHILD_PROP_TAB_EXPAND:
4068 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4070 g_value_set_boolean (value, expand);
4072 case CHILD_PROP_TAB_FILL:
4073 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4075 g_value_set_boolean (value, fill);
4077 case CHILD_PROP_REORDERABLE:
4078 g_value_set_boolean (value,
4079 gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
4081 case CHILD_PROP_DETACHABLE:
4082 g_value_set_boolean (value,
4083 gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
4086 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
4092 gtk_notebook_add (GtkContainer *container,
4095 gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget,
4100 gtk_notebook_remove (GtkContainer *container,
4103 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4104 GtkNotebookPrivate *priv = notebook->priv;
4105 GtkNotebookPage *page;
4106 GList *children, *list;
4109 children = priv->children;
4112 page = children->data;
4114 if (page->child == widget)
4118 children = children->next;
4121 if (children == NULL)
4124 g_object_ref (widget);
4126 list = children->next;
4127 gtk_notebook_real_remove (notebook, children);
4131 gtk_widget_child_notify (((GtkNotebookPage *)list->data)->child, "position");
4135 g_signal_emit (notebook,
4136 notebook_signals[PAGE_REMOVED],
4141 g_object_unref (widget);
4145 focus_tabs_in (GtkNotebook *notebook)
4147 GtkNotebookPrivate *priv = notebook->priv;
4149 if (priv->show_tabs && priv->cur_page)
4151 gtk_widget_grab_focus (GTK_WIDGET (notebook));
4152 gtk_notebook_set_focus_child (GTK_CONTAINER (notebook), NULL);
4153 gtk_notebook_switch_focus_tab (notebook,
4154 g_list_find (priv->children,
4164 focus_tabs_move (GtkNotebook *notebook,
4165 GtkDirectionType direction,
4166 gint search_direction)
4168 GtkNotebookPrivate *priv = notebook->priv;
4171 new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
4172 search_direction, TRUE);
4175 gboolean wrap_around;
4177 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
4178 "gtk-keynav-wrap-around", &wrap_around,
4182 new_page = gtk_notebook_search_page (notebook, NULL,
4183 search_direction, TRUE);
4187 gtk_notebook_switch_focus_tab (notebook, new_page);
4189 gtk_widget_error_bell (GTK_WIDGET (notebook));
4195 focus_child_in (GtkNotebook *notebook,
4196 GtkDirectionType direction)
4198 GtkNotebookPrivate *priv = notebook->priv;
4201 return gtk_widget_child_focus (priv->cur_page->child, direction);
4207 focus_action_in (GtkNotebook *notebook,
4209 GtkDirectionType direction)
4211 GtkNotebookPrivate *priv = notebook->priv;
4213 if (priv->action_widget[action] &&
4214 gtk_widget_get_visible (priv->action_widget[action]))
4215 return gtk_widget_child_focus (priv->action_widget[action], direction);
4220 /* Focus in the notebook can either be on the pages, or on
4221 * the tabs or on the action_widgets.
4224 gtk_notebook_focus (GtkWidget *widget,
4225 GtkDirectionType direction)
4227 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
4228 GtkNotebookPrivate *priv = notebook->priv;
4229 GtkWidget *old_focus_child;
4230 GtkDirectionType effective_direction;
4234 gboolean widget_is_focus;
4235 GtkContainer *container;
4237 container = GTK_CONTAINER (widget);
4239 if (priv->tab_pos == GTK_POS_TOP ||
4240 priv->tab_pos == GTK_POS_LEFT)
4242 first_action = ACTION_WIDGET_START;
4243 last_action = ACTION_WIDGET_END;
4247 first_action = ACTION_WIDGET_END;
4248 last_action = ACTION_WIDGET_START;
4251 if (priv->focus_out)
4253 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
4257 widget_is_focus = gtk_widget_is_focus (widget);
4258 old_focus_child = gtk_container_get_focus_child (container);
4260 effective_direction = get_effective_direction (notebook, direction);
4262 if (old_focus_child) /* Focus on page child or action widget */
4264 if (gtk_widget_child_focus (old_focus_child, direction))
4267 if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
4269 switch (effective_direction)
4272 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4274 return focus_tabs_in (notebook);
4282 case GTK_DIR_TAB_FORWARD:
4283 if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
4284 focus_child_in (notebook, direction))
4286 return focus_tabs_in (notebook);
4287 case GTK_DIR_TAB_BACKWARD:
4290 g_assert_not_reached ();
4294 else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
4296 switch (effective_direction)
4299 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4303 return focus_tabs_in (notebook);
4309 case GTK_DIR_TAB_FORWARD:
4311 case GTK_DIR_TAB_BACKWARD:
4312 if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
4313 focus_child_in (notebook, direction))
4315 return focus_tabs_in (notebook);
4317 g_assert_not_reached ();
4323 switch (effective_direction)
4325 case GTK_DIR_TAB_BACKWARD:
4327 /* Focus onto the tabs */
4328 return focus_tabs_in (notebook);
4333 case GTK_DIR_TAB_FORWARD:
4334 return focus_action_in (notebook, last_action, direction);
4338 else if (widget_is_focus) /* Focus was on tabs */
4340 switch (effective_direction)
4342 case GTK_DIR_TAB_BACKWARD:
4343 return focus_action_in (notebook, first_action, direction);
4346 case GTK_DIR_TAB_FORWARD:
4347 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4349 return focus_action_in (notebook, last_action, direction);
4351 /* We use TAB_FORWARD rather than direction so that we focus a more
4352 * predictable widget for the user; users may be using arrow focusing
4353 * in this situation even if they don't usually use arrow focusing.
4355 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4357 return focus_tabs_move (notebook, direction, STEP_PREV);
4359 return focus_tabs_move (notebook, direction, STEP_NEXT);
4362 else /* Focus was not on widget */
4364 switch (effective_direction)
4366 case GTK_DIR_TAB_FORWARD:
4368 if (focus_action_in (notebook, first_action, direction))
4370 if (focus_tabs_in (notebook))
4372 if (focus_action_in (notebook, last_action, direction))
4374 if (focus_child_in (notebook, direction))
4377 case GTK_DIR_TAB_BACKWARD:
4378 if (focus_action_in (notebook, last_action, direction))
4380 if (focus_child_in (notebook, direction))
4382 if (focus_tabs_in (notebook))
4384 if (focus_action_in (notebook, first_action, direction))
4389 return focus_child_in (notebook, direction);
4393 g_assert_not_reached ();
4398 gtk_notebook_set_focus_child (GtkContainer *container,
4401 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4402 GtkNotebookPrivate *priv = notebook->priv;
4403 GtkWidget *page_child;
4404 GtkWidget *toplevel;
4406 /* If the old focus widget was within a page of the notebook,
4407 * (child may either be NULL or not in this case), record it
4408 * for future use if we switch to the page with a mnemonic.
4411 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4412 if (toplevel && gtk_widget_is_toplevel (toplevel))
4414 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4417 if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4419 GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4422 GtkNotebookPage *page = list->data;
4424 if (page->last_focus_child)
4425 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4427 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4428 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4434 page_child = gtk_widget_get_parent (page_child);
4440 g_return_if_fail (GTK_IS_WIDGET (child));
4442 priv->child_has_focus = TRUE;
4443 if (!priv->focus_tab)
4446 GtkNotebookPage *page;
4448 children = priv->children;
4451 page = children->data;
4452 if (page->child == child || page->tab_label == child)
4453 gtk_notebook_switch_focus_tab (notebook, children);
4454 children = children->next;
4459 priv->child_has_focus = FALSE;
4461 GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4465 gtk_notebook_forall (GtkContainer *container,
4466 gboolean include_internals,
4467 GtkCallback callback,
4468 gpointer callback_data)
4470 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4471 GtkNotebookPrivate *priv = notebook->priv;
4475 children = priv->children;
4478 GtkNotebookPage *page;
4480 page = children->data;
4481 children = children->next;
4482 (* callback) (page->child, callback_data);
4484 if (include_internals)
4486 if (page->tab_label)
4487 (* callback) (page->tab_label, callback_data);
4491 if (include_internals) {
4492 for (i = 0; i < N_ACTION_WIDGETS; i++)
4494 if (priv->action_widget[i])
4495 (* callback) (priv->action_widget[i], callback_data);
4500 static GtkWidgetPath *
4501 gtk_notebook_get_path_for_child (GtkContainer *container,
4504 GtkNotebookPrivate *priv;
4505 GtkNotebook *notebook;
4506 GtkNotebookPage *page;
4507 GtkWidgetPath *path;
4510 path = GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->get_path_for_child (container, widget);
4512 notebook = GTK_NOTEBOOK (container);
4513 priv = notebook->priv;
4515 for (c = priv->children; c; c = c->next)
4519 if (page->tab_label == widget)
4523 /* Widget is not a tab label */
4527 gtk_widget_path_iter_add_region (path,
4528 gtk_widget_path_length (path) - 2,
4529 GTK_STYLE_REGION_TAB,
4530 _gtk_notebook_get_tab_flags (notebook, page));
4536 gtk_notebook_child_type (GtkContainer *container)
4538 return GTK_TYPE_WIDGET;
4541 /* Private GtkNotebook Methods:
4543 * gtk_notebook_real_insert_page
4546 page_visible_cb (GtkWidget *page,
4550 GtkNotebook *notebook = GTK_NOTEBOOK (data);
4551 GtkNotebookPrivate *priv = notebook->priv;
4555 if (priv->cur_page &&
4556 priv->cur_page->child == page &&
4557 !gtk_widget_get_visible (page))
4559 list = g_list_find (priv->children, priv->cur_page);
4562 next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4564 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4568 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4573 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4575 GtkWidget *tab_label,
4576 GtkWidget *menu_label,
4579 GtkNotebookPrivate *priv = notebook->priv;
4580 GtkNotebookPage *page;
4584 gtk_widget_freeze_child_notify (child);
4586 page = g_slice_new0 (GtkNotebookPage);
4587 page->child = child;
4589 nchildren = g_list_length (priv->children);
4590 if ((position < 0) || (position > nchildren))
4591 position = nchildren;
4593 priv->children = g_list_insert (priv->children, page, position);
4597 page->default_tab = TRUE;
4599 page->tab_label = tab_label;
4600 page->menu_label = menu_label;
4601 page->expand = FALSE;
4605 page->default_menu = TRUE;
4607 g_object_ref_sink (page->menu_label);
4610 gtk_notebook_menu_item_create (notebook,
4611 g_list_find (priv->children, page));
4613 /* child visible will be turned on by switch_page below */
4614 if (priv->cur_page != page)
4615 gtk_widget_set_child_visible (child, FALSE);
4617 gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4619 gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4621 gtk_notebook_update_labels (notebook);
4623 if (!priv->first_tab)
4624 priv->first_tab = priv->children;
4628 if (priv->show_tabs && gtk_widget_get_visible (child))
4629 gtk_widget_show (tab_label);
4631 gtk_widget_hide (tab_label);
4633 page->mnemonic_activate_signal =
4634 g_signal_connect (tab_label,
4635 "mnemonic-activate",
4636 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4640 page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4641 G_CALLBACK (page_visible_cb), notebook);
4643 g_signal_emit (notebook,
4644 notebook_signals[PAGE_ADDED],
4649 if (!priv->cur_page)
4651 gtk_notebook_switch_page (notebook, page);
4652 /* focus_tab is set in the switch_page method */
4653 gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4656 gtk_notebook_update_tab_states (notebook);
4658 if (priv->scrollable)
4659 gtk_notebook_redraw_arrows (notebook);
4661 gtk_widget_child_notify (child, "tab-expand");
4662 gtk_widget_child_notify (child, "tab-fill");
4663 gtk_widget_child_notify (child, "tab-label");
4664 gtk_widget_child_notify (child, "menu-label");
4666 list = g_list_nth (priv->children, position);
4669 gtk_widget_child_notify (((GtkNotebookPage *)list->data)->child, "position");
4673 gtk_widget_thaw_child_notify (child);
4675 /* The page-added handler might have reordered the pages, re-get the position */
4676 return gtk_notebook_page_num (notebook, child);
4679 /* Private GtkNotebook Functions:
4681 * gtk_notebook_redraw_tabs
4682 * gtk_notebook_real_remove
4683 * gtk_notebook_update_labels
4684 * gtk_notebook_timer
4685 * gtk_notebook_set_scroll_timer
4686 * gtk_notebook_page_compare
4687 * gtk_notebook_search_page
4690 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4692 GtkNotebookPrivate *priv = notebook->priv;
4693 GtkAllocation allocation;
4695 GtkNotebookPage *page;
4696 GdkRectangle redraw_rect;
4698 gint tab_pos = get_effective_tab_pos (notebook);
4701 widget = GTK_WIDGET (notebook);
4702 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4704 if (!gtk_widget_get_mapped (widget) || !priv->cur_page)
4707 page = priv->cur_page;
4709 redraw_rect.x = border;
4710 redraw_rect.y = border;
4712 gtk_widget_get_allocation (widget, &allocation);
4714 get_padding_and_border (notebook, &padding);
4718 case GTK_POS_BOTTOM:
4719 redraw_rect.y = allocation.height - border -
4720 page->allocation.height - padding.bottom;
4723 redraw_rect.width = allocation.width - 2 * border;
4724 redraw_rect.height = page->allocation.height + padding.top;
4728 redraw_rect.x = allocation.width - border -
4729 page->allocation.width - padding.right;
4733 redraw_rect.width = page->allocation.width + padding.left;
4734 redraw_rect.height = allocation.height - 2 * border;
4739 redraw_rect.x += allocation.x;
4740 redraw_rect.y += allocation.y;
4742 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4743 &redraw_rect, TRUE);
4747 gtk_notebook_redraw_tabs_junction (GtkNotebook *notebook)
4749 GtkNotebookPrivate *priv = notebook->priv;
4750 GtkAllocation allocation;
4752 GtkNotebookPage *page;
4753 GdkRectangle redraw_rect;
4755 gint tab_pos = get_effective_tab_pos (notebook);
4758 widget = GTK_WIDGET (notebook);
4759 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4761 if (!gtk_widget_get_mapped (widget) || !priv->cur_page)
4764 page = priv->cur_page;
4766 redraw_rect.x = border;
4767 redraw_rect.y = border;
4769 gtk_widget_get_allocation (widget, &allocation);
4771 get_padding_and_border (notebook, &padding);
4776 case GTK_POS_BOTTOM:
4777 redraw_rect.width = allocation.width - 2 * border;
4778 if (tab_pos == GTK_POS_TOP)
4780 redraw_rect.y = border + page->allocation.y +
4781 page->allocation.height;
4782 redraw_rect.height = padding.top;
4786 redraw_rect.y = allocation.height - border -
4787 page->allocation.height - padding.bottom;
4788 redraw_rect.height = padding.bottom;
4793 redraw_rect.height = allocation.height - 2 * border;
4795 if (tab_pos == GTK_POS_LEFT)
4797 redraw_rect.x = border + page->allocation.x + page->allocation.width;
4798 redraw_rect.width = padding.left;
4802 redraw_rect.x = allocation.width - border -
4803 page->allocation.width - padding.right;
4804 redraw_rect.width = padding.right;
4809 redraw_rect.x += allocation.x;
4810 redraw_rect.y += allocation.y;
4812 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4813 &redraw_rect, TRUE);
4817 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4819 GtkNotebookPrivate *priv = notebook->priv;
4821 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4822 gtk_notebook_show_arrows (notebook))
4826 GtkNotebookArrow arrow[4];
4828 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4829 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4830 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4831 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4833 for (i = 0; i < 4; i++)
4835 if (arrow[i] == ARROW_NONE)
4838 gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4839 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4846 gtk_notebook_timer (GtkNotebook *notebook)
4848 GtkNotebookPrivate *priv = notebook->priv;
4849 gboolean retval = FALSE;
4853 gtk_notebook_do_arrow (notebook, priv->click_child);
4855 if (priv->need_timer)
4857 GtkSettings *settings;
4860 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4861 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4863 priv->need_timer = FALSE;
4864 priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4865 (GSourceFunc) gtk_notebook_timer,
4866 (gpointer) notebook);
4876 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4878 GtkNotebookPrivate *priv = notebook->priv;
4879 GtkWidget *widget = GTK_WIDGET (notebook);
4883 GtkSettings *settings = gtk_widget_get_settings (widget);
4886 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4888 priv->timer = gdk_threads_add_timeout (timeout,
4889 (GSourceFunc) gtk_notebook_timer,
4890 (gpointer) notebook);
4891 priv->need_timer = TRUE;
4896 gtk_notebook_page_compare (gconstpointer a,
4899 return (((GtkNotebookPage *) a)->child != b);
4903 gtk_notebook_find_child (GtkNotebook *notebook,
4905 const gchar *function)
4907 GtkNotebookPrivate *priv = notebook->priv;
4908 GList *list = g_list_find_custom (priv->children, child,
4909 gtk_notebook_page_compare);
4911 #ifndef G_DISABLE_CHECKS
4912 if (!list && function)
4913 g_warning ("%s: unable to find child %p in notebook %p",
4914 function, child, notebook);
4921 gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4922 GtkNotebookPage *page)
4924 if (page->tab_label)
4926 if (page->mnemonic_activate_signal)
4927 g_signal_handler_disconnect (page->tab_label,
4928 page->mnemonic_activate_signal);
4929 page->mnemonic_activate_signal = 0;
4931 gtk_widget_set_state_flags (page->tab_label, 0, TRUE);
4932 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (GTK_WIDGET (notebook)) ||
4933 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4937 /* we hit this condition during dnd of a detached tab */
4938 parent = gtk_widget_get_parent (page->tab_label);
4939 if (GTK_IS_WINDOW (parent))
4940 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
4942 gtk_widget_unparent (page->tab_label);
4946 gtk_widget_unparent (page->tab_label);
4949 page->tab_label = NULL;
4954 gtk_notebook_real_remove (GtkNotebook *notebook,
4957 GtkNotebookPrivate *priv = notebook->priv;
4958 GtkNotebookPage *page;
4960 gint need_resize = FALSE;
4961 GtkWidget *tab_label;
4962 gboolean destroying;
4964 destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4966 next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4968 next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4970 priv->children = g_list_remove_link (priv->children, list);
4972 if (priv->cur_page == list->data)
4974 priv->cur_page = NULL;
4975 if (next_list && !destroying)
4976 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4979 if (priv->detached_tab == list->data)
4980 priv->detached_tab = NULL;
4982 if (list == priv->first_tab)
4983 priv->first_tab = next_list;
4984 if (list == priv->focus_tab && !destroying)
4985 gtk_notebook_switch_focus_tab (notebook, next_list);
4989 g_signal_handler_disconnect (page->child, page->notify_visible_handler);
4991 if (gtk_widget_get_visible (page->child) &&
4992 gtk_widget_get_visible (GTK_WIDGET (notebook)))
4995 gtk_widget_unparent (page->child);
4997 tab_label = page->tab_label;
5000 g_object_ref (tab_label);
5001 gtk_notebook_remove_tab_label (notebook, page);
5003 gtk_widget_destroy (tab_label);
5004 g_object_unref (tab_label);
5009 GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
5011 gtk_notebook_menu_label_unparent (parent, NULL);
5012 gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
5014 gtk_widget_queue_resize (priv->menu);
5016 if (!page->default_menu)
5017 g_object_unref (page->menu_label);
5021 if (page->last_focus_child)
5023 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
5024 page->last_focus_child = NULL;
5027 g_slice_free (GtkNotebookPage, page);
5029 gtk_notebook_update_labels (notebook);
5031 gtk_widget_queue_resize (GTK_WIDGET (notebook));
5035 gtk_notebook_update_labels (GtkNotebook *notebook)
5037 GtkNotebookPrivate *priv = notebook->priv;
5038 GtkNotebookPage *page;
5043 if (!priv->show_tabs && !priv->menu)
5046 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
5048 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
5051 g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
5052 if (priv->show_tabs)
5054 if (page->default_tab)
5056 if (!page->tab_label)
5058 page->tab_label = gtk_label_new (string);
5059 gtk_widget_set_parent (page->tab_label,
5060 GTK_WIDGET (notebook));
5063 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
5066 if (gtk_widget_get_visible (page->child) &&
5067 !gtk_widget_get_visible (page->tab_label))
5068 gtk_widget_show (page->tab_label);
5069 else if (!gtk_widget_get_visible (page->child) &&
5070 gtk_widget_get_visible (page->tab_label))
5071 gtk_widget_hide (page->tab_label);
5073 if (priv->menu && page->default_menu)
5075 if (GTK_IS_LABEL (page->tab_label))
5076 gtk_label_set_text (GTK_LABEL (page->menu_label),
5077 gtk_label_get_label (GTK_LABEL (page->tab_label)));
5079 gtk_label_set_text (GTK_LABEL (page->menu_label), string);
5085 gtk_notebook_search_page (GtkNotebook *notebook,
5088 gboolean find_visible)
5090 GtkNotebookPrivate *priv = notebook->priv;
5091 GtkNotebookPage *page = NULL;
5092 GList *old_list = NULL;
5097 if (!page || direction == STEP_NEXT)
5105 list = priv->children;
5110 if (direction == STEP_NEXT &&
5112 (gtk_widget_get_visible (page->child) &&
5113 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
5128 if (direction == STEP_PREV &&
5130 (gtk_widget_get_visible (page->child) &&
5131 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
5139 /* Private GtkNotebook Drawing Functions:
5141 * gtk_notebook_paint
5142 * gtk_notebook_draw_tab
5143 * gtk_notebook_draw_arrow
5146 gtk_notebook_paint (GtkWidget *widget,
5149 GtkNotebook *notebook;
5150 GtkNotebookPrivate *priv;
5151 GtkNotebookPage *page;
5152 GtkAllocation allocation;
5157 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5158 gint gap_x = 0, gap_width = 0, step = STEP_PREV;
5161 GtkStyleContext *context;
5163 notebook = GTK_NOTEBOOK (widget);
5164 priv = notebook->priv;
5165 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5166 tab_pos = get_effective_tab_pos (notebook);
5167 context = gtk_widget_get_style_context (widget);
5170 if ((!priv->show_tabs && !priv->show_border) ||
5171 !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
5174 gtk_widget_get_allocation (widget, &allocation);
5176 x = allocation.x + border_width;
5177 y = allocation.y + border_width;
5178 width = allocation.width - border_width * 2;
5179 height = allocation.height - border_width * 2;
5181 if (priv->show_border && (!priv->show_tabs || !priv->children))
5183 gtk_render_background (context, cr,
5184 x, y, width, height);
5185 gtk_render_frame (context, cr,
5186 x, y, width, height);
5190 if (!priv->first_tab)
5191 priv->first_tab = priv->children;
5193 if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
5194 page = GTK_NOTEBOOK_PAGE (priv->first_tab);
5196 page = priv->cur_page;
5201 y += page->allocation.height;
5203 case GTK_POS_BOTTOM:
5204 height -= page->allocation.height;
5207 x += page->allocation.width;
5210 width -= page->allocation.width;
5214 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
5215 !gtk_widget_get_mapped (priv->cur_page->tab_label))
5225 case GTK_POS_BOTTOM:
5226 if (priv->operation == DRAG_OPERATION_REORDER)
5227 gap_x = priv->drag_window_x - allocation.x - border_width;
5229 gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
5231 gap_width = priv->cur_page->allocation.width;
5232 step = is_rtl ? STEP_PREV : STEP_NEXT;
5236 if (priv->operation == DRAG_OPERATION_REORDER)
5237 gap_x = priv->drag_window_y - border_width - allocation.y;
5239 gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
5241 gap_width = priv->cur_page->allocation.height;
5247 for (children = priv->children; children; children = children->next)
5249 page = children->data;
5251 if (!gtk_widget_get_visible (page->child))
5254 if (!gtk_widget_get_mapped (page->tab_label))
5257 /* No point in keeping searching */
5262 gtk_style_context_save (context);
5264 if (!showarrow || !priv->scrollable)
5266 GtkJunctionSides junction = 0;
5268 /* Apply junction sides, if no arrows are shown,
5269 * then make corners with connecting tabs square.
5274 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_TOPRIGHT : GTK_JUNCTION_CORNER_TOPLEFT;
5277 case GTK_POS_BOTTOM:
5278 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_BOTTOMRIGHT : GTK_JUNCTION_CORNER_BOTTOMLEFT;
5282 junction |= GTK_JUNCTION_CORNER_TOPLEFT;
5286 junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
5291 gtk_style_context_set_junction_sides (context, junction);
5294 gtk_render_background (context, cr,
5295 x, y, width, height);
5296 gtk_render_frame_gap (context, cr,
5297 x, y, width, height,
5298 tab_pos, gap_x, gap_x + gap_width);
5300 gtk_style_context_restore (context);
5302 children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
5306 page = children->data;
5308 if (page == priv->cur_page)
5311 children = gtk_notebook_search_page (notebook, children,
5314 if (!gtk_widget_get_visible (page->child) ||
5315 !gtk_widget_get_mapped (page->tab_label))
5318 gtk_notebook_draw_tab (notebook, page, cr, TRUE);
5321 if (children != NULL)
5323 GList *other_order = NULL;
5327 page = children->data;
5328 children = gtk_notebook_search_page (notebook, children,
5330 if (!gtk_widget_get_visible (page->child) ||
5331 !gtk_widget_get_mapped (page->tab_label))
5334 if (children != NULL)
5335 other_order = g_list_prepend (other_order, children->data);
5338 /* draw them with the opposite order */
5339 for (children = other_order; children; children = children->next)
5341 page = children->data;
5342 gtk_notebook_draw_tab (notebook, page, cr, TRUE);
5345 g_list_free (other_order);
5348 if (showarrow && priv->scrollable)
5350 if (priv->has_before_previous)
5351 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
5352 if (priv->has_before_next)
5353 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
5354 if (priv->has_after_previous)
5355 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
5356 if (priv->has_after_next)
5357 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
5360 if (priv->operation != DRAG_OPERATION_REORDER)
5361 gtk_notebook_draw_tab (notebook, priv->cur_page, cr, TRUE);
5365 gtk_notebook_draw_tab (GtkNotebook *notebook,
5366 GtkNotebookPage *page,
5370 GtkNotebookPrivate *priv;
5372 GtkStyleContext *context;
5374 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5375 !gtk_widget_get_mapped (page->tab_label) ||
5376 (page->allocation.width == 0) || (page->allocation.height == 0))
5379 widget = GTK_WIDGET (notebook);
5380 priv = notebook->priv;
5382 context = gtk_widget_get_style_context (widget);
5383 gtk_style_context_save (context);
5384 notebook_tab_prepare_style_context (notebook, page, context, use_flags);
5386 gtk_render_extension (context, cr,
5389 page->allocation.width,
5390 page->allocation.height,
5391 get_tab_gap_pos (notebook));
5393 if (gtk_widget_has_visible_focus (widget) &&
5394 priv->cur_page == page)
5396 gint focus_width, focus_pad;
5397 GtkAllocation allocation;
5399 gtk_widget_get_allocation (page->tab_label, &allocation);
5400 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
5401 gtk_widget_style_get (widget, "focus-padding", &focus_pad, NULL);
5403 gtk_render_focus (context, cr,
5404 allocation.x - focus_width - focus_pad,
5405 allocation.y - focus_width - focus_pad,
5406 allocation.width + 2 * (focus_width + focus_pad),
5407 allocation.height + 2 * (focus_width + focus_pad));
5410 gtk_style_context_restore (context);
5414 gtk_notebook_draw_arrow (GtkNotebook *notebook,
5416 GtkNotebookArrow nbarrow)
5418 GtkNotebookPrivate *priv = notebook->priv;
5419 GtkStyleContext *context;
5420 GtkStateFlags state = 0;
5422 GdkRectangle arrow_rect;
5423 gboolean is_rtl, left;
5424 gint scroll_arrow_hlength;
5425 gint scroll_arrow_vlength;
5429 widget = GTK_WIDGET (notebook);
5430 context = gtk_widget_get_style_context (widget);
5431 state = gtk_widget_get_state_flags (widget);
5433 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
5435 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5436 left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
5437 (!ARROW_IS_LEFT (nbarrow) && is_rtl);
5439 gtk_widget_style_get (widget,
5440 "scroll-arrow-hlength", &scroll_arrow_hlength,
5441 "scroll-arrow-vlength", &scroll_arrow_vlength,
5444 if (priv->focus_tab &&
5445 !gtk_notebook_search_page (notebook, priv->focus_tab,
5446 left ? STEP_PREV : STEP_NEXT, TRUE))
5448 state |= GTK_STATE_FLAG_INSENSITIVE;
5450 else if (priv->in_child == nbarrow)
5452 state |= GTK_STATE_FLAG_PRELIGHT;
5454 if (priv->click_child == nbarrow)
5455 state |= GTK_STATE_FLAG_ACTIVE;
5458 if (priv->tab_pos == GTK_POS_LEFT ||
5459 priv->tab_pos == GTK_POS_RIGHT)
5461 angle = (ARROW_IS_LEFT (nbarrow)) ? 0 : G_PI;
5462 arrow_size = scroll_arrow_vlength;
5466 angle = (ARROW_IS_LEFT (nbarrow)) ? 3 * (G_PI / 2) : G_PI / 2;
5467 arrow_size = scroll_arrow_hlength;
5470 gtk_style_context_save (context);
5471 gtk_style_context_set_state (context, state);
5473 gtk_render_arrow (context, cr, angle,
5474 arrow_rect.x, arrow_rect.y,
5477 gtk_style_context_restore (context);
5480 /* Private GtkNotebook Size Allocate Functions:
5482 * gtk_notebook_tab_space
5483 * gtk_notebook_calculate_shown_tabs
5484 * gtk_notebook_calculate_tabs_allocation
5485 * gtk_notebook_pages_allocate
5486 * gtk_notebook_page_allocate
5487 * gtk_notebook_calc_tabs
5490 gtk_notebook_tab_space (GtkNotebook *notebook,
5491 gboolean *show_arrows,
5496 GtkNotebookPrivate *priv = notebook->priv;
5497 GtkAllocation allocation, action_allocation;
5500 gint tab_pos = get_effective_tab_pos (notebook);
5503 gint scroll_arrow_hlength;
5504 gint scroll_arrow_vlength;
5511 widget = GTK_WIDGET (notebook);
5512 children = priv->children;
5513 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5515 gtk_widget_style_get (GTK_WIDGET (notebook),
5516 "arrow-spacing", &arrow_spacing,
5517 "scroll-arrow-hlength", &scroll_arrow_hlength,
5518 "scroll-arrow-vlength", &scroll_arrow_vlength,
5519 "initial-gap", &initial_gap,
5522 border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5523 get_padding_and_border (notebook, &padding);
5525 gtk_widget_get_allocation (widget, &allocation);
5530 case GTK_POS_BOTTOM:
5531 *min = allocation.x + border_width;
5532 *max = allocation.x + allocation.width - border_width;
5534 for (i = 0; i < N_ACTION_WIDGETS; i++)
5536 if (priv->action_widget[i])
5538 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5540 if ((i == ACTION_WIDGET_START && !is_rtl) ||
5541 (i == ACTION_WIDGET_END && is_rtl))
5542 *min += action_allocation.width + padding.left;
5544 *max -= action_allocation.width + padding.right;
5550 GtkNotebookPage *page;
5552 page = children->data;
5553 children = children->next;
5555 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5556 gtk_widget_get_visible (page->child))
5557 *tab_space += page->requisition.width;
5562 *min = allocation.y + border_width;
5563 *max = allocation.y + allocation.height - border_width;
5565 for (i = 0; i < N_ACTION_WIDGETS; i++)
5567 if (priv->action_widget[i])
5569 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5571 if (i == ACTION_WIDGET_START)
5572 *min += action_allocation.height + padding.top;
5574 *max -= action_allocation.height + padding.bottom;
5580 GtkNotebookPage *page;
5582 page = children->data;
5583 children = children->next;
5585 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5586 gtk_widget_get_visible (page->child))
5587 *tab_space += page->requisition.height;
5592 *min += initial_gap;
5593 *max -= (2 * initial_gap);
5595 if (!priv->scrollable)
5596 *show_arrows = FALSE;
5599 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5604 case GTK_POS_BOTTOM:
5605 if (*tab_space > *max - *min - tab_overlap)
5607 *show_arrows = TRUE;
5609 /* take arrows into account */
5610 *tab_space = *max - *min - tab_overlap;
5612 if (priv->has_after_previous)
5614 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5615 *max -= arrow_spacing + scroll_arrow_hlength;
5618 if (priv->has_after_next)
5620 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5621 *max -= arrow_spacing + scroll_arrow_hlength;
5624 if (priv->has_before_previous)
5626 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5627 *min += arrow_spacing + scroll_arrow_hlength;
5630 if (priv->has_before_next)
5632 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5633 *min += arrow_spacing + scroll_arrow_hlength;
5639 if (*tab_space > *max - *min - tab_overlap)
5641 *show_arrows = TRUE;
5643 /* take arrows into account */
5644 *tab_space = *max - *min - tab_overlap;
5646 if (priv->has_after_previous || priv->has_after_next)
5648 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5649 *max -= arrow_spacing + scroll_arrow_vlength;
5652 if (priv->has_before_previous || priv->has_before_next)
5654 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5655 *min += arrow_spacing + scroll_arrow_vlength;
5664 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5665 gboolean show_arrows,
5671 gint *remaining_space)
5673 GtkNotebookPrivate *priv = notebook->priv;
5676 GtkNotebookPage *page;
5679 widget = GTK_WIDGET (notebook);
5680 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5682 if (show_arrows) /* first_tab <- focus_tab */
5684 *remaining_space = tab_space;
5686 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5687 gtk_widget_get_visible (priv->cur_page->child))
5689 gtk_notebook_calc_tabs (notebook,
5692 remaining_space, STEP_NEXT);
5695 if (tab_space <= 0 || *remaining_space <= 0)
5698 priv->first_tab = priv->focus_tab;
5699 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5701 page = priv->first_tab->data;
5702 *remaining_space = tab_space - page->requisition.width;
5709 if (priv->first_tab && priv->first_tab != priv->focus_tab)
5711 /* Is first_tab really predecessor of focus_tab? */
5712 page = priv->first_tab->data;
5713 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5714 gtk_widget_get_visible (page->child))
5715 for (children = priv->focus_tab;
5716 children && children != priv->first_tab;
5717 children = gtk_notebook_search_page (notebook,
5725 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5726 priv->first_tab = priv->focus_tab;
5728 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5732 /* calculate shown tabs counting backwards from the focus tab */
5733 gtk_notebook_calc_tabs (notebook,
5734 gtk_notebook_search_page (notebook,
5742 if (*remaining_space < 0)
5745 gtk_notebook_search_page (notebook, priv->first_tab,
5747 if (!priv->first_tab)
5748 priv->first_tab = priv->focus_tab;
5750 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5753 else /* focus_tab -> end */
5755 if (!priv->first_tab)
5756 priv->first_tab = gtk_notebook_search_page (notebook,
5761 gtk_notebook_calc_tabs (notebook,
5762 gtk_notebook_search_page (notebook,
5770 if (*remaining_space <= 0)
5771 *last_child = children;
5772 else /* start <- first_tab */
5777 gtk_notebook_calc_tabs (notebook,
5778 gtk_notebook_search_page (notebook,
5786 if (*remaining_space == 0)
5787 priv->first_tab = children;
5789 priv->first_tab = gtk_notebook_search_page(notebook,
5796 if (*remaining_space < 0)
5798 /* calculate number of tabs */
5799 *remaining_space = - (*remaining_space);
5802 for (children = priv->first_tab;
5803 children && children != *last_child;
5804 children = gtk_notebook_search_page (notebook, children,
5809 *remaining_space = 0;
5812 /* unmap all non-visible tabs */
5813 for (children = gtk_notebook_search_page (notebook, NULL,
5815 children && children != priv->first_tab;
5816 children = gtk_notebook_search_page (notebook, children,
5819 page = children->data;
5821 if (page->tab_label &&
5822 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5823 gtk_widget_set_child_visible (page->tab_label, FALSE);
5826 for (children = *last_child; children;
5827 children = gtk_notebook_search_page (notebook, children,
5830 page = children->data;
5832 if (page->tab_label &&
5833 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5834 gtk_widget_set_child_visible (page->tab_label, FALSE);
5837 else /* !show_arrows */
5839 GtkOrientation tab_expand_orientation;
5843 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5844 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5846 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5847 *remaining_space = max - min - tab_overlap - tab_space;
5848 children = priv->children;
5849 priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5853 page = children->data;
5854 children = children->next;
5856 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5857 !gtk_widget_get_visible (page->child))
5863 (gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5870 get_allocate_at_bottom (GtkWidget *widget,
5871 gint search_direction)
5873 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5874 gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5879 case GTK_POS_BOTTOM:
5881 return (search_direction == STEP_PREV);
5883 return (search_direction == STEP_NEXT);
5888 return (search_direction == STEP_PREV);
5896 gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5901 gint *remaining_space,
5902 gint *expanded_tabs,
5906 GtkNotebookPrivate *priv = notebook->priv;
5907 GtkAllocation allocation;
5909 GtkContainer *container;
5910 GtkNotebookPage *page;
5911 GtkStyleContext *context;
5912 gboolean allocate_at_bottom;
5913 gint tab_overlap, tab_pos, tab_extra_space;
5914 gint left_x, right_x, top_y, bottom_y, anchor;
5916 gboolean gap_left, packing_changed;
5917 GtkAllocation child_allocation = { 0, };
5918 GtkOrientation tab_expand_orientation;
5920 widget = GTK_WIDGET (notebook);
5921 container = GTK_CONTAINER (notebook);
5922 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5923 tab_pos = get_effective_tab_pos (notebook);
5924 allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5927 gtk_widget_get_allocation (widget, &allocation);
5929 border_width = gtk_container_get_border_width (container);
5930 child_allocation.x = allocation.x + border_width;
5931 child_allocation.y = allocation.y + border_width;
5933 context = gtk_widget_get_style_context (widget);
5937 case GTK_POS_BOTTOM:
5938 child_allocation.y = allocation.y + allocation.height -
5939 priv->cur_page->requisition.height - border_width;
5942 child_allocation.x = (allocate_at_bottom) ? max : min;
5943 child_allocation.height = priv->cur_page->requisition.height;
5944 anchor = child_allocation.x;
5948 child_allocation.x = allocation.x + allocation.width -
5949 priv->cur_page->requisition.width - border_width;
5952 child_allocation.y = (allocate_at_bottom) ? max : min;
5953 child_allocation.width = priv->cur_page->requisition.width;
5954 anchor = child_allocation.y;
5958 left_x = CLAMP (priv->mouse_x - priv->drag_offset_x,
5959 min, max - priv->cur_page->allocation.width);
5960 top_y = CLAMP (priv->mouse_y - priv->drag_offset_y,
5961 min, max - priv->cur_page->allocation.height);
5962 right_x = left_x + priv->cur_page->allocation.width;
5963 bottom_y = top_y + priv->cur_page->allocation.height;
5964 gap_left = packing_changed = FALSE;
5966 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5967 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5969 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5971 while (*children && *children != last_child)
5973 page = (*children)->data;
5975 if (direction == STEP_NEXT)
5976 *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5979 *children = (*children)->next;
5983 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5986 tab_extra_space = 0;
5987 if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5989 tab_extra_space = *remaining_space / *expanded_tabs;
5990 *remaining_space -= tab_extra_space;
5997 case GTK_POS_BOTTOM:
5998 child_allocation.width = MAX (1, page->requisition.width + tab_overlap + tab_extra_space);
6000 /* make sure that the reordered tab doesn't go past the last position */
6001 if (priv->operation == DRAG_OPERATION_REORDER &&
6002 !gap_left && packing_changed)
6004 if (!allocate_at_bottom)
6006 if (left_x >= anchor)
6008 left_x = priv->drag_window_x = anchor;
6009 anchor += priv->cur_page->allocation.width - tab_overlap;
6014 if (right_x <= anchor)
6016 anchor -= priv->cur_page->allocation.width;
6017 left_x = priv->drag_window_x = anchor;
6018 anchor += tab_overlap;
6025 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
6027 priv->drag_window_x = left_x;
6028 priv->drag_window_y = child_allocation.y;
6032 if (allocate_at_bottom)
6033 anchor -= child_allocation.width;
6035 if (priv->operation == DRAG_OPERATION_REORDER)
6037 if (!allocate_at_bottom &&
6039 left_x <= anchor + child_allocation.width / 2)
6040 anchor += priv->cur_page->allocation.width - tab_overlap;
6041 else if (allocate_at_bottom &&
6042 right_x >= anchor + child_allocation.width / 2 &&
6043 right_x <= anchor + child_allocation.width)
6044 anchor -= priv->cur_page->allocation.width - tab_overlap;
6047 child_allocation.x = anchor;
6053 child_allocation.height = MAX (1, page->requisition.height + tab_overlap + tab_extra_space);
6055 /* make sure that the reordered tab doesn't go past the last position */
6056 if (priv->operation == DRAG_OPERATION_REORDER &&
6057 !gap_left && packing_changed)
6059 if (!allocate_at_bottom && top_y >= anchor)
6061 top_y = priv->drag_window_y = anchor;
6062 anchor += priv->cur_page->allocation.height - tab_overlap;
6068 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
6070 priv->drag_window_x = child_allocation.x;
6071 priv->drag_window_y = top_y;
6075 if (allocate_at_bottom)
6076 anchor -= child_allocation.height;
6078 if (priv->operation == DRAG_OPERATION_REORDER)
6080 if (!allocate_at_bottom &&
6082 top_y <= anchor + child_allocation.height / 2)
6083 anchor += priv->cur_page->allocation.height - tab_overlap;
6084 else if (allocate_at_bottom &&
6085 bottom_y >= anchor + child_allocation.height / 2 &&
6086 bottom_y <= anchor + child_allocation.height)
6087 anchor -= priv->cur_page->allocation.height - tab_overlap;
6090 child_allocation.y = anchor;
6096 page->allocation = child_allocation;
6098 if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
6099 (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
6101 /* needs to be allocated at 0,0
6102 * to be shown in the drag window */
6103 page->allocation.x = 0;
6104 page->allocation.y = 0;
6107 if (page != priv->cur_page)
6109 GtkBorder active_padding, normal_padding, padding;
6111 /* The active tab is by definition at least the same height as the inactive one.
6112 * The padding we're building is the offset between the two tab states,
6113 * so in case the style specifies normal_padding > active_padding we
6114 * remove the offset and draw them with the same height.
6115 * Note that the padding will still be applied to the tab content though,
6116 * see gtk_notebook_page_allocate().
6118 gtk_style_context_save (context);
6119 notebook_tab_prepare_style_context (notebook, page, context, TRUE);
6121 gtk_style_context_get_padding (context, GTK_STATE_FLAG_ACTIVE, &active_padding);
6122 gtk_style_context_get_padding (context, GTK_STATE_FLAG_NORMAL, &normal_padding);
6124 gtk_style_context_restore (context);
6126 padding.top = MAX (0, active_padding.top - normal_padding.top);
6127 padding.right = MAX (0, active_padding.right - normal_padding.right);
6128 padding.bottom = MAX (0, active_padding.bottom - normal_padding.bottom);
6129 padding.left = MAX (0, active_padding.left - normal_padding.left);
6134 page->allocation.y += padding.top + padding.bottom;
6135 page->allocation.height = MAX (1, page->allocation.height - padding.top - padding.bottom);
6137 case GTK_POS_BOTTOM:
6138 page->allocation.height = MAX (1, page->allocation.height - padding.top - padding.bottom);
6141 page->allocation.x += padding.left + padding.right;
6142 page->allocation.width = MAX (1, page->allocation.width - padding.left - padding.right);
6145 page->allocation.width = MAX (1, page->allocation.width - padding.left - padding.right);
6150 /* calculate whether to leave a gap based on reorder operation or not */
6154 case GTK_POS_BOTTOM:
6155 if (priv->operation != DRAG_OPERATION_REORDER ||
6156 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6158 if (priv->operation == DRAG_OPERATION_REORDER)
6160 if (!allocate_at_bottom &&
6161 left_x > anchor + child_allocation.width / 2 &&
6162 left_x <= anchor + child_allocation.width)
6163 anchor += priv->cur_page->allocation.width - tab_overlap;
6164 else if (allocate_at_bottom &&
6165 right_x >= anchor &&
6166 right_x <= anchor + child_allocation.width / 2)
6167 anchor -= priv->cur_page->allocation.width - tab_overlap;
6170 if (!allocate_at_bottom)
6171 anchor += child_allocation.width - tab_overlap;
6173 anchor += tab_overlap;
6179 if (priv->operation != DRAG_OPERATION_REORDER ||
6180 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6182 if (priv->operation == DRAG_OPERATION_REORDER)
6184 if (!allocate_at_bottom &&
6185 top_y >= anchor + child_allocation.height / 2 &&
6186 top_y <= anchor + child_allocation.height)
6187 anchor += priv->cur_page->allocation.height - tab_overlap;
6188 else if (allocate_at_bottom &&
6189 bottom_y >= anchor &&
6190 bottom_y <= anchor + child_allocation.height / 2)
6191 anchor -= priv->cur_page->allocation.height - tab_overlap;
6194 if (!allocate_at_bottom)
6195 anchor += child_allocation.height - tab_overlap;
6197 anchor += tab_overlap;
6203 /* set child visible */
6204 if (page->tab_label)
6205 gtk_widget_set_child_visible (page->tab_label, TRUE);
6208 /* Don't move the current tab past the last position during tabs reordering */
6210 priv->operation == DRAG_OPERATION_REORDER &&
6211 direction == STEP_NEXT)
6216 case GTK_POS_BOTTOM:
6217 if (allocate_at_bottom)
6218 anchor -= priv->cur_page->allocation.width;
6220 if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
6221 (allocate_at_bottom && priv->drag_window_x < anchor))
6222 priv->drag_window_x = anchor;
6226 if (allocate_at_bottom)
6227 anchor -= priv->cur_page->allocation.height;
6229 if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
6230 (allocate_at_bottom && priv->drag_window_y < anchor))
6231 priv->drag_window_y = anchor;
6238 gtk_notebook_pages_allocate (GtkNotebook *notebook)
6240 GtkNotebookPrivate *priv = notebook->priv;
6241 GList *children = NULL;
6242 GList *last_child = NULL;
6243 gboolean showarrow = FALSE;
6244 gint tab_space, min, max, remaining_space;
6246 gboolean tab_allocations_changed = FALSE;
6248 if (!priv->show_tabs || !priv->children || !priv->cur_page)
6251 min = max = tab_space = remaining_space = 0;
6254 gtk_notebook_tab_space (notebook, &showarrow,
6255 &min, &max, &tab_space);
6257 gtk_notebook_calculate_shown_tabs (notebook, showarrow,
6258 min, max, tab_space, &last_child,
6259 &expanded_tabs, &remaining_space);
6261 children = priv->first_tab;
6262 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6263 showarrow, STEP_NEXT,
6264 &remaining_space, &expanded_tabs, min, max);
6265 if (children && children != last_child)
6267 children = priv->children;
6268 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6269 showarrow, STEP_PREV,
6270 &remaining_space, &expanded_tabs, min, max);
6273 children = priv->children;
6277 if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
6278 tab_allocations_changed = TRUE;
6279 children = children->next;
6282 if (!priv->first_tab)
6283 priv->first_tab = priv->children;
6285 if (tab_allocations_changed)
6286 gtk_notebook_redraw_tabs (notebook);
6290 gtk_notebook_page_allocate (GtkNotebook *notebook,
6291 GtkNotebookPage *page)
6293 GtkWidget *widget = GTK_WIDGET (notebook);
6294 GtkNotebookPrivate *priv = notebook->priv;
6295 GtkAllocation child_allocation, label_allocation;
6296 GtkRequisition tab_requisition;
6297 GtkStyleContext *context;
6299 gint focus_width, focus_padding;
6300 gint tab_curvature, tab_overlap;
6301 gint tab_pos = get_effective_tab_pos (notebook);
6302 gboolean tab_allocation_changed;
6303 gboolean was_visible = page->tab_allocated_visible;
6304 GtkBorder tab_padding;
6305 GtkStateFlags state;
6307 if (!page->tab_label ||
6308 !gtk_widget_get_visible (page->tab_label) ||
6309 !gtk_widget_get_child_visible (page->tab_label))
6311 page->tab_allocated_visible = FALSE;
6315 context = gtk_widget_get_style_context (widget);
6317 gtk_style_context_save (context);
6318 state = notebook_tab_prepare_style_context (notebook, page, context, TRUE);
6320 gtk_style_context_get_padding (context, state, &tab_padding);
6322 gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
6323 gtk_widget_style_get (widget,
6324 "focus-line-width", &focus_width,
6325 "focus-padding", &focus_padding,
6326 "tab-curvature", &tab_curvature,
6327 "tab-overlap", &tab_overlap,
6332 case GTK_POS_BOTTOM:
6333 padding = tab_curvature + focus_width + focus_padding;
6336 child_allocation.x = tab_padding.left + padding;
6337 child_allocation.width = MAX (1, (page->allocation.width -
6338 tab_padding.left - tab_padding.right -
6340 child_allocation.x += page->allocation.x;
6342 /* if we're drawing an inactive page, trim the allocation width
6343 * for the children by the difference between tab-curvature
6345 * if we're after the active tab, we need to trim the x
6346 * coordinate of the allocation too, to position it after
6347 * the end of the overlap.
6349 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.left, tab_padding.right))
6351 if (gtk_notebook_page_num (notebook, page->child) >
6352 gtk_notebook_page_num (notebook, priv->cur_page->child))
6354 child_allocation.x += tab_overlap - tab_curvature - tab_padding.left;
6355 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.left;
6359 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.right;
6365 child_allocation.x = page->allocation.x +
6366 (page->allocation.width - tab_requisition.width) / 2;
6368 child_allocation.width = tab_requisition.width;
6371 child_allocation.y =
6372 page->allocation.y + tab_padding.top + focus_width + focus_padding;
6374 child_allocation.height = MAX (1, (page->allocation.height -
6375 tab_padding.top - tab_padding.bottom -
6376 2 * (focus_width + focus_padding)));
6380 padding = tab_curvature + focus_width + focus_padding;
6383 child_allocation.y = tab_padding.top + padding;
6384 child_allocation.height = MAX (1, (page->allocation.height -
6385 tab_padding.bottom - tab_padding.top -
6387 child_allocation.y += page->allocation.y;
6389 /* if we're drawing an inactive page, trim the allocation height
6390 * for the children by the difference between tab-curvature
6392 * if we're after the active tab, we need to trim the y
6393 * coordinate of the allocation too, to position it after
6394 * the end of the overlap.
6396 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.top, tab_padding.bottom))
6398 if (gtk_notebook_page_num (notebook, page->child) >
6399 gtk_notebook_page_num (notebook, priv->cur_page->child))
6401 child_allocation.y += tab_overlap - tab_curvature - tab_padding.top;
6402 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.top;
6406 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.bottom;
6412 child_allocation.y = page->allocation.y +
6413 (page->allocation.height - tab_requisition.height) / 2;
6415 child_allocation.height = tab_requisition.height;
6418 child_allocation.x =
6419 page->allocation.x + tab_padding.left + focus_width + focus_padding;
6421 child_allocation.width = MAX (1, (page->allocation.width -
6422 tab_padding.left - tab_padding.right -
6423 2 * (focus_width + focus_padding)));
6427 gtk_widget_get_allocation (page->tab_label, &label_allocation);
6428 tab_allocation_changed = (child_allocation.x != label_allocation.x ||
6429 child_allocation.y != label_allocation.y ||
6430 child_allocation.width != label_allocation.width ||
6431 child_allocation.height != label_allocation.height);
6433 gtk_widget_size_allocate (page->tab_label, &child_allocation);
6437 page->tab_allocated_visible = TRUE;
6438 tab_allocation_changed = TRUE;
6441 gtk_style_context_restore (context);
6443 return tab_allocation_changed;
6447 gtk_notebook_calc_tabs (GtkNotebook *notebook,
6453 GtkNotebookPage *page = NULL;
6455 GList *last_calculated_child = NULL;
6456 gint tab_pos = get_effective_tab_pos (notebook);
6466 case GTK_POS_BOTTOM:
6469 page = children->data;
6470 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6471 gtk_widget_get_visible (page->child))
6473 *tab_space -= page->requisition.width;
6474 if (*tab_space < 0 || children == *end)
6478 *tab_space = - (*tab_space +
6479 page->requisition.width);
6481 if (*tab_space == 0 && direction == STEP_PREV)
6482 children = last_calculated_child;
6489 last_calculated_child = children;
6491 if (direction == STEP_NEXT)
6492 children = children->next;
6494 children = children->prev;
6501 page = children->data;
6502 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6503 gtk_widget_get_visible (page->child))
6505 *tab_space -= page->requisition.height;
6506 if (*tab_space < 0 || children == *end)
6510 *tab_space = - (*tab_space + page->requisition.height);
6512 if (*tab_space == 0 && direction == STEP_PREV)
6513 children = last_calculated_child;
6520 last_calculated_child = children;
6522 if (direction == STEP_NEXT)
6523 children = children->next;
6525 children = children->prev;
6532 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6534 GtkNotebookPrivate *priv = notebook->priv;
6538 pos = gtk_widget_path_length (gtk_widget_get_path (GTK_WIDGET (notebook))) - 1;
6540 for (list = priv->children; list != NULL; list = list->next)
6542 GtkNotebookPage *page = list->data;
6544 if (page->tab_label)
6546 GtkRegionFlags current_flags;
6548 /* FIXME: We should store these flags somewhere instead of poking
6549 * the widget's path */
6550 if (!gtk_widget_path_iter_has_region (gtk_widget_get_path (page->tab_label),
6552 GTK_STYLE_REGION_TAB,
6554 || current_flags != _gtk_notebook_get_tab_flags (notebook, page))
6555 _gtk_widget_invalidate_style_context (page->tab_label, GTK_CSS_CHANGE_PARENT_STATE);
6560 /* Private GtkNotebook Page Switch Methods:
6562 * gtk_notebook_real_switch_page
6565 gtk_notebook_real_switch_page (GtkNotebook *notebook,
6569 GtkNotebookPrivate *priv = notebook->priv;
6570 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6571 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6572 gboolean child_has_focus;
6574 if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6577 /* save the value here, changing visibility changes focus */
6578 child_has_focus = priv->child_has_focus;
6581 gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6583 priv->cur_page = page;
6585 if (!priv->focus_tab ||
6586 priv->focus_tab->data != (gpointer) priv->cur_page)
6588 g_list_find (priv->children, priv->cur_page);
6590 gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6592 /* If the focus was on the previous page, move it to the first
6593 * element on the new page, if possible, or if not, to the
6596 if (child_has_focus)
6598 if (priv->cur_page->last_focus_child &&
6599 gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6600 gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6602 if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6603 gtk_widget_grab_focus (GTK_WIDGET (notebook));
6606 gtk_notebook_update_tab_states (notebook);
6607 gtk_notebook_pages_allocate (notebook);
6609 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6610 g_object_notify (G_OBJECT (notebook), "page");
6613 /* Private GtkNotebook Page Switch Functions:
6615 * gtk_notebook_switch_page
6616 * gtk_notebook_page_select
6617 * gtk_notebook_switch_focus_tab
6618 * gtk_notebook_menu_switch_page
6621 gtk_notebook_switch_page (GtkNotebook *notebook,
6622 GtkNotebookPage *page)
6624 GtkNotebookPrivate *priv = notebook->priv;
6627 if (priv->cur_page == page)
6630 page_num = g_list_index (priv->children, page);
6632 g_signal_emit (notebook,
6633 notebook_signals[SWITCH_PAGE],
6640 gtk_notebook_page_select (GtkNotebook *notebook,
6641 gboolean move_focus)
6643 GtkNotebookPrivate *priv = notebook->priv;
6644 GtkNotebookPage *page;
6645 GtkDirectionType dir = GTK_DIR_DOWN; /* Quiet GCC */
6646 gint tab_pos = get_effective_tab_pos (notebook);
6648 if (!priv->focus_tab)
6651 page = priv->focus_tab->data;
6652 gtk_notebook_switch_page (notebook, page);
6661 case GTK_POS_BOTTOM:
6665 dir = GTK_DIR_RIGHT;
6672 if (gtk_widget_child_focus (page->child, dir))
6679 gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
6682 GtkNotebookPrivate *priv = notebook->priv;
6683 GtkNotebookPage *page;
6685 if (priv->focus_tab == new_child)
6688 priv->focus_tab = new_child;
6690 if (priv->scrollable)
6691 gtk_notebook_redraw_arrows (notebook);
6693 if (!priv->show_tabs || !priv->focus_tab)
6696 page = priv->focus_tab->data;
6697 gtk_notebook_switch_page (notebook, page);
6701 gtk_notebook_menu_switch_page (GtkWidget *widget,
6702 GtkNotebookPage *page)
6704 GtkNotebookPrivate *priv;
6705 GtkNotebook *notebook;
6710 parent = gtk_widget_get_parent (widget);
6711 notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6712 priv = notebook->priv;
6714 if (priv->cur_page == page)
6718 children = priv->children;
6719 while (children && children->data != page)
6721 children = children->next;
6725 g_signal_emit (notebook,
6726 notebook_signals[SWITCH_PAGE],
6732 /* Private GtkNotebook Menu Functions:
6734 * gtk_notebook_menu_item_create
6735 * gtk_notebook_menu_label_unparent
6736 * gtk_notebook_menu_detacher
6739 gtk_notebook_menu_item_create (GtkNotebook *notebook,
6742 GtkNotebookPrivate *priv = notebook->priv;
6743 GtkNotebookPage *page;
6744 GtkWidget *menu_item;
6747 if (page->default_menu)
6749 if (GTK_IS_LABEL (page->tab_label))
6750 page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6752 page->menu_label = gtk_label_new ("");
6753 gtk_widget_set_halign (page->menu_label, GTK_ALIGN_START);
6754 gtk_widget_set_valign (page->menu_label, GTK_ALIGN_CENTER);
6757 gtk_widget_show (page->menu_label);
6758 menu_item = gtk_menu_item_new ();
6759 gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6760 gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6761 g_list_position (priv->children, list));
6762 g_signal_connect (menu_item, "activate",
6763 G_CALLBACK (gtk_notebook_menu_switch_page), page);
6764 if (gtk_widget_get_visible (page->child))
6765 gtk_widget_show (menu_item);
6769 gtk_notebook_menu_label_unparent (GtkWidget *widget,
6772 gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6773 _gtk_bin_set_child (GTK_BIN (widget), NULL);
6777 gtk_notebook_menu_detacher (GtkWidget *widget,
6780 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6781 GtkNotebookPrivate *priv = notebook->priv;
6783 g_return_if_fail (priv->menu == (GtkWidget*) menu);
6788 /* Public GtkNotebook Page Insert/Remove Methods :
6790 * gtk_notebook_append_page
6791 * gtk_notebook_append_page_menu
6792 * gtk_notebook_prepend_page
6793 * gtk_notebook_prepend_page_menu
6794 * gtk_notebook_insert_page
6795 * gtk_notebook_insert_page_menu
6796 * gtk_notebook_remove_page
6799 * gtk_notebook_append_page:
6800 * @notebook: a #GtkNotebook
6801 * @child: the #GtkWidget to use as the contents of the page
6802 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6803 * for the page, or %NULL to use the default label, 'page N'
6805 * Appends a page to @notebook.
6807 * Return value: the index (starting from 0) of the appended
6808 * page in the notebook, or -1 if function fails
6811 gtk_notebook_append_page (GtkNotebook *notebook,
6813 GtkWidget *tab_label)
6815 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6816 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6817 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6819 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6823 * gtk_notebook_append_page_menu:
6824 * @notebook: a #GtkNotebook
6825 * @child: the #GtkWidget to use as the contents of the page
6826 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6827 * for the page, or %NULL to use the default label, 'page N'
6828 * @menu_label: (allow-none): the widget to use as a label for the
6829 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6830 * is a #GtkLabel or %NULL, then the menu label will be a newly
6831 * created label with the same text as @tab_label; if @tab_label
6832 * is not a #GtkLabel, @menu_label must be specified if the
6833 * page-switch menu is to be used.
6835 * Appends a page to @notebook, specifying the widget to use as the
6836 * label in the popup menu.
6838 * Return value: the index (starting from 0) of the appended
6839 * page in the notebook, or -1 if function fails
6842 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6844 GtkWidget *tab_label,
6845 GtkWidget *menu_label)
6847 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6848 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6849 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6850 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6852 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6856 * gtk_notebook_prepend_page:
6857 * @notebook: a #GtkNotebook
6858 * @child: the #GtkWidget to use as the contents of the page
6859 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6860 * for the page, or %NULL to use the default label, 'page N'
6862 * Prepends a page to @notebook.
6864 * Return value: the index (starting from 0) of the prepended
6865 * page in the notebook, or -1 if function fails
6868 gtk_notebook_prepend_page (GtkNotebook *notebook,
6870 GtkWidget *tab_label)
6872 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6873 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6874 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6876 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6880 * gtk_notebook_prepend_page_menu:
6881 * @notebook: a #GtkNotebook
6882 * @child: the #GtkWidget to use as the contents of the page
6883 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6884 * for the page, or %NULL to use the default label, 'page N'
6885 * @menu_label: (allow-none): the widget to use as a label for the
6886 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6887 * is a #GtkLabel or %NULL, then the menu label will be a newly
6888 * created label with the same text as @tab_label; if @tab_label
6889 * is not a #GtkLabel, @menu_label must be specified if the
6890 * page-switch menu is to be used.
6892 * Prepends a page to @notebook, specifying the widget to use as the
6893 * label in the popup menu.
6895 * Return value: the index (starting from 0) of the prepended
6896 * page in the notebook, or -1 if function fails
6899 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6901 GtkWidget *tab_label,
6902 GtkWidget *menu_label)
6904 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6905 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6906 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6907 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6909 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6913 * gtk_notebook_insert_page:
6914 * @notebook: a #GtkNotebook
6915 * @child: the #GtkWidget to use as the contents of the page
6916 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6917 * for the page, or %NULL to use the default label, 'page N'
6918 * @position: the index (starting at 0) at which to insert the page,
6919 * or -1 to append the page after all other pages
6921 * Insert a page into @notebook at the given position.
6923 * Return value: the index (starting from 0) of the inserted
6924 * page in the notebook, or -1 if function fails
6927 gtk_notebook_insert_page (GtkNotebook *notebook,
6929 GtkWidget *tab_label,
6932 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6933 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6934 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6936 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6941 gtk_notebook_page_compare_tab (gconstpointer a,
6944 return (((GtkNotebookPage *) a)->tab_label != b);
6948 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6952 GtkNotebook *notebook = GTK_NOTEBOOK (data);
6953 GtkNotebookPrivate *priv = notebook->priv;
6956 list = g_list_find_custom (priv->children, child,
6957 gtk_notebook_page_compare_tab);
6960 GtkNotebookPage *page = list->data;
6962 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
6963 gtk_notebook_switch_page (notebook, page);
6964 focus_tabs_in (notebook);
6971 * gtk_notebook_insert_page_menu:
6972 * @notebook: a #GtkNotebook
6973 * @child: the #GtkWidget to use as the contents of the page
6974 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6975 * for the page, or %NULL to use the default label, 'page N'
6976 * @menu_label: (allow-none): the widget to use as a label for the
6977 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6978 * is a #GtkLabel or %NULL, then the menu label will be a newly
6979 * created label with the same text as @tab_label; if @tab_label
6980 * is not a #GtkLabel, @menu_label must be specified if the
6981 * page-switch menu is to be used.
6982 * @position: the index (starting at 0) at which to insert the page,
6983 * or -1 to append the page after all other pages.
6985 * Insert a page into @notebook at the given position, specifying
6986 * the widget to use as the label in the popup menu.
6988 * Return value: the index (starting from 0) of the inserted
6989 * page in the notebook
6992 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
6994 GtkWidget *tab_label,
6995 GtkWidget *menu_label,
6998 GtkNotebookClass *class;
7000 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7001 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
7002 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
7003 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
7005 class = GTK_NOTEBOOK_GET_CLASS (notebook);
7007 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
7011 * gtk_notebook_remove_page:
7012 * @notebook: a #GtkNotebook
7013 * @page_num: the index of a notebook page, starting
7014 * from 0. If -1, the last page will be removed.
7016 * Removes a page from the notebook given its index
7020 gtk_notebook_remove_page (GtkNotebook *notebook,
7023 GtkNotebookPrivate *priv;
7026 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7028 priv = notebook->priv;
7031 list = g_list_nth (priv->children, page_num);
7033 list = g_list_last (priv->children);
7036 gtk_container_remove (GTK_CONTAINER (notebook),
7037 ((GtkNotebookPage *) list->data)->child);
7040 /* Public GtkNotebook Page Switch Methods :
7041 * gtk_notebook_get_current_page
7042 * gtk_notebook_page_num
7043 * gtk_notebook_set_current_page
7044 * gtk_notebook_next_page
7045 * gtk_notebook_prev_page
7048 * gtk_notebook_get_current_page:
7049 * @notebook: a #GtkNotebook
7051 * Returns the page number of the current page.
7053 * Return value: the index (starting from 0) of the current
7054 * page in the notebook. If the notebook has no pages,
7055 * then -1 will be returned.
7058 gtk_notebook_get_current_page (GtkNotebook *notebook)
7060 GtkNotebookPrivate *priv;
7062 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7064 priv = notebook->priv;
7066 if (!priv->cur_page)
7069 return g_list_index (priv->children, priv->cur_page);
7073 * gtk_notebook_get_nth_page:
7074 * @notebook: a #GtkNotebook
7075 * @page_num: the index of a page in the notebook, or -1
7076 * to get the last page
7078 * Returns the child widget contained in page number @page_num.
7080 * Return value: (transfer none): the child widget, or %NULL
7081 * if @page_num is out of bounds
7084 gtk_notebook_get_nth_page (GtkNotebook *notebook,
7087 GtkNotebookPrivate *priv;
7088 GtkNotebookPage *page;
7091 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7093 priv = notebook->priv;
7096 list = g_list_nth (priv->children, page_num);
7098 list = g_list_last (priv->children);
7110 * gtk_notebook_get_n_pages:
7111 * @notebook: a #GtkNotebook
7113 * Gets the number of pages in a notebook.
7115 * Return value: the number of pages in the notebook
7120 gtk_notebook_get_n_pages (GtkNotebook *notebook)
7122 GtkNotebookPrivate *priv;
7124 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
7126 priv = notebook->priv;
7128 return g_list_length (priv->children);
7132 * gtk_notebook_page_num:
7133 * @notebook: a #GtkNotebook
7134 * @child: a #GtkWidget
7136 * Finds the index of the page which contains the given child
7139 * Return value: the index of the page containing @child, or
7140 * -1 if @child is not in the notebook
7143 gtk_notebook_page_num (GtkNotebook *notebook,
7146 GtkNotebookPrivate *priv;
7150 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7152 priv = notebook->priv;
7155 children = priv->children;
7158 GtkNotebookPage *page = children->data;
7160 if (page->child == child)
7163 children = children->next;
7171 * gtk_notebook_set_current_page:
7172 * @notebook: a #GtkNotebook
7173 * @page_num: index of the page to switch to, starting from 0.
7174 * If negative, the last page will be used. If greater
7175 * than the number of pages in the notebook, nothing
7178 * Switches to the page number @page_num.
7180 * Note that due to historical reasons, GtkNotebook refuses
7181 * to switch to a page unless the child widget is visible.
7182 * Therefore, it is recommended to show child widgets before
7183 * adding them to a notebook.
7186 gtk_notebook_set_current_page (GtkNotebook *notebook,
7189 GtkNotebookPrivate *priv;
7192 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7194 priv = notebook->priv;
7197 page_num = g_list_length (priv->children) - 1;
7199 list = g_list_nth (priv->children, page_num);
7201 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7205 * gtk_notebook_next_page:
7206 * @notebook: a #GtkNotebook
7208 * Switches to the next page. Nothing happens if the current page is
7212 gtk_notebook_next_page (GtkNotebook *notebook)
7214 GtkNotebookPrivate *priv;
7217 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7219 priv = notebook->priv;
7221 list = g_list_find (priv->children, priv->cur_page);
7225 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
7229 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7233 * gtk_notebook_prev_page:
7234 * @notebook: a #GtkNotebook
7236 * Switches to the previous page. Nothing happens if the current page
7237 * is the first page.
7240 gtk_notebook_prev_page (GtkNotebook *notebook)
7242 GtkNotebookPrivate *priv;
7245 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7247 priv = notebook->priv;
7249 list = g_list_find (priv->children, priv->cur_page);
7253 list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
7257 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7260 /* Public GtkNotebook/Tab Style Functions
7262 * gtk_notebook_set_show_border
7263 * gtk_notebook_get_show_border
7264 * gtk_notebook_set_show_tabs
7265 * gtk_notebook_get_show_tabs
7266 * gtk_notebook_set_tab_pos
7267 * gtk_notebook_get_tab_pos
7268 * gtk_notebook_set_scrollable
7269 * gtk_notebook_get_scrollable
7270 * gtk_notebook_get_tab_hborder
7271 * gtk_notebook_get_tab_vborder
7274 * gtk_notebook_set_show_border:
7275 * @notebook: a #GtkNotebook
7276 * @show_border: %TRUE if a bevel should be drawn around the notebook
7278 * Sets whether a bevel will be drawn around the notebook pages.
7279 * This only has a visual effect when the tabs are not shown.
7280 * See gtk_notebook_set_show_tabs().
7283 gtk_notebook_set_show_border (GtkNotebook *notebook,
7284 gboolean show_border)
7286 GtkNotebookPrivate *priv;
7288 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7290 priv = notebook->priv;
7292 if (priv->show_border != show_border)
7294 priv->show_border = show_border;
7296 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7297 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7299 g_object_notify (G_OBJECT (notebook), "show-border");
7304 * gtk_notebook_get_show_border:
7305 * @notebook: a #GtkNotebook
7307 * Returns whether a bevel will be drawn around the notebook pages.
7308 * See gtk_notebook_set_show_border().
7310 * Return value: %TRUE if the bevel is drawn
7313 gtk_notebook_get_show_border (GtkNotebook *notebook)
7315 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7317 return notebook->priv->show_border;
7321 * gtk_notebook_set_show_tabs:
7322 * @notebook: a #GtkNotebook
7323 * @show_tabs: %TRUE if the tabs should be shown
7325 * Sets whether to show the tabs for the notebook or not.
7328 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
7331 GtkNotebookPrivate *priv;
7332 GtkNotebookPage *page;
7333 GtkStyleContext *context;
7337 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7339 priv = notebook->priv;
7341 show_tabs = show_tabs != FALSE;
7343 if (priv->show_tabs == show_tabs)
7346 priv->show_tabs = show_tabs;
7347 children = priv->children;
7348 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
7352 gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
7356 page = children->data;
7357 children = children->next;
7358 if (page->default_tab)
7360 gtk_widget_destroy (page->tab_label);
7361 page->tab_label = NULL;
7364 gtk_widget_hide (page->tab_label);
7367 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7371 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
7372 gtk_notebook_update_labels (notebook);
7373 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7376 for (i = 0; i < N_ACTION_WIDGETS; i++)
7378 if (priv->action_widget[i])
7379 gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
7382 gtk_widget_reset_style (GTK_WIDGET (notebook));
7383 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7385 g_object_notify (G_OBJECT (notebook), "show-tabs");
7389 * gtk_notebook_get_show_tabs:
7390 * @notebook: a #GtkNotebook
7392 * Returns whether the tabs of the notebook are shown.
7393 * See gtk_notebook_set_show_tabs().
7395 * Return value: %TRUE if the tabs are shown
7398 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
7400 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7402 return notebook->priv->show_tabs;
7406 * gtk_notebook_set_tab_pos:
7407 * @notebook: a #GtkNotebook.
7408 * @pos: the edge to draw the tabs at
7410 * Sets the edge at which the tabs for switching pages in the
7411 * notebook are drawn.
7414 gtk_notebook_set_tab_pos (GtkNotebook *notebook,
7415 GtkPositionType pos)
7417 GtkNotebookPrivate *priv;
7419 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7421 priv = notebook->priv;
7423 if (priv->tab_pos != pos)
7425 priv->tab_pos = pos;
7426 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7427 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7430 g_object_notify (G_OBJECT (notebook), "tab-pos");
7434 * gtk_notebook_get_tab_pos:
7435 * @notebook: a #GtkNotebook
7437 * Gets the edge at which the tabs for switching pages in the
7438 * notebook are drawn.
7440 * Return value: the edge at which the tabs are drawn
7443 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
7445 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
7447 return notebook->priv->tab_pos;
7451 * gtk_notebook_set_scrollable:
7452 * @notebook: a #GtkNotebook
7453 * @scrollable: %TRUE if scroll arrows should be added
7455 * Sets whether the tab label area will have arrows for
7456 * scrolling if there are too many tabs to fit in the area.
7459 gtk_notebook_set_scrollable (GtkNotebook *notebook,
7460 gboolean scrollable)
7462 GtkNotebookPrivate *priv;
7464 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7466 priv = notebook->priv;
7468 scrollable = (scrollable != FALSE);
7470 if (scrollable != priv->scrollable)
7472 priv->scrollable = scrollable;
7474 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7475 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7477 g_object_notify (G_OBJECT (notebook), "scrollable");
7482 * gtk_notebook_get_scrollable:
7483 * @notebook: a #GtkNotebook
7485 * Returns whether the tab label area has arrows for scrolling.
7486 * See gtk_notebook_set_scrollable().
7488 * Return value: %TRUE if arrows for scrolling are present
7491 gtk_notebook_get_scrollable (GtkNotebook *notebook)
7493 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7495 return notebook->priv->scrollable;
7499 * gtk_notebook_get_tab_hborder:
7500 * @notebook: a #GtkNotebook
7502 * Returns the horizontal width of a tab border.
7504 * Return value: horizontal width of a tab border
7508 * Deprecated: 3.4: this function returns zero
7511 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
7513 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7519 * gtk_notebook_get_tab_vborder:
7520 * @notebook: a #GtkNotebook
7522 * Returns the vertical width of a tab border.
7524 * Return value: vertical width of a tab border
7528 * Deprecated: 3.4: this function returns zero
7531 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7533 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7539 /* Public GtkNotebook Popup Menu Methods:
7541 * gtk_notebook_popup_enable
7542 * gtk_notebook_popup_disable
7547 * gtk_notebook_popup_enable:
7548 * @notebook: a #GtkNotebook
7550 * Enables the popup menu: if the user clicks with the right
7551 * mouse button on the tab labels, a menu with all the pages
7552 * will be popped up.
7555 gtk_notebook_popup_enable (GtkNotebook *notebook)
7557 GtkNotebookPrivate *priv;
7560 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7562 priv = notebook->priv;
7567 priv->menu = gtk_menu_new ();
7568 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7570 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7571 gtk_notebook_menu_item_create (notebook, list);
7573 gtk_notebook_update_labels (notebook);
7574 gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7575 GTK_WIDGET (notebook),
7576 gtk_notebook_menu_detacher);
7578 g_object_notify (G_OBJECT (notebook), "enable-popup");
7582 * gtk_notebook_popup_disable:
7583 * @notebook: a #GtkNotebook
7585 * Disables the popup menu.
7588 gtk_notebook_popup_disable (GtkNotebook *notebook)
7590 GtkNotebookPrivate *priv;
7592 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7594 priv = notebook->priv;
7599 gtk_container_foreach (GTK_CONTAINER (priv->menu),
7600 (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7601 gtk_widget_destroy (priv->menu);
7603 g_object_notify (G_OBJECT (notebook), "enable-popup");
7606 /* Public GtkNotebook Page Properties Functions:
7608 * gtk_notebook_get_tab_label
7609 * gtk_notebook_set_tab_label
7610 * gtk_notebook_set_tab_label_text
7611 * gtk_notebook_get_menu_label
7612 * gtk_notebook_set_menu_label
7613 * gtk_notebook_set_menu_label_text
7614 * gtk_notebook_get_tab_reorderable
7615 * gtk_notebook_set_tab_reorderable
7616 * gtk_notebook_get_tab_detachable
7617 * gtk_notebook_set_tab_detachable
7621 * gtk_notebook_get_tab_label:
7622 * @notebook: a #GtkNotebook
7625 * Returns the tab label widget for the page @child.
7626 * %NULL is returned if @child is not in @notebook or
7627 * if no tab label has specifically been set for @child.
7629 * Return value: (transfer none): the tab label
7632 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7637 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7638 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7640 list = CHECK_FIND_CHILD (notebook, child);
7644 if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7647 return GTK_NOTEBOOK_PAGE (list)->tab_label;
7651 * gtk_notebook_set_tab_label:
7652 * @notebook: a #GtkNotebook
7654 * @tab_label: (allow-none): the tab label widget to use, or %NULL
7655 * for default tab label
7657 * Changes the tab label for @child.
7658 * If %NULL is specified for @tab_label, then the page will
7659 * have the label 'page N'.
7662 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7664 GtkWidget *tab_label)
7666 GtkNotebookPrivate *priv;
7667 GtkNotebookPage *page;
7670 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7671 g_return_if_fail (GTK_IS_WIDGET (child));
7673 priv = notebook->priv;
7675 list = CHECK_FIND_CHILD (notebook, child);
7679 /* a NULL pointer indicates a default_tab setting, otherwise
7680 * we need to set the associated label
7684 if (page->tab_label == tab_label)
7688 gtk_notebook_remove_tab_label (notebook, page);
7692 page->default_tab = FALSE;
7693 page->tab_label = tab_label;
7694 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7698 page->default_tab = TRUE;
7699 page->tab_label = NULL;
7701 if (priv->show_tabs)
7705 g_snprintf (string, sizeof(string), _("Page %u"),
7706 g_list_position (priv->children, list));
7707 page->tab_label = gtk_label_new (string);
7708 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7712 if (page->tab_label)
7713 page->mnemonic_activate_signal =
7714 g_signal_connect (page->tab_label,
7715 "mnemonic-activate",
7716 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7719 if (priv->show_tabs && gtk_widget_get_visible (child))
7721 gtk_widget_show (page->tab_label);
7722 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7725 gtk_notebook_update_tab_states (notebook);
7726 gtk_widget_child_notify (child, "tab-label");
7730 * gtk_notebook_set_tab_label_text:
7731 * @notebook: a #GtkNotebook
7733 * @tab_text: the label text
7735 * Creates a new label and sets it as the tab label for the page
7736 * containing @child.
7739 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7741 const gchar *tab_text)
7743 GtkWidget *tab_label = NULL;
7745 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7748 tab_label = gtk_label_new (tab_text);
7749 gtk_notebook_set_tab_label (notebook, child, tab_label);
7750 gtk_widget_child_notify (child, "tab-label");
7754 * gtk_notebook_get_tab_label_text:
7755 * @notebook: a #GtkNotebook
7756 * @child: a widget contained in a page of @notebook
7758 * Retrieves the text of the tab label for the page containing
7761 * Return value: the text of the tab label, or %NULL if the
7762 * tab label widget is not a #GtkLabel. The string is owned
7763 * by the widget and must not be freed.
7766 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7769 GtkWidget *tab_label;
7771 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7772 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7774 tab_label = gtk_notebook_get_tab_label (notebook, child);
7776 if (GTK_IS_LABEL (tab_label))
7777 return gtk_label_get_text (GTK_LABEL (tab_label));
7783 * gtk_notebook_get_menu_label:
7784 * @notebook: a #GtkNotebook
7785 * @child: a widget contained in a page of @notebook
7787 * Retrieves the menu label widget of the page containing @child.
7789 * Return value: (transfer none): the menu label, or %NULL if the
7790 * notebook page does not have a menu label other than the
7791 * default (the tab label).
7794 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7799 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7800 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7802 list = CHECK_FIND_CHILD (notebook, child);
7806 if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7809 return GTK_NOTEBOOK_PAGE (list)->menu_label;
7813 * gtk_notebook_set_menu_label:
7814 * @notebook: a #GtkNotebook
7815 * @child: the child widget
7816 * @menu_label: (allow-none): the menu label, or %NULL for default
7818 * Changes the menu label for the page containing @child.
7821 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7823 GtkWidget *menu_label)
7825 GtkNotebookPrivate *priv;
7826 GtkNotebookPage *page;
7829 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7830 g_return_if_fail (GTK_IS_WIDGET (child));
7832 priv = notebook->priv;
7834 list = CHECK_FIND_CHILD (notebook, child);
7839 if (page->menu_label)
7842 gtk_container_remove (GTK_CONTAINER (priv->menu),
7843 gtk_widget_get_parent (page->menu_label));
7845 if (!page->default_menu)
7846 g_object_unref (page->menu_label);
7851 page->menu_label = menu_label;
7852 g_object_ref_sink (page->menu_label);
7853 page->default_menu = FALSE;
7856 page->default_menu = TRUE;
7859 gtk_notebook_menu_item_create (notebook, list);
7860 gtk_widget_child_notify (child, "menu-label");
7864 * gtk_notebook_set_menu_label_text:
7865 * @notebook: a #GtkNotebook
7866 * @child: the child widget
7867 * @menu_text: the label text
7869 * Creates a new label and sets it as the menu label of @child.
7872 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7874 const gchar *menu_text)
7876 GtkWidget *menu_label = NULL;
7878 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7882 menu_label = gtk_label_new (menu_text);
7883 gtk_widget_set_halign (menu_label, GTK_ALIGN_START);
7884 gtk_widget_set_valign (menu_label, GTK_ALIGN_CENTER);
7886 gtk_notebook_set_menu_label (notebook, child, menu_label);
7887 gtk_widget_child_notify (child, "menu-label");
7891 * gtk_notebook_get_menu_label_text:
7892 * @notebook: a #GtkNotebook
7893 * @child: the child widget of a page of the notebook.
7895 * Retrieves the text of the menu label for the page containing
7898 * Return value: the text of the tab label, or %NULL if the
7899 * widget does not have a menu label other than the default
7900 * menu label, or the menu label widget is not a #GtkLabel.
7901 * The string is owned by the widget and must not be freed.
7904 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7907 GtkWidget *menu_label;
7909 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7910 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7912 menu_label = gtk_notebook_get_menu_label (notebook, child);
7914 if (GTK_IS_LABEL (menu_label))
7915 return gtk_label_get_text (GTK_LABEL (menu_label));
7920 /* Helper function called when pages are reordered
7923 gtk_notebook_child_reordered (GtkNotebook *notebook,
7924 GtkNotebookPage *page)
7926 GtkNotebookPrivate *priv = notebook->priv;
7930 GtkWidget *menu_item;
7932 menu_item = gtk_widget_get_parent (page->menu_label);
7933 gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7934 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7935 gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7938 gtk_notebook_update_tab_states (notebook);
7939 gtk_notebook_update_labels (notebook);
7943 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7948 GtkNotebookPrivate *priv;
7949 GtkNotebookPage *page;
7952 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7953 g_return_if_fail (GTK_IS_WIDGET (child));
7955 priv = notebook->priv;
7957 list = CHECK_FIND_CHILD (notebook, child);
7962 expand = expand != FALSE;
7963 fill = fill != FALSE;
7964 if (page->expand == expand && page->fill == fill)
7967 gtk_widget_freeze_child_notify (child);
7968 page->expand = expand;
7969 gtk_widget_child_notify (child, "tab-expand");
7971 gtk_widget_child_notify (child, "tab-fill");
7972 gtk_widget_child_notify (child, "position");
7973 if (priv->show_tabs)
7974 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7975 gtk_widget_thaw_child_notify (child);
7979 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
7986 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7987 g_return_if_fail (GTK_IS_WIDGET (child));
7989 list = CHECK_FIND_CHILD (notebook, child);
7994 *expand = GTK_NOTEBOOK_PAGE (list)->expand;
7996 *fill = GTK_NOTEBOOK_PAGE (list)->fill;
8000 * gtk_notebook_reorder_child:
8001 * @notebook: a #GtkNotebook
8002 * @child: the child to move
8003 * @position: the new position, or -1 to move to the end
8005 * Reorders the page containing @child, so that it appears in position
8006 * @position. If @position is greater than or equal to the number of
8007 * children in the list or negative, @child will be moved to the end
8011 gtk_notebook_reorder_child (GtkNotebook *notebook,
8015 GtkNotebookPrivate *priv;
8016 GList *list, *new_list;
8017 GtkNotebookPage *page;
8022 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8023 g_return_if_fail (GTK_IS_WIDGET (child));
8025 priv = notebook->priv;
8027 list = CHECK_FIND_CHILD (notebook, child);
8031 max_pos = g_list_length (priv->children) - 1;
8032 if (position < 0 || position > max_pos)
8035 old_pos = g_list_position (priv->children, list);
8037 if (old_pos == position)
8041 priv->children = g_list_delete_link (priv->children, list);
8043 priv->children = g_list_insert (priv->children, page, position);
8044 new_list = g_list_nth (priv->children, position);
8046 /* Fix up GList references in GtkNotebook structure */
8047 if (priv->first_tab == list)
8048 priv->first_tab = new_list;
8049 if (priv->focus_tab == list)
8050 priv->focus_tab = new_list;
8052 gtk_widget_freeze_child_notify (child);
8054 /* Move around the menu items if necessary */
8055 gtk_notebook_child_reordered (notebook, page);
8057 for (list = priv->children, i = 0; list; list = list->next, i++)
8059 if (MIN (old_pos, position) <= i && i <= MAX (old_pos, position))
8060 gtk_widget_child_notify (((GtkNotebookPage *) list->data)->child, "position");
8063 if (priv->show_tabs)
8064 gtk_notebook_pages_allocate (notebook);
8066 gtk_widget_thaw_child_notify (child);
8068 g_signal_emit (notebook,
8069 notebook_signals[PAGE_REORDERED],
8076 * gtk_notebook_set_group_name:
8077 * @notebook: a #GtkNotebook
8078 * @group_name: (allow-none): the name of the notebook group,
8079 * or %NULL to unset it
8081 * Sets a group name for @notebook.
8083 * Notebooks with the same name will be able to exchange tabs
8084 * via drag and drop. A notebook with a %NULL group name will
8085 * not be able to exchange tabs with any other notebook.
8090 gtk_notebook_set_group_name (GtkNotebook *notebook,
8091 const gchar *group_name)
8093 GtkNotebookPrivate *priv;
8096 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8098 priv = notebook->priv;
8100 group = g_quark_from_string (group_name);
8102 if (priv->group != group)
8104 priv->group = group;
8105 g_object_notify (G_OBJECT (notebook), "group-name");
8110 * gtk_notebook_get_group_name:
8111 * @notebook: a #GtkNotebook
8113 * Gets the current group name for @notebook.
8115 * Return Value: (transfer none): the group name,
8116 * or %NULL if none is set.
8121 gtk_notebook_get_group_name (GtkNotebook *notebook)
8123 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8125 return g_quark_to_string (notebook->priv->group);
8129 * gtk_notebook_get_tab_reorderable:
8130 * @notebook: a #GtkNotebook
8131 * @child: a child #GtkWidget
8133 * Gets whether the tab can be reordered via drag and drop or not.
8135 * Return Value: %TRUE if the tab is reorderable.
8140 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
8145 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8146 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8148 list = CHECK_FIND_CHILD (notebook, child);
8152 return GTK_NOTEBOOK_PAGE (list)->reorderable;
8156 * gtk_notebook_set_tab_reorderable:
8157 * @notebook: a #GtkNotebook
8158 * @child: a child #GtkWidget
8159 * @reorderable: whether the tab is reorderable or not
8161 * Sets whether the notebook tab can be reordered
8162 * via drag and drop or not.
8167 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
8169 gboolean reorderable)
8173 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8174 g_return_if_fail (GTK_IS_WIDGET (child));
8176 list = CHECK_FIND_CHILD (notebook, child);
8180 if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
8182 GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
8183 gtk_widget_child_notify (child, "reorderable");
8188 * gtk_notebook_get_tab_detachable:
8189 * @notebook: a #GtkNotebook
8190 * @child: a child #GtkWidget
8192 * Returns whether the tab contents can be detached from @notebook.
8194 * Return Value: %TRUE if the tab is detachable.
8199 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
8204 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8205 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8207 list = CHECK_FIND_CHILD (notebook, child);
8211 return GTK_NOTEBOOK_PAGE (list)->detachable;
8215 * gtk_notebook_set_tab_detachable:
8216 * @notebook: a #GtkNotebook
8217 * @child: a child #GtkWidget
8218 * @detachable: whether the tab is detachable or not
8220 * Sets whether the tab can be detached from @notebook to another
8221 * notebook or widget.
8223 * Note that 2 notebooks must share a common group identificator
8224 * (see gtk_notebook_set_group_name()) to allow automatic tabs
8225 * interchange between them.
8227 * If you want a widget to interact with a notebook through DnD
8228 * (i.e.: accept dragged tabs from it) it must be set as a drop
8229 * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
8230 * will fill the selection with a GtkWidget** pointing to the child
8231 * widget that corresponds to the dropped tab.
8234 * on_drop_zone_drag_data_received (GtkWidget *widget,
8235 * GdkDragContext *context,
8238 * GtkSelectionData *selection_data,
8241 * gpointer user_data)
8243 * GtkWidget *notebook;
8244 * GtkWidget **child;
8246 * notebook = gtk_drag_get_source_widget (context);
8247 * child = (void*) gtk_selection_data_get_data (selection_data);
8249 * process_widget (*child);
8250 * gtk_container_remove (GTK_CONTAINER (notebook), *child);
8254 * If you want a notebook to accept drags from other widgets,
8255 * you will have to set your own DnD code to do it.
8260 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
8262 gboolean detachable)
8266 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8267 g_return_if_fail (GTK_IS_WIDGET (child));
8269 list = CHECK_FIND_CHILD (notebook, child);
8273 if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
8275 GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
8276 gtk_widget_child_notify (child, "detachable");
8281 * gtk_notebook_get_action_widget:
8282 * @notebook: a #GtkNotebook
8283 * @pack_type: pack type of the action widget to receive
8285 * Gets one of the action widgets. See gtk_notebook_set_action_widget().
8287 * Returns: (transfer none): The action widget with the given @pack_type
8288 * or %NULL when this action widget has not been set
8293 gtk_notebook_get_action_widget (GtkNotebook *notebook,
8294 GtkPackType pack_type)
8296 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8298 return notebook->priv->action_widget[pack_type];
8302 * gtk_notebook_set_action_widget:
8303 * @notebook: a #GtkNotebook
8304 * @widget: a #GtkWidget
8305 * @pack_type: pack type of the action widget
8307 * Sets @widget as one of the action widgets. Depending on the pack type
8308 * the widget will be placed before or after the tabs. You can use
8309 * a #GtkBox if you need to pack more than one widget on the same side.
8311 * Note that action widgets are "internal" children of the notebook and thus
8312 * not included in the list returned from gtk_container_foreach().
8317 gtk_notebook_set_action_widget (GtkNotebook *notebook,
8319 GtkPackType pack_type)
8321 GtkNotebookPrivate *priv;
8323 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8324 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
8325 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
8327 priv = notebook->priv;
8329 if (priv->action_widget[pack_type])
8330 gtk_widget_unparent (priv->action_widget[pack_type]);
8332 priv->action_widget[pack_type] = widget;
8336 gtk_widget_set_child_visible (widget, priv->show_tabs);
8337 gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
8340 gtk_widget_queue_resize (GTK_WIDGET (notebook));