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, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
23 * file for a list of people on the GTK+ Team. See the ChangeLog
24 * files for a list of changes. These files are distributed with
25 * GTK+ at ftp:ftp.gtk.org/pub/gtk/.
33 #include "gtknotebook.h"
37 #include "gtkmenuitem.h"
40 #include "gtkmarshalers.h"
41 #include "gtkbindings.h"
42 #include "gtkprivate.h"
44 #include "gtkbuildable.h"
45 #include "gtktypebuiltins.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;
161 guint child_has_focus : 1;
162 guint click_child : 3;
163 guint during_detach : 1;
164 guint during_reorder : 1;
165 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
166 guint has_scrolled : 1;
168 guint need_timer : 1;
169 guint show_border : 1;
171 guint scrollable : 1;
174 guint has_before_previous : 1;
175 guint has_before_next : 1;
176 guint has_after_previous : 1;
177 guint has_after_next : 1;
213 } GtkNotebookPointerPosition;
215 #define ARROW_IS_LEFT(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
216 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
231 CHILD_PROP_TAB_LABEL,
232 CHILD_PROP_MENU_LABEL,
234 CHILD_PROP_TAB_EXPAND,
236 CHILD_PROP_REORDERABLE,
237 CHILD_PROP_DETACHABLE
240 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
242 /* some useful defines for calculating coords */
243 #define PAGE_LEFT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x)
244 #define PAGE_RIGHT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
245 #define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
246 #define PAGE_TOP_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y)
247 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
248 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
249 #define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent (((GtkNotebookPage *) (_page_))->tab_label) == ((GtkWidget *) (_notebook_)))
251 struct _GtkNotebookPage
254 GtkWidget *tab_label;
255 GtkWidget *menu_label;
256 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
258 guint default_menu : 1; /* If true, we create the menu label ourself */
259 guint default_tab : 1; /* If true, we create the tab label ourself */
262 guint reorderable : 1;
263 guint detachable : 1;
265 /* if true, the tab label was visible on last allocation; we track this so
266 * that we know to redraw the tab area if a tab label was hidden then shown
267 * without changing position */
268 guint tab_allocated_visible : 1;
270 GtkRequisition requisition;
271 GtkAllocation allocation;
273 gulong mnemonic_activate_signal;
274 gulong notify_visible_handler;
277 static const GtkTargetEntry notebook_targets [] = {
278 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
281 #ifdef G_DISABLE_CHECKS
282 #define CHECK_FIND_CHILD(notebook, child) \
283 gtk_notebook_find_child (notebook, child, G_STRLOC)
285 #define CHECK_FIND_CHILD(notebook, child) \
286 gtk_notebook_find_child (notebook, child, NULL)
289 /*** GtkNotebook Methods ***/
290 static gboolean gtk_notebook_select_page (GtkNotebook *notebook,
291 gboolean move_focus);
292 static gboolean gtk_notebook_focus_tab (GtkNotebook *notebook,
293 GtkNotebookTab type);
294 static gboolean gtk_notebook_change_current_page (GtkNotebook *notebook,
296 static void gtk_notebook_move_focus_out (GtkNotebook *notebook,
297 GtkDirectionType direction_type);
298 static gboolean gtk_notebook_reorder_tab (GtkNotebook *notebook,
299 GtkDirectionType direction_type,
300 gboolean move_to_last);
301 static void gtk_notebook_remove_tab_label (GtkNotebook *notebook,
302 GtkNotebookPage *page);
303 static void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
307 static void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
312 /*** GObject Methods ***/
313 static void gtk_notebook_set_property (GObject *object,
317 static void gtk_notebook_get_property (GObject *object,
322 /*** GtkWidget Methods ***/
323 static void gtk_notebook_destroy (GtkWidget *widget);
324 static void gtk_notebook_map (GtkWidget *widget);
325 static void gtk_notebook_unmap (GtkWidget *widget);
326 static void gtk_notebook_realize (GtkWidget *widget);
327 static void gtk_notebook_unrealize (GtkWidget *widget);
328 static void gtk_notebook_get_preferred_width (GtkWidget *widget,
331 static void gtk_notebook_get_preferred_height(GtkWidget *widget,
334 static void gtk_notebook_get_preferred_width_for_height
339 static void gtk_notebook_get_preferred_height_for_width
344 static void gtk_notebook_size_allocate (GtkWidget *widget,
345 GtkAllocation *allocation);
346 static gint gtk_notebook_draw (GtkWidget *widget,
348 static gint gtk_notebook_button_press (GtkWidget *widget,
349 GdkEventButton *event);
350 static gint gtk_notebook_button_release (GtkWidget *widget,
351 GdkEventButton *event);
352 static gboolean gtk_notebook_popup_menu (GtkWidget *widget);
353 static gint gtk_notebook_leave_notify (GtkWidget *widget,
354 GdkEventCrossing *event);
355 static gint gtk_notebook_motion_notify (GtkWidget *widget,
356 GdkEventMotion *event);
357 static gint gtk_notebook_focus_in (GtkWidget *widget,
358 GdkEventFocus *event);
359 static gint gtk_notebook_focus_out (GtkWidget *widget,
360 GdkEventFocus *event);
361 static void gtk_notebook_grab_notify (GtkWidget *widget,
362 gboolean was_grabbed);
363 static void gtk_notebook_state_flags_changed (GtkWidget *widget,
364 GtkStateFlags previous_state);
365 static gint gtk_notebook_focus (GtkWidget *widget,
366 GtkDirectionType direction);
367 static void gtk_notebook_style_updated (GtkWidget *widget);
369 /*** Drag and drop Methods ***/
370 static void gtk_notebook_drag_begin (GtkWidget *widget,
371 GdkDragContext *context);
372 static void gtk_notebook_drag_end (GtkWidget *widget,
373 GdkDragContext *context);
374 static gboolean gtk_notebook_drag_failed (GtkWidget *widget,
375 GdkDragContext *context,
376 GtkDragResult result);
377 static gboolean gtk_notebook_drag_motion (GtkWidget *widget,
378 GdkDragContext *context,
382 static void gtk_notebook_drag_leave (GtkWidget *widget,
383 GdkDragContext *context,
385 static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
386 GdkDragContext *context,
390 static void gtk_notebook_drag_data_get (GtkWidget *widget,
391 GdkDragContext *context,
392 GtkSelectionData *data,
395 static void gtk_notebook_drag_data_received (GtkWidget *widget,
396 GdkDragContext *context,
399 GtkSelectionData *data,
403 /*** GtkContainer Methods ***/
404 static void gtk_notebook_set_child_property (GtkContainer *container,
409 static void gtk_notebook_get_child_property (GtkContainer *container,
414 static void gtk_notebook_add (GtkContainer *container,
416 static void gtk_notebook_remove (GtkContainer *container,
418 static void gtk_notebook_set_focus_child (GtkContainer *container,
420 static GType gtk_notebook_child_type (GtkContainer *container);
421 static void gtk_notebook_forall (GtkContainer *container,
422 gboolean include_internals,
423 GtkCallback callback,
424 gpointer callback_data);
425 static GtkWidgetPath * gtk_notebook_get_path_for_child (GtkContainer *container,
428 /*** GtkNotebook Methods ***/
429 static gint gtk_notebook_real_insert_page (GtkNotebook *notebook,
431 GtkWidget *tab_label,
432 GtkWidget *menu_label,
435 static GtkNotebook *gtk_notebook_create_window (GtkNotebook *notebook,
440 /*** GtkNotebook Private Functions ***/
441 static void gtk_notebook_redraw_tabs (GtkNotebook *notebook);
442 static void gtk_notebook_redraw_arrows (GtkNotebook *notebook);
443 static void gtk_notebook_real_remove (GtkNotebook *notebook,
445 static void gtk_notebook_update_labels (GtkNotebook *notebook);
446 static gint gtk_notebook_timer (GtkNotebook *notebook);
447 static void gtk_notebook_set_scroll_timer (GtkNotebook *notebook);
448 static gint gtk_notebook_page_compare (gconstpointer a,
450 static GList* gtk_notebook_find_child (GtkNotebook *notebook,
452 const gchar *function);
453 static GList * gtk_notebook_search_page (GtkNotebook *notebook,
456 gboolean find_visible);
457 static void gtk_notebook_child_reordered (GtkNotebook *notebook,
458 GtkNotebookPage *page);
460 /*** GtkNotebook Drawing Functions ***/
461 static void gtk_notebook_paint (GtkWidget *widget,
463 static void gtk_notebook_draw_tab (GtkNotebook *notebook,
464 GtkNotebookPage *page,
466 GtkRegionFlags flags);
467 static void gtk_notebook_draw_arrow (GtkNotebook *notebook,
469 GtkNotebookArrow arrow);
471 /*** GtkNotebook Size Allocate Functions ***/
472 static void gtk_notebook_pages_allocate (GtkNotebook *notebook);
473 static gboolean gtk_notebook_page_allocate (GtkNotebook *notebook,
474 GtkNotebookPage *page);
475 static void gtk_notebook_calc_tabs (GtkNotebook *notebook,
481 /*** GtkNotebook Page Switch Methods ***/
482 static void gtk_notebook_real_switch_page (GtkNotebook *notebook,
486 /*** GtkNotebook Page Switch Functions ***/
487 static void gtk_notebook_switch_page (GtkNotebook *notebook,
488 GtkNotebookPage *page);
489 static gint gtk_notebook_page_select (GtkNotebook *notebook,
490 gboolean move_focus);
491 static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
493 static void gtk_notebook_menu_switch_page (GtkWidget *widget,
494 GtkNotebookPage *page);
496 /*** GtkNotebook Menu Functions ***/
497 static void gtk_notebook_menu_item_create (GtkNotebook *notebook,
499 static void gtk_notebook_menu_label_unparent (GtkWidget *widget,
501 static void gtk_notebook_menu_detacher (GtkWidget *widget,
504 /*** GtkNotebook Private Setters ***/
505 static void gtk_notebook_update_tab_states (GtkNotebook *notebook);
506 static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
510 static gboolean focus_tabs_in (GtkNotebook *notebook);
511 static gboolean focus_child_in (GtkNotebook *notebook,
512 GtkDirectionType direction);
514 static void stop_scrolling (GtkNotebook *notebook);
515 static void do_detach_tab (GtkNotebook *from,
522 static void gtk_notebook_buildable_init (GtkBuildableIface *iface);
523 static void gtk_notebook_buildable_add_child (GtkBuildable *buildable,
528 static guint notebook_signals[LAST_SIGNAL] = { 0 };
530 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
531 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
532 gtk_notebook_buildable_init))
535 add_tab_bindings (GtkBindingSet *binding_set,
536 GdkModifierType modifiers,
537 GtkDirectionType direction)
539 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
541 GTK_TYPE_DIRECTION_TYPE, direction);
542 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
544 GTK_TYPE_DIRECTION_TYPE, direction);
548 add_arrow_bindings (GtkBindingSet *binding_set,
550 GtkDirectionType direction)
552 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
554 gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
556 GTK_TYPE_DIRECTION_TYPE, direction);
557 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
559 GTK_TYPE_DIRECTION_TYPE, direction);
563 add_reorder_bindings (GtkBindingSet *binding_set,
565 GtkDirectionType direction,
566 gboolean move_to_last)
568 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
570 gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
572 GTK_TYPE_DIRECTION_TYPE, direction,
573 G_TYPE_BOOLEAN, move_to_last);
574 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
576 GTK_TYPE_DIRECTION_TYPE, direction,
577 G_TYPE_BOOLEAN, move_to_last);
581 gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
583 const GValue *handler_return,
586 gboolean continue_emission;
589 object = g_value_get_object (handler_return);
590 g_value_set_object (return_accu, object);
591 continue_emission = !object;
593 return continue_emission;
597 gtk_notebook_compute_expand (GtkWidget *widget,
601 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
602 GtkNotebookPrivate *priv = notebook->priv;
606 GtkNotebookPage *page;
611 for (list = priv->children; list; list = list->next)
616 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_HORIZONTAL);
619 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);
621 if (hexpand & vexpand)
625 *hexpand_p = hexpand;
626 *vexpand_p = vexpand;
630 gtk_notebook_class_init (GtkNotebookClass *class)
632 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
633 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
634 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
635 GtkBindingSet *binding_set;
637 gobject_class->set_property = gtk_notebook_set_property;
638 gobject_class->get_property = gtk_notebook_get_property;
640 widget_class->destroy = gtk_notebook_destroy;
641 widget_class->map = gtk_notebook_map;
642 widget_class->unmap = gtk_notebook_unmap;
643 widget_class->realize = gtk_notebook_realize;
644 widget_class->unrealize = gtk_notebook_unrealize;
645 widget_class->get_preferred_width = gtk_notebook_get_preferred_width;
646 widget_class->get_preferred_height = gtk_notebook_get_preferred_height;
647 widget_class->get_preferred_width_for_height = gtk_notebook_get_preferred_width_for_height;
648 widget_class->get_preferred_height_for_width = gtk_notebook_get_preferred_height_for_width;
649 widget_class->size_allocate = gtk_notebook_size_allocate;
650 widget_class->draw = gtk_notebook_draw;
651 widget_class->button_press_event = gtk_notebook_button_press;
652 widget_class->button_release_event = gtk_notebook_button_release;
653 widget_class->popup_menu = gtk_notebook_popup_menu;
654 widget_class->leave_notify_event = gtk_notebook_leave_notify;
655 widget_class->motion_notify_event = gtk_notebook_motion_notify;
656 widget_class->grab_notify = gtk_notebook_grab_notify;
657 widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
658 widget_class->focus_in_event = gtk_notebook_focus_in;
659 widget_class->focus_out_event = gtk_notebook_focus_out;
660 widget_class->focus = gtk_notebook_focus;
661 widget_class->style_updated = gtk_notebook_style_updated;
662 widget_class->drag_begin = gtk_notebook_drag_begin;
663 widget_class->drag_end = gtk_notebook_drag_end;
664 widget_class->drag_motion = gtk_notebook_drag_motion;
665 widget_class->drag_leave = gtk_notebook_drag_leave;
666 widget_class->drag_drop = gtk_notebook_drag_drop;
667 widget_class->drag_data_get = gtk_notebook_drag_data_get;
668 widget_class->drag_data_received = gtk_notebook_drag_data_received;
669 widget_class->drag_failed = gtk_notebook_drag_failed;
670 widget_class->compute_expand = gtk_notebook_compute_expand;
672 container_class->add = gtk_notebook_add;
673 container_class->remove = gtk_notebook_remove;
674 container_class->forall = gtk_notebook_forall;
675 container_class->set_focus_child = gtk_notebook_set_focus_child;
676 container_class->get_child_property = gtk_notebook_get_child_property;
677 container_class->set_child_property = gtk_notebook_set_child_property;
678 container_class->child_type = gtk_notebook_child_type;
679 container_class->get_path_for_child = gtk_notebook_get_path_for_child;
681 class->switch_page = gtk_notebook_real_switch_page;
682 class->insert_page = gtk_notebook_real_insert_page;
684 class->focus_tab = gtk_notebook_focus_tab;
685 class->select_page = gtk_notebook_select_page;
686 class->change_current_page = gtk_notebook_change_current_page;
687 class->move_focus_out = gtk_notebook_move_focus_out;
688 class->reorder_tab = gtk_notebook_reorder_tab;
689 class->create_window = gtk_notebook_create_window;
691 g_object_class_install_property (gobject_class,
693 g_param_spec_int ("page",
695 P_("The index of the current page"),
699 GTK_PARAM_READWRITE));
700 g_object_class_install_property (gobject_class,
702 g_param_spec_enum ("tab-pos",
704 P_("Which side of the notebook holds the tabs"),
705 GTK_TYPE_POSITION_TYPE,
707 GTK_PARAM_READWRITE));
708 g_object_class_install_property (gobject_class,
710 g_param_spec_boolean ("show-tabs",
712 P_("Whether tabs should be shown"),
714 GTK_PARAM_READWRITE));
715 g_object_class_install_property (gobject_class,
717 g_param_spec_boolean ("show-border",
719 P_("Whether the border should be shown"),
721 GTK_PARAM_READWRITE));
722 g_object_class_install_property (gobject_class,
724 g_param_spec_boolean ("scrollable",
726 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
728 GTK_PARAM_READWRITE));
729 g_object_class_install_property (gobject_class,
731 g_param_spec_boolean ("enable-popup",
733 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
735 GTK_PARAM_READWRITE));
738 * GtkNotebook:group-name:
740 * Group name for tab drag and drop.
744 g_object_class_install_property (gobject_class,
746 g_param_spec_string ("group-name",
748 P_("Group name for tab drag and drop"),
750 GTK_PARAM_READWRITE));
752 gtk_container_class_install_child_property (container_class,
753 CHILD_PROP_TAB_LABEL,
754 g_param_spec_string ("tab-label",
756 P_("The string displayed on the child's tab label"),
758 GTK_PARAM_READWRITE));
759 gtk_container_class_install_child_property (container_class,
760 CHILD_PROP_MENU_LABEL,
761 g_param_spec_string ("menu-label",
763 P_("The string displayed in the child's menu entry"),
765 GTK_PARAM_READWRITE));
766 gtk_container_class_install_child_property (container_class,
768 g_param_spec_int ("position",
770 P_("The index of the child in the parent"),
772 GTK_PARAM_READWRITE));
773 gtk_container_class_install_child_property (container_class,
774 CHILD_PROP_TAB_EXPAND,
775 g_param_spec_boolean ("tab-expand",
777 P_("Whether to expand the child's tab"),
779 GTK_PARAM_READWRITE));
780 gtk_container_class_install_child_property (container_class,
782 g_param_spec_boolean ("tab-fill",
784 P_("Whether the child's tab should fill the allocated area"),
786 GTK_PARAM_READWRITE));
788 gtk_container_class_install_child_property (container_class,
789 CHILD_PROP_REORDERABLE,
790 g_param_spec_boolean ("reorderable",
791 P_("Tab reorderable"),
792 P_("Whether the tab is reorderable by user action"),
794 GTK_PARAM_READWRITE));
795 gtk_container_class_install_child_property (container_class,
796 CHILD_PROP_DETACHABLE,
797 g_param_spec_boolean ("detachable",
798 P_("Tab detachable"),
799 P_("Whether the tab is detachable"),
801 GTK_PARAM_READWRITE));
804 * GtkNotebook:has-secondary-backward-stepper:
806 * The "has-secondary-backward-stepper" property determines whether
807 * a second backward arrow button is displayed on the opposite end
812 gtk_widget_class_install_style_property (widget_class,
813 g_param_spec_boolean ("has-secondary-backward-stepper",
814 P_("Secondary backward stepper"),
815 P_("Display a second backward arrow button on the opposite end of the tab area"),
817 GTK_PARAM_READABLE));
820 * GtkNotebook:has-secondary-forward-stepper:
822 * The "has-secondary-forward-stepper" property determines whether
823 * a second forward arrow button is displayed on the opposite end
828 gtk_widget_class_install_style_property (widget_class,
829 g_param_spec_boolean ("has-secondary-forward-stepper",
830 P_("Secondary forward stepper"),
831 P_("Display a second forward arrow button on the opposite end of the tab area"),
833 GTK_PARAM_READABLE));
836 * GtkNotebook:has-backward-stepper:
838 * The "has-backward-stepper" property determines whether
839 * the standard backward arrow button is displayed.
843 gtk_widget_class_install_style_property (widget_class,
844 g_param_spec_boolean ("has-backward-stepper",
845 P_("Backward stepper"),
846 P_("Display the standard backward arrow button"),
848 GTK_PARAM_READABLE));
851 * GtkNotebook:has-forward-stepper:
853 * The "has-forward-stepper" property determines whether
854 * the standard forward arrow button is displayed.
858 gtk_widget_class_install_style_property (widget_class,
859 g_param_spec_boolean ("has-forward-stepper",
860 P_("Forward stepper"),
861 P_("Display the standard forward arrow button"),
863 GTK_PARAM_READABLE));
866 * GtkNotebook:tab-overlap:
868 * The "tab-overlap" property defines size of tab overlap
873 gtk_widget_class_install_style_property (widget_class,
874 g_param_spec_int ("tab-overlap",
876 P_("Size of tab overlap area"),
880 GTK_PARAM_READABLE));
883 * GtkNotebook:tab-curvature:
885 * The "tab-curvature" property defines size of tab curvature.
889 gtk_widget_class_install_style_property (widget_class,
890 g_param_spec_int ("tab-curvature",
892 P_("Size of tab curvature"),
896 GTK_PARAM_READABLE));
899 * GtkNotebook:arrow-spacing:
901 * The "arrow-spacing" property defines the spacing between the scroll
902 * arrows and the tabs.
906 gtk_widget_class_install_style_property (widget_class,
907 g_param_spec_int ("arrow-spacing",
909 P_("Scroll arrow spacing"),
913 GTK_PARAM_READABLE));
916 * GtkNotebook:initial-gap:
918 * The "initial-gap" property defines the minimum size for the initial
919 * gap between the first tab.
923 gtk_widget_class_install_style_property (widget_class,
924 g_param_spec_int ("initial-gap",
926 P_("Initial gap before the first tab"),
930 GTK_PARAM_READABLE));
933 * GtkNotebook::switch-page:
934 * @notebook: the object which received the signal.
935 * @page: the new current page
936 * @page_num: the index of the page
938 * Emitted when the user or a function changes the current page.
940 notebook_signals[SWITCH_PAGE] =
941 g_signal_new (I_("switch-page"),
942 G_TYPE_FROM_CLASS (gobject_class),
944 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
946 _gtk_marshal_VOID__OBJECT_UINT,
950 notebook_signals[FOCUS_TAB] =
951 g_signal_new (I_("focus-tab"),
952 G_TYPE_FROM_CLASS (gobject_class),
953 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
954 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
956 _gtk_marshal_BOOLEAN__ENUM,
958 GTK_TYPE_NOTEBOOK_TAB);
959 notebook_signals[SELECT_PAGE] =
960 g_signal_new (I_("select-page"),
961 G_TYPE_FROM_CLASS (gobject_class),
962 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
963 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
965 _gtk_marshal_BOOLEAN__BOOLEAN,
968 notebook_signals[CHANGE_CURRENT_PAGE] =
969 g_signal_new (I_("change-current-page"),
970 G_TYPE_FROM_CLASS (gobject_class),
971 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
972 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
974 _gtk_marshal_BOOLEAN__INT,
977 notebook_signals[MOVE_FOCUS_OUT] =
978 g_signal_new (I_("move-focus-out"),
979 G_TYPE_FROM_CLASS (gobject_class),
980 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
981 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
983 _gtk_marshal_VOID__ENUM,
985 GTK_TYPE_DIRECTION_TYPE);
986 notebook_signals[REORDER_TAB] =
987 g_signal_new (I_("reorder-tab"),
988 G_TYPE_FROM_CLASS (gobject_class),
989 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
990 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
992 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
994 GTK_TYPE_DIRECTION_TYPE,
997 * GtkNotebook::page-reordered:
998 * @notebook: the #GtkNotebook
999 * @child: the child #GtkWidget affected
1000 * @page_num: the new page number for @child
1002 * the ::page-reordered signal is emitted in the notebook
1003 * right after a page has been reordered.
1007 notebook_signals[PAGE_REORDERED] =
1008 g_signal_new (I_("page-reordered"),
1009 G_TYPE_FROM_CLASS (gobject_class),
1011 G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1013 _gtk_marshal_VOID__OBJECT_UINT,
1018 * GtkNotebook::page-removed:
1019 * @notebook: the #GtkNotebook
1020 * @child: the child #GtkWidget affected
1021 * @page_num: the @child page number
1023 * the ::page-removed signal is emitted in the notebook
1024 * right after a page is removed from the notebook.
1028 notebook_signals[PAGE_REMOVED] =
1029 g_signal_new (I_("page-removed"),
1030 G_TYPE_FROM_CLASS (gobject_class),
1032 G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1034 _gtk_marshal_VOID__OBJECT_UINT,
1039 * GtkNotebook::page-added:
1040 * @notebook: the #GtkNotebook
1041 * @child: the child #GtkWidget affected
1042 * @page_num: the new page number for @child
1044 * the ::page-added signal is emitted in the notebook
1045 * right after a page is added to the notebook.
1049 notebook_signals[PAGE_ADDED] =
1050 g_signal_new (I_("page-added"),
1051 G_TYPE_FROM_CLASS (gobject_class),
1053 G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1055 _gtk_marshal_VOID__OBJECT_UINT,
1061 * GtkNotebook::create-window:
1062 * @notebook: the #GtkNotebook emitting the signal
1063 * @page: the tab of @notebook that is being detached
1064 * @x: the X coordinate where the drop happens
1065 * @y: the Y coordinate where the drop happens
1067 * The ::create-window signal is emitted when a detachable
1068 * tab is dropped on the root window.
1070 * A handler for this signal can create a window containing
1071 * a notebook where the tab will be attached. It is also
1072 * responsible for moving/resizing the window and adding the
1073 * necessary properties to the notebook (e.g. the
1074 * #GtkNotebook:group ).
1076 * Returns: (transfer none): a #GtkNotebook that @page should be
1077 * added to, or %NULL.
1081 notebook_signals[CREATE_WINDOW] =
1082 g_signal_new (I_("create-window"),
1083 G_TYPE_FROM_CLASS (gobject_class),
1085 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1086 gtk_object_handled_accumulator, NULL,
1087 _gtk_marshal_OBJECT__OBJECT_INT_INT,
1088 GTK_TYPE_NOTEBOOK, 3,
1089 GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
1091 binding_set = gtk_binding_set_by_class (class);
1092 gtk_binding_entry_add_signal (binding_set,
1095 G_TYPE_BOOLEAN, FALSE);
1096 gtk_binding_entry_add_signal (binding_set,
1097 GDK_KEY_KP_Space, 0,
1099 G_TYPE_BOOLEAN, FALSE);
1101 gtk_binding_entry_add_signal (binding_set,
1104 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1105 gtk_binding_entry_add_signal (binding_set,
1108 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1109 gtk_binding_entry_add_signal (binding_set,
1112 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1113 gtk_binding_entry_add_signal (binding_set,
1116 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1118 gtk_binding_entry_add_signal (binding_set,
1119 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1120 "change-current-page", 1,
1122 gtk_binding_entry_add_signal (binding_set,
1123 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1124 "change-current-page", 1,
1127 gtk_binding_entry_add_signal (binding_set,
1128 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1129 "change-current-page", 1,
1131 gtk_binding_entry_add_signal (binding_set,
1132 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1133 "change-current-page", 1,
1136 add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1137 add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1138 add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1139 add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1141 add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1142 add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1143 add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1144 add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1145 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1146 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1147 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1148 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1150 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1151 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1153 g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1155 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_NOTEBOOK_ACCESSIBLE);
1159 gtk_notebook_init (GtkNotebook *notebook)
1161 GtkNotebookPrivate *priv;
1162 GtkStyleContext *context;
1164 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1165 gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1167 notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1169 GtkNotebookPrivate);
1170 priv = notebook->priv;
1172 priv->cur_page = NULL;
1173 priv->children = NULL;
1174 priv->first_tab = NULL;
1175 priv->focus_tab = NULL;
1176 priv->event_window = NULL;
1179 priv->tab_hborder = 2;
1180 priv->tab_vborder = 2;
1182 priv->show_tabs = TRUE;
1183 priv->show_border = TRUE;
1184 priv->tab_pos = GTK_POS_TOP;
1185 priv->scrollable = FALSE;
1187 priv->click_child = 0;
1189 priv->need_timer = 0;
1190 priv->child_has_focus = FALSE;
1191 priv->focus_out = FALSE;
1193 priv->has_before_previous = 1;
1194 priv->has_before_next = 0;
1195 priv->has_after_previous = 0;
1196 priv->has_after_next = 1;
1199 priv->pressed_button = -1;
1200 priv->dnd_timer = 0;
1201 priv->switch_tab_timer = 0;
1202 priv->source_targets = gtk_target_list_new (notebook_targets,
1203 G_N_ELEMENTS (notebook_targets));
1204 priv->operation = DRAG_OPERATION_NONE;
1205 priv->detached_tab = NULL;
1206 priv->during_detach = FALSE;
1207 priv->has_scrolled = FALSE;
1209 gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1210 notebook_targets, G_N_ELEMENTS (notebook_targets),
1213 gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1215 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
1216 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
1220 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1222 iface->add_child = gtk_notebook_buildable_add_child;
1226 gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1227 GtkBuilder *builder,
1231 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1233 if (type && strcmp (type, "tab") == 0)
1237 page = gtk_notebook_get_nth_page (notebook, -1);
1238 /* To set the tab label widget, we must have already a child
1239 * inside the tab container. */
1240 g_assert (page != NULL);
1241 /* warn when Glade tries to overwrite label */
1242 if (gtk_notebook_get_tab_label (notebook, page))
1243 g_warning ("Overriding tab label for notebook");
1244 gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1246 else if (type && strcmp (type, "action-start") == 0)
1248 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1250 else if (type && strcmp (type, "action-end") == 0)
1252 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1255 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1257 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1261 gtk_notebook_select_page (GtkNotebook *notebook,
1262 gboolean move_focus)
1264 GtkNotebookPrivate *priv = notebook->priv;
1266 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1268 gtk_notebook_page_select (notebook, move_focus);
1276 gtk_notebook_focus_tab (GtkNotebook *notebook,
1277 GtkNotebookTab type)
1279 GtkNotebookPrivate *priv = notebook->priv;
1282 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1286 case GTK_NOTEBOOK_TAB_FIRST:
1287 list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1289 gtk_notebook_switch_focus_tab (notebook, list);
1291 case GTK_NOTEBOOK_TAB_LAST:
1292 list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1294 gtk_notebook_switch_focus_tab (notebook, list);
1305 gtk_notebook_change_current_page (GtkNotebook *notebook,
1308 GtkNotebookPrivate *priv = notebook->priv;
1309 GList *current = NULL;
1311 if (!priv->show_tabs)
1315 current = g_list_find (priv->children, priv->cur_page);
1319 current = gtk_notebook_search_page (notebook, current,
1320 offset < 0 ? STEP_PREV : STEP_NEXT,
1325 gboolean wrap_around;
1327 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1328 "gtk-keynav-wrap-around", &wrap_around,
1332 current = gtk_notebook_search_page (notebook, NULL,
1333 offset < 0 ? STEP_PREV : STEP_NEXT,
1339 offset += offset < 0 ? 1 : -1;
1343 gtk_notebook_switch_page (notebook, current->data);
1345 gtk_widget_error_bell (GTK_WIDGET (notebook));
1350 static GtkDirectionType
1351 get_effective_direction (GtkNotebook *notebook,
1352 GtkDirectionType direction)
1354 GtkNotebookPrivate *priv = notebook->priv;
1356 /* Remap the directions into the effective direction it would be for a
1357 * GTK_POS_TOP notebook
1360 #define D(rest) GTK_DIR_##rest
1362 static const GtkDirectionType translate_direction[2][4][6] = {
1363 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1364 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1365 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1366 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1367 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1368 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1369 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1370 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1375 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1377 return translate_direction[text_dir][priv->tab_pos][direction];
1381 get_effective_tab_pos (GtkNotebook *notebook)
1383 GtkNotebookPrivate *priv = notebook->priv;
1385 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1387 switch (priv->tab_pos)
1390 return GTK_POS_RIGHT;
1392 return GTK_POS_LEFT;
1397 return priv->tab_pos;
1401 get_tab_gap_pos (GtkNotebook *notebook)
1403 gint tab_pos = get_effective_tab_pos (notebook);
1404 gint gap_side = GTK_POS_BOTTOM;
1409 gap_side = GTK_POS_BOTTOM;
1411 case GTK_POS_BOTTOM:
1412 gap_side = GTK_POS_TOP;
1415 gap_side = GTK_POS_RIGHT;
1418 gap_side = GTK_POS_LEFT;
1426 gtk_notebook_move_focus_out (GtkNotebook *notebook,
1427 GtkDirectionType direction_type)
1429 GtkNotebookPrivate *priv = notebook->priv;
1430 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1431 GtkWidget *toplevel;
1433 if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1434 if (focus_tabs_in (notebook))
1436 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1437 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1440 /* At this point, we know we should be focusing out of the notebook entirely. We
1441 * do this by setting a flag, then propagating the focus motion to the notebook.
1443 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1444 if (!gtk_widget_is_toplevel (toplevel))
1447 g_object_ref (notebook);
1449 priv->focus_out = TRUE;
1450 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1451 priv->focus_out = FALSE;
1453 g_object_unref (notebook);
1457 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1459 GtkNotebookPrivate *priv = notebook->priv;
1462 if (position == tab)
1463 return g_list_position (priv->children, tab);
1465 /* check that we aren't inserting the tab in the
1466 * same relative position, taking packing into account */
1467 elem = (position) ? position->prev : g_list_last (priv->children);
1470 return g_list_position (priv->children, tab);
1472 /* now actually reorder the tab */
1473 if (priv->first_tab == tab)
1474 priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1477 priv->children = g_list_remove_link (priv->children, tab);
1480 elem = g_list_last (priv->children);
1483 elem = position->prev;
1484 position->prev = tab;
1490 priv->children = tab;
1493 tab->next = position;
1495 return g_list_position (priv->children, tab);
1499 gtk_notebook_reorder_tab (GtkNotebook *notebook,
1500 GtkDirectionType direction_type,
1501 gboolean move_to_last)
1503 GtkNotebookPrivate *priv = notebook->priv;
1504 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1505 GList *last, *child;
1508 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1511 if (!priv->cur_page ||
1512 !priv->cur_page->reorderable)
1515 if (effective_direction != GTK_DIR_LEFT &&
1516 effective_direction != GTK_DIR_RIGHT)
1521 child = priv->focus_tab;
1526 child = gtk_notebook_search_page (notebook, last,
1527 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1535 child = gtk_notebook_search_page (notebook, priv->focus_tab,
1536 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1539 if (!child || child->data == priv->cur_page)
1542 if (effective_direction == GTK_DIR_RIGHT)
1543 page_num = reorder_tab (notebook, child->next, priv->focus_tab);
1545 page_num = reorder_tab (notebook, child, priv->focus_tab);
1547 gtk_notebook_pages_allocate (notebook);
1549 g_signal_emit (notebook,
1550 notebook_signals[PAGE_REORDERED],
1552 ((GtkNotebookPage *) priv->focus_tab->data)->child,
1561 * Creates a new #GtkNotebook widget with no pages.
1563 * Return value: the newly created #GtkNotebook
1566 gtk_notebook_new (void)
1568 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1571 /* Private GObject Methods :
1573 * gtk_notebook_set_property
1574 * gtk_notebook_get_property
1577 gtk_notebook_set_property (GObject *object,
1579 const GValue *value,
1582 GtkNotebook *notebook;
1584 notebook = GTK_NOTEBOOK (object);
1588 case PROP_SHOW_TABS:
1589 gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1591 case PROP_SHOW_BORDER:
1592 gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1594 case PROP_SCROLLABLE:
1595 gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1597 case PROP_ENABLE_POPUP:
1598 if (g_value_get_boolean (value))
1599 gtk_notebook_popup_enable (notebook);
1601 gtk_notebook_popup_disable (notebook);
1604 gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1607 gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1609 case PROP_GROUP_NAME:
1610 gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1613 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1619 gtk_notebook_get_property (GObject *object,
1624 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1625 GtkNotebookPrivate *priv = notebook->priv;
1629 case PROP_SHOW_TABS:
1630 g_value_set_boolean (value, priv->show_tabs);
1632 case PROP_SHOW_BORDER:
1633 g_value_set_boolean (value, priv->show_border);
1635 case PROP_SCROLLABLE:
1636 g_value_set_boolean (value, priv->scrollable);
1638 case PROP_ENABLE_POPUP:
1639 g_value_set_boolean (value, priv->menu != NULL);
1642 g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1645 g_value_set_enum (value, priv->tab_pos);
1647 case PROP_GROUP_NAME:
1648 g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1651 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1656 /* Private GtkWidget Methods :
1658 * gtk_notebook_destroy
1660 * gtk_notebook_unmap
1661 * gtk_notebook_realize
1662 * gtk_notebook_size_request
1663 * gtk_notebook_size_allocate
1665 * gtk_notebook_scroll
1666 * gtk_notebook_button_press
1667 * gtk_notebook_button_release
1668 * gtk_notebook_popup_menu
1669 * gtk_notebook_leave_notify
1670 * gtk_notebook_motion_notify
1671 * gtk_notebook_focus_in
1672 * gtk_notebook_focus_out
1673 * gtk_notebook_style_updated
1674 * gtk_notebook_drag_begin
1675 * gtk_notebook_drag_end
1676 * gtk_notebook_drag_failed
1677 * gtk_notebook_drag_motion
1678 * gtk_notebook_drag_drop
1679 * gtk_notebook_drag_data_get
1680 * gtk_notebook_drag_data_received
1683 gtk_notebook_destroy (GtkWidget *widget)
1685 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1686 GtkNotebookPrivate *priv = notebook->priv;
1689 gtk_notebook_popup_disable (notebook);
1691 if (priv->source_targets)
1693 gtk_target_list_unref (priv->source_targets);
1694 priv->source_targets = NULL;
1697 if (priv->switch_tab_timer)
1699 g_source_remove (priv->switch_tab_timer);
1700 priv->switch_tab_timer = 0;
1703 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->destroy (widget);
1707 gtk_notebook_get_event_window_position (GtkNotebook *notebook,
1708 GdkRectangle *rectangle)
1710 GtkNotebookPrivate *priv = notebook->priv;
1711 GtkAllocation allocation, action_allocation;
1712 GtkWidget *widget = GTK_WIDGET (notebook);
1713 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1714 GtkNotebookPage *visible_page = NULL;
1716 gint tab_pos = get_effective_tab_pos (notebook);
1720 for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1722 GtkNotebookPage *page = tmp_list->data;
1723 if (gtk_widget_get_visible (page->child))
1725 visible_page = page;
1730 if (priv->show_tabs && visible_page)
1734 gtk_widget_get_allocation (widget, &allocation);
1736 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1737 rectangle->x = allocation.x + border_width;
1738 rectangle->y = allocation.y + border_width;
1743 case GTK_POS_BOTTOM:
1744 rectangle->width = allocation.width - 2 * border_width;
1745 rectangle->height = visible_page->requisition.height;
1746 if (tab_pos == GTK_POS_BOTTOM)
1747 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1749 for (i = 0; i < N_ACTION_WIDGETS; i++)
1751 if (priv->action_widget[i] &&
1752 gtk_widget_get_visible (priv->action_widget[i]))
1754 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1756 rectangle->width -= action_allocation.width;
1757 if ((!is_rtl && i == ACTION_WIDGET_START) ||
1758 (is_rtl && i == ACTION_WIDGET_END))
1759 rectangle->x += action_allocation.width;
1765 rectangle->width = visible_page->requisition.width;
1766 rectangle->height = allocation.height - 2 * border_width;
1767 if (tab_pos == GTK_POS_RIGHT)
1768 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1770 for (i = 0; i < N_ACTION_WIDGETS; i++)
1772 if (priv->action_widget[i] &&
1773 gtk_widget_get_visible (priv->action_widget[i]))
1775 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1777 rectangle->height -= action_allocation.height;
1779 if (i == ACTION_WIDGET_START)
1780 rectangle->y += action_allocation.height;
1793 rectangle->x = rectangle->y = 0;
1794 rectangle->width = rectangle->height = 10;
1802 gtk_notebook_map (GtkWidget *widget)
1804 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1805 GtkNotebookPrivate *priv = notebook->priv;
1806 GtkNotebookPage *page;
1810 gtk_widget_set_mapped (widget, TRUE);
1812 if (priv->cur_page &&
1813 gtk_widget_get_visible (priv->cur_page->child) &&
1814 !gtk_widget_get_mapped (priv->cur_page->child))
1815 gtk_widget_map (priv->cur_page->child);
1817 for (i = 0; i < N_ACTION_WIDGETS; i++)
1819 if (priv->action_widget[i] &&
1820 gtk_widget_get_visible (priv->action_widget[i]) &&
1821 gtk_widget_get_child_visible (priv->action_widget[i]) &&
1822 !gtk_widget_get_mapped (priv->action_widget[i]))
1823 gtk_widget_map (priv->action_widget[i]);
1826 if (priv->scrollable)
1827 gtk_notebook_pages_allocate (notebook);
1830 children = priv->children;
1834 page = children->data;
1835 children = children->next;
1837 if (page->tab_label &&
1838 gtk_widget_get_visible (page->tab_label) &&
1839 !gtk_widget_get_mapped (page->tab_label))
1840 gtk_widget_map (page->tab_label);
1844 if (gtk_notebook_get_event_window_position (notebook, NULL))
1845 gdk_window_show_unraised (priv->event_window);
1849 gtk_notebook_unmap (GtkWidget *widget)
1851 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1852 GtkNotebookPrivate *priv = notebook->priv;
1854 stop_scrolling (notebook);
1856 gtk_widget_set_mapped (widget, FALSE);
1858 gdk_window_hide (priv->event_window);
1860 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1864 gtk_notebook_realize (GtkWidget *widget)
1866 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1867 GtkNotebookPrivate *priv = notebook->priv;
1869 GdkWindowAttr attributes;
1870 gint attributes_mask;
1871 GdkRectangle event_window_pos;
1873 gtk_widget_set_realized (widget, TRUE);
1875 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1877 window = gtk_widget_get_parent_window (widget);
1878 gtk_widget_set_window (widget, window);
1879 g_object_ref (window);
1881 attributes.window_type = GDK_WINDOW_CHILD;
1882 attributes.x = event_window_pos.x;
1883 attributes.y = event_window_pos.y;
1884 attributes.width = event_window_pos.width;
1885 attributes.height = event_window_pos.height;
1886 attributes.wclass = GDK_INPUT_ONLY;
1887 attributes.event_mask = gtk_widget_get_events (widget);
1888 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1889 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1890 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1891 attributes_mask = GDK_WA_X | GDK_WA_Y;
1893 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1894 &attributes, attributes_mask);
1895 gdk_window_set_user_data (priv->event_window, notebook);
1899 gtk_notebook_unrealize (GtkWidget *widget)
1901 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1902 GtkNotebookPrivate *priv = notebook->priv;
1904 gdk_window_set_user_data (priv->event_window, NULL);
1905 gdk_window_destroy (priv->event_window);
1906 priv->event_window = NULL;
1908 if (priv->drag_window)
1910 gdk_window_set_user_data (priv->drag_window, NULL);
1911 gdk_window_destroy (priv->drag_window);
1912 priv->drag_window = NULL;
1915 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1918 static GtkRegionFlags
1919 _gtk_notebook_get_tab_flags (GtkNotebook *notebook,
1920 GtkNotebookPage *page)
1922 GtkNotebookPrivate *priv = notebook->priv;
1923 gint i = 0, page_num = -1;
1924 GtkRegionFlags flags = 0;
1925 gboolean is_last = FALSE;
1928 for (pages = priv->children; pages; pages = pages->next)
1930 GtkNotebookPage *p = pages->data;
1932 if (!p->tab_label || !gtk_widget_get_visible (p->tab_label))
1937 /* No need to keep counting tabs after it */
1941 is_last = pages->next == NULL;
1949 if ((page_num) % 2 == 0)
1950 flags |= GTK_REGION_EVEN;
1952 flags |= GTK_REGION_ODD;
1955 flags |= GTK_REGION_FIRST;
1958 flags |= GTK_REGION_LAST;
1964 gtk_notebook_get_preferred_tabs_size (GtkNotebook *notebook,
1965 GtkRequisition *requisition)
1967 GtkNotebookPrivate *priv;
1970 gint tab_height = 0;
1974 gint action_width = 0;
1975 gint action_height = 0;
1976 guint vis_pages = 0;
1978 GtkNotebookPage *page;
1979 GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
1980 GtkRequisition child_requisition;
1981 GtkStyleContext *context;
1987 gint scroll_arrow_hlength;
1988 gint scroll_arrow_vlength;
1990 priv = notebook->priv;
1991 widget = GTK_WIDGET (notebook);
1992 context = gtk_widget_get_style_context (widget);
1993 gtk_widget_style_get (widget,
1994 "focus-line-width", &focus_width,
1995 "focus-padding", &focus_pad,
1996 "tab-overlap", &tab_overlap,
1997 "tab-curvature", &tab_curvature,
1998 "arrow-spacing", &arrow_spacing,
1999 "scroll-arrow-hlength", &scroll_arrow_hlength,
2000 "scroll-arrow-vlength", &scroll_arrow_vlength,
2003 for (children = priv->children; children;
2004 children = children->next)
2006 page = children->data;
2008 if (gtk_widget_get_visible (page->child))
2010 GtkBorder tab_padding;
2011 GtkStateFlags state;
2015 if (!gtk_widget_get_visible (page->tab_label))
2016 gtk_widget_show (page->tab_label);
2018 gtk_widget_get_preferred_size (page->tab_label,
2019 &child_requisition, NULL);
2021 /* Get border/padding for tab */
2022 if (page == priv->cur_page)
2023 state = GTK_STATE_FLAG_ACTIVE;
2025 state = GTK_STATE_FLAG_NORMAL;
2027 gtk_style_context_save (context);
2028 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB,
2029 _gtk_notebook_get_tab_flags (notebook, page));
2030 gtk_style_context_get_padding (context, state, &tab_padding);
2031 gtk_style_context_restore (context);
2033 page->requisition.width = child_requisition.width +
2034 tab_padding.left + tab_padding.right + 2 * (focus_width + focus_pad);
2036 page->requisition.height = child_requisition.height +
2037 tab_padding.top + tab_padding.bottom + 2 * (focus_width + focus_pad);
2039 switch (priv->tab_pos)
2042 case GTK_POS_BOTTOM:
2043 page->requisition.height += 2 * priv->tab_vborder;
2044 tab_height = MAX (tab_height, page->requisition.height);
2045 tab_max = MAX (tab_max, page->requisition.width);
2049 page->requisition.width += 2 * priv->tab_hborder;
2050 tab_width = MAX (tab_width, page->requisition.width);
2051 tab_max = MAX (tab_max, page->requisition.height);
2055 else if (gtk_widget_get_visible (page->tab_label))
2056 gtk_widget_hide (page->tab_label);
2059 children = priv->children;
2063 for (i = 0; i < N_ACTION_WIDGETS; i++)
2065 if (priv->action_widget[i])
2067 gtk_widget_get_preferred_size (priv->action_widget[i],
2068 &action_widget_requisition[i], NULL);
2072 switch (priv->tab_pos)
2075 case GTK_POS_BOTTOM:
2076 if (tab_height == 0)
2079 if (priv->scrollable)
2080 tab_height = MAX (tab_height, scroll_arrow_hlength);
2082 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
2083 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
2085 padding = 2 * (tab_curvature + priv->tab_hborder) - tab_overlap;
2089 page = children->data;
2090 children = children->next;
2092 if (!gtk_widget_get_visible (page->child))
2095 page->requisition.width += padding;
2097 tab_width += page->requisition.width;
2098 page->requisition.height = tab_height;
2101 if (priv->scrollable)
2102 tab_width = MIN (tab_width,
2103 tab_max + 2 * (scroll_arrow_hlength + arrow_spacing));
2105 action_width += action_widget_requisition[ACTION_WIDGET_START].width;
2106 action_width += action_widget_requisition[ACTION_WIDGET_END].width;
2107 requisition->width = tab_width + tab_overlap + action_width;
2109 requisition->height = tab_height;
2116 if (priv->scrollable)
2117 tab_width = MAX (tab_width, arrow_spacing + 2 * scroll_arrow_vlength);
2119 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2120 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2122 padding = 2 * (tab_curvature + priv->tab_vborder) - tab_overlap;
2127 page = children->data;
2128 children = children->next;
2130 if (!gtk_widget_get_visible (page->child))
2133 page->requisition.width = tab_width;
2135 page->requisition.height += padding;
2137 tab_height += page->requisition.height;
2140 if (priv->scrollable)
2141 tab_height = MIN (tab_height,
2142 tab_max + (2 * scroll_arrow_vlength + arrow_spacing));
2143 action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2144 action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2146 requisition->height = tab_height + tab_overlap + action_height;
2148 requisition->height = MAX (requisition->height, tab_max + tab_overlap);
2150 requisition->width = tab_width;
2153 g_assert_not_reached ();
2154 requisition->width = 0;
2155 requisition->height = 0;
2160 requisition->width = 0;
2161 requisition->height = 0;
2166 get_preferred_size_for_size (GtkWidget *widget,
2167 GtkOrientation orientation,
2172 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2174 gtk_widget_get_preferred_width (widget, minimum, natural);
2176 gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
2179 gtk_widget_get_preferred_height (widget, minimum, natural);
2181 gtk_widget_get_preferred_height_for_width (widget, size, minimum, natural);
2185 gtk_notebook_size_request (GtkWidget *widget,
2186 GtkOrientation orientation,
2191 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2192 GtkNotebookPrivate *priv = notebook->priv;
2193 GtkNotebookPage *page;
2195 gint child_minimum, child_natural;
2196 gboolean switch_page = FALSE;
2203 for (children = priv->children, vis_pages = 0; children;
2204 children = children->next)
2207 page = children->data;
2209 if (gtk_widget_get_visible (page->child))
2212 get_preferred_size_for_size (page->child,
2218 *minimum = MAX (*minimum, child_minimum);
2219 *natural = MAX (*natural, child_natural);
2221 if (priv->menu && page->menu_label)
2223 parent = gtk_widget_get_parent (page->menu_label);
2224 if (parent && !gtk_widget_get_visible (parent))
2225 gtk_widget_show (parent);
2230 if (page == priv->cur_page)
2233 if (priv->menu && page->menu_label)
2235 parent = gtk_widget_get_parent (page->menu_label);
2236 if (parent && gtk_widget_get_visible (parent))
2237 gtk_widget_hide (parent);
2242 if (priv->show_border || priv->show_tabs)
2244 GtkStyleContext *context;
2245 GtkBorder notebook_padding;
2247 context = gtk_widget_get_style_context (widget);
2248 gtk_style_context_get_padding (context, 0, ¬ebook_padding);
2250 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2252 *minimum += notebook_padding.left + notebook_padding.right;
2253 *natural += notebook_padding.left + notebook_padding.right;
2257 *minimum += notebook_padding.top + notebook_padding.bottom;
2258 *natural += notebook_padding.top + notebook_padding.bottom;
2261 if (priv->show_tabs)
2263 GtkRequisition tabs_requisition = { 0, 0 };
2265 gtk_notebook_get_preferred_tabs_size (notebook, &tabs_requisition);
2266 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2268 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
2270 *minimum = MAX (*minimum, tabs_requisition.width);
2271 *natural = MAX (*minimum, *natural);
2275 *minimum += tabs_requisition.width;
2276 *natural += tabs_requisition.width;
2281 if (priv->tab_pos == GTK_POS_LEFT || priv->tab_pos == GTK_POS_RIGHT)
2283 *minimum = MAX (*minimum, tabs_requisition.height);
2284 *natural = MAX (*minimum, *natural);
2288 *minimum += tabs_requisition.height;
2289 *natural += tabs_requisition.height;
2295 for (children = priv->children; children;
2296 children = children->next)
2298 page = children->data;
2300 if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2301 gtk_widget_hide (page->tab_label);
2306 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2308 *minimum += border_width * 2;
2309 *natural += border_width * 2;
2315 for (children = priv->children; children;
2316 children = children->next)
2318 page = children->data;
2319 if (gtk_widget_get_visible (page->child))
2321 gtk_notebook_switch_page (notebook, page);
2326 else if (gtk_widget_get_visible (widget))
2328 *minimum = border_width * 2;
2331 if (vis_pages && !priv->cur_page)
2333 children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2336 priv->first_tab = children;
2337 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2343 gtk_notebook_get_preferred_width_for_height (GtkWidget *widget,
2348 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
2352 gtk_notebook_get_preferred_height_for_width (GtkWidget *widget,
2357 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
2361 gtk_notebook_get_preferred_width (GtkWidget *widget,
2365 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural);
2369 gtk_notebook_get_preferred_height (GtkWidget *widget,
2373 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural);
2377 gtk_notebook_size_allocate (GtkWidget *widget,
2378 GtkAllocation *allocation)
2380 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2381 GtkNotebookPrivate *priv = notebook->priv;
2382 gint tab_pos = get_effective_tab_pos (notebook);
2386 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2388 gtk_widget_set_allocation (widget, allocation);
2390 if (gtk_widget_get_realized (widget))
2392 GdkRectangle position;
2394 if (gtk_notebook_get_event_window_position (notebook, &position))
2396 gdk_window_move_resize (priv->event_window,
2397 position.x, position.y,
2398 position.width, position.height);
2399 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2400 gdk_window_show_unraised (priv->event_window);
2403 gdk_window_hide (priv->event_window);
2408 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2409 GtkNotebookPage *page;
2410 GtkAllocation child_allocation;
2414 child_allocation.x = allocation->x + border_width;
2415 child_allocation.y = allocation->y + border_width;
2416 child_allocation.width = MAX (1, allocation->width - border_width * 2);
2417 child_allocation.height = MAX (1, allocation->height - border_width * 2);
2419 if (priv->show_tabs || priv->show_border)
2421 GtkStyleContext *context;
2424 context = gtk_widget_get_style_context (widget);
2425 gtk_style_context_get_padding (context, 0, &padding);
2427 child_allocation.x += padding.left;
2428 child_allocation.y += padding.top;
2429 child_allocation.width = MAX (1, child_allocation.width - padding.left - padding.right);
2430 child_allocation.height = MAX (1, child_allocation.height - padding.top - padding.bottom);
2432 if (priv->show_tabs && priv->children && priv->cur_page)
2437 child_allocation.y += priv->cur_page->requisition.height;
2438 case GTK_POS_BOTTOM:
2439 child_allocation.height =
2440 MAX (1, child_allocation.height -
2441 priv->cur_page->requisition.height);
2444 child_allocation.x += priv->cur_page->requisition.width;
2446 child_allocation.width =
2447 MAX (1, child_allocation.width -
2448 priv->cur_page->requisition.width);
2452 for (i = 0; i < N_ACTION_WIDGETS; i++)
2454 GtkAllocation widget_allocation;
2455 GtkRequisition requisition;
2457 if (!priv->action_widget[i])
2460 widget_allocation.x = allocation->x + border_width;
2461 widget_allocation.y = allocation->y + border_width;
2462 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2464 gtk_widget_get_preferred_size (priv->action_widget[i],
2465 &requisition, NULL);
2469 case GTK_POS_BOTTOM:
2470 widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2473 widget_allocation.width = requisition.width;
2474 widget_allocation.height = priv->cur_page->requisition.height - padding.top;
2476 if ((i == ACTION_WIDGET_START && is_rtl) ||
2477 (i == ACTION_WIDGET_END && !is_rtl))
2478 widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2479 if (tab_pos == GTK_POS_TOP) /* no fall through */
2480 widget_allocation.y += 2 * focus_width;
2483 widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2486 widget_allocation.height = requisition.height;
2487 widget_allocation.width = priv->cur_page->requisition.width - padding.left;
2489 if (i == ACTION_WIDGET_END)
2490 widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2491 if (tab_pos == GTK_POS_LEFT) /* no fall through */
2492 widget_allocation.x += 2 * focus_width;
2496 gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2501 children = priv->children;
2504 page = children->data;
2505 children = children->next;
2507 if (gtk_widget_get_visible (page->child))
2508 gtk_widget_size_allocate (page->child, &child_allocation);
2511 gtk_notebook_pages_allocate (notebook);
2516 gtk_notebook_draw (GtkWidget *widget,
2519 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2520 GtkNotebookPrivate *priv = notebook->priv;
2521 GtkAllocation allocation;
2525 gtk_widget_get_allocation (widget, &allocation);
2527 window = gtk_widget_get_window (widget);
2528 if (gtk_cairo_should_draw_window (cr, window))
2532 cairo_translate (cr, -allocation.x, -allocation.y);
2533 gtk_notebook_paint (widget, cr);
2537 if (priv->show_tabs)
2539 GtkNotebookPage *page;
2542 for (pages = priv->children; pages; pages = pages->next)
2544 page = GTK_NOTEBOOK_PAGE (pages);
2546 if (gtk_widget_get_parent (page->tab_label) == widget)
2547 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2548 page->tab_label, cr);
2552 if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2553 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2554 priv->cur_page->child,
2556 if (priv->show_tabs)
2558 for (i = 0; i < N_ACTION_WIDGETS; i++)
2560 if (priv->action_widget[i])
2561 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2562 priv->action_widget[i], cr);
2567 if (priv->operation == DRAG_OPERATION_REORDER &&
2568 gtk_cairo_should_draw_window (cr, priv->drag_window))
2570 GtkStyleContext *context;
2574 gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2575 context = gtk_widget_get_style_context (widget);
2577 /* FIXME: This is a workaround to make tabs reordering work better
2578 * with engines with rounded tabs. If the drag window background
2579 * isn't set, the rounded corners would be black.
2581 * Ideally, these corners should be made transparent, Either by using
2582 * ARGB visuals or shape windows.
2584 gtk_style_context_get_background_color (context, 0, &bg_color);
2585 gdk_cairo_set_source_rgba (cr, &bg_color);
2588 gtk_notebook_draw_tab (notebook,
2594 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2595 priv->cur_page->tab_label, cr);
2602 gtk_notebook_show_arrows (GtkNotebook *notebook)
2604 GtkNotebookPrivate *priv = notebook->priv;
2605 gboolean show_arrow = FALSE;
2608 if (!priv->scrollable)
2611 children = priv->children;
2614 GtkNotebookPage *page = children->data;
2616 if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2619 children = children->next;
2626 gtk_notebook_get_arrow_rect (GtkNotebook *notebook,
2627 GdkRectangle *rectangle,
2628 GtkNotebookArrow arrow)
2630 GtkNotebookPrivate *priv = notebook->priv;
2631 GdkRectangle event_window_pos;
2632 gboolean before = ARROW_IS_BEFORE (arrow);
2633 gboolean left = ARROW_IS_LEFT (arrow);
2635 if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2637 gint scroll_arrow_hlength;
2638 gint scroll_arrow_vlength;
2640 gtk_widget_style_get (GTK_WIDGET (notebook),
2641 "scroll-arrow-hlength", &scroll_arrow_hlength,
2642 "scroll-arrow-vlength", &scroll_arrow_vlength,
2645 switch (priv->tab_pos)
2649 rectangle->width = scroll_arrow_vlength;
2650 rectangle->height = scroll_arrow_vlength;
2652 if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2653 (!before && (priv->has_after_previous != priv->has_after_next)))
2654 rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2656 rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2658 rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2659 rectangle->y = event_window_pos.y;
2661 rectangle->y += event_window_pos.height - rectangle->height;
2665 case GTK_POS_BOTTOM:
2666 rectangle->width = scroll_arrow_hlength;
2667 rectangle->height = scroll_arrow_hlength;
2671 if (left || !priv->has_before_previous)
2672 rectangle->x = event_window_pos.x;
2674 rectangle->x = event_window_pos.x + rectangle->width;
2678 if (!left || !priv->has_after_next)
2679 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2681 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2683 rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2689 static GtkNotebookArrow
2690 gtk_notebook_get_arrow (GtkNotebook *notebook,
2694 GtkNotebookPrivate *priv = notebook->priv;
2695 GdkRectangle arrow_rect;
2696 GdkRectangle event_window_pos;
2699 GtkNotebookArrow arrow[4];
2701 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2702 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2703 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2704 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2706 if (gtk_notebook_show_arrows (notebook))
2708 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2709 for (i = 0; i < 4; i++)
2711 if (arrow[i] == ARROW_NONE)
2714 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2716 x0 = x - arrow_rect.x;
2717 y0 = y - arrow_rect.y;
2719 if (y0 >= 0 && y0 < arrow_rect.height &&
2720 x0 >= 0 && x0 < arrow_rect.width)
2729 gtk_notebook_do_arrow (GtkNotebook *notebook,
2730 GtkNotebookArrow arrow)
2732 GtkNotebookPrivate *priv = notebook->priv;
2733 GtkWidget *widget = GTK_WIDGET (notebook);
2734 gboolean is_rtl, left;
2736 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2737 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2738 (!ARROW_IS_LEFT (arrow) && is_rtl);
2740 if (!priv->focus_tab ||
2741 gtk_notebook_search_page (notebook, priv->focus_tab,
2742 left ? STEP_PREV : STEP_NEXT,
2745 gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2746 gtk_widget_grab_focus (widget);
2751 gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2752 GtkNotebookArrow arrow,
2755 GtkNotebookPrivate *priv = notebook->priv;
2756 GtkWidget *widget = GTK_WIDGET (notebook);
2757 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2758 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2759 (!ARROW_IS_LEFT (arrow) && is_rtl);
2761 if (!gtk_widget_has_focus (widget))
2762 gtk_widget_grab_focus (widget);
2764 priv->button = button;
2765 priv->click_child = arrow;
2769 gtk_notebook_do_arrow (notebook, arrow);
2770 gtk_notebook_set_scroll_timer (notebook);
2772 else if (button == 2)
2773 gtk_notebook_page_select (notebook, TRUE);
2774 else if (button == 3)
2775 gtk_notebook_switch_focus_tab (notebook,
2776 gtk_notebook_search_page (notebook,
2778 left ? STEP_NEXT : STEP_PREV,
2780 gtk_notebook_redraw_arrows (notebook);
2786 get_widget_coordinates (GtkWidget *widget,
2791 GdkWindow *window = ((GdkEventAny *)event)->window;
2794 if (!gdk_event_get_coords (event, &tx, &ty))
2797 while (window && window != gtk_widget_get_window (widget))
2799 gint window_x, window_y;
2801 gdk_window_get_position (window, &window_x, &window_y);
2805 window = gdk_window_get_parent (window);
2820 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2822 GtkNotebookPrivate *priv = notebook->priv;
2823 GtkNotebookPage *page;
2826 children = priv->children;
2829 page = children->data;
2831 if (gtk_widget_get_visible (page->child) &&
2832 page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2833 (x >= page->allocation.x) &&
2834 (y >= page->allocation.y) &&
2835 (x <= (page->allocation.x + page->allocation.width)) &&
2836 (y <= (page->allocation.y + page->allocation.height)))
2839 children = children->next;
2846 gtk_notebook_button_press (GtkWidget *widget,
2847 GdkEventButton *event)
2849 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2850 GtkNotebookPrivate *priv = notebook->priv;
2851 GtkNotebookPage *page;
2853 GtkNotebookArrow arrow;
2856 if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2860 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2863 arrow = gtk_notebook_get_arrow (notebook, x, y);
2865 return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2867 if (priv->menu && gdk_event_triggers_context_menu ((GdkEvent *) event))
2869 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2870 NULL, NULL, 3, event->time);
2874 if (event->button != 1)
2877 priv->button = event->button;
2879 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2881 gboolean page_changed, was_focus;
2884 page_changed = page != priv->cur_page;
2885 was_focus = gtk_widget_is_focus (widget);
2887 gtk_notebook_switch_focus_tab (notebook, tab);
2888 gtk_widget_grab_focus (widget);
2890 if (page_changed && !was_focus)
2891 gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2893 /* save press to possibly begin a drag */
2894 if (page->reorderable || page->detachable)
2896 priv->during_detach = FALSE;
2897 priv->during_reorder = FALSE;
2898 priv->pressed_button = event->button;
2903 priv->drag_begin_x = priv->mouse_x;
2904 priv->drag_begin_y = priv->mouse_y;
2905 priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2906 priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2914 popup_position_func (GtkMenu *menu,
2920 GtkNotebook *notebook = data;
2921 GtkNotebookPrivate *priv = notebook->priv;
2922 GtkAllocation allocation;
2924 GtkRequisition requisition;
2926 if (priv->focus_tab)
2928 GtkNotebookPage *page;
2930 page = priv->focus_tab->data;
2931 w = page->tab_label;
2935 w = GTK_WIDGET (notebook);
2938 gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2940 gtk_widget_get_allocation (w, &allocation);
2941 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2942 &requisition, NULL);
2944 if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
2945 *x += allocation.x + allocation.width - requisition.width;
2949 *y += allocation.y + allocation.height;
2955 gtk_notebook_popup_menu (GtkWidget *widget)
2957 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2958 GtkNotebookPrivate *priv = notebook->priv;
2962 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2963 popup_position_func, notebook,
2964 0, gtk_get_current_event_time ());
2965 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
2973 stop_scrolling (GtkNotebook *notebook)
2975 GtkNotebookPrivate *priv = notebook->priv;
2979 g_source_remove (priv->timer);
2981 priv->need_timer = FALSE;
2983 priv->click_child = 0;
2985 gtk_notebook_redraw_arrows (notebook);
2989 get_drop_position (GtkNotebook *notebook)
2991 GtkNotebookPrivate *priv = notebook->priv;
2992 GList *children, *last_child;
2993 GtkNotebookPage *page;
3000 is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
3001 children = priv->children;
3006 page = children->data;
3008 if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
3009 gtk_widget_get_visible (page->child) &&
3011 gtk_widget_get_mapped (page->tab_label))
3013 switch (priv->tab_pos)
3016 case GTK_POS_BOTTOM:
3019 if (PAGE_MIDDLE_X (page) > x)
3024 if (PAGE_MIDDLE_X (page) < x)
3031 if (PAGE_MIDDLE_Y (page) > y)
3037 last_child = children->next;
3040 children = children->next;
3047 show_drag_window (GtkNotebook *notebook,
3048 GtkNotebookPrivate *priv,
3049 GtkNotebookPage *page,
3052 GtkWidget *widget = GTK_WIDGET (notebook);
3054 if (!priv->drag_window)
3056 GdkWindowAttr attributes;
3057 guint attributes_mask;
3059 attributes.x = page->allocation.x;
3060 attributes.y = page->allocation.y;
3061 attributes.width = page->allocation.width;
3062 attributes.height = page->allocation.height;
3063 attributes.window_type = GDK_WINDOW_CHILD;
3064 attributes.wclass = GDK_INPUT_OUTPUT;
3065 attributes.visual = gtk_widget_get_visual (widget);
3066 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3067 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3069 priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
3072 gdk_window_set_user_data (priv->drag_window, widget);
3075 g_object_ref (page->tab_label);
3076 gtk_widget_unparent (page->tab_label);
3077 gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
3078 gtk_widget_set_parent (page->tab_label, widget);
3079 g_object_unref (page->tab_label);
3081 gdk_window_show (priv->drag_window);
3083 /* the grab will dissapear when the window is hidden */
3084 gdk_device_grab (device, priv->drag_window,
3085 GDK_OWNERSHIP_WINDOW, FALSE,
3086 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3087 NULL, GDK_CURRENT_TIME);
3090 /* This function undoes the reparenting that happens both when drag_window
3091 * is shown for reordering and when the DnD icon is shown for detaching
3094 hide_drag_window (GtkNotebook *notebook,
3095 GtkNotebookPrivate *priv,
3096 GtkNotebookPage *page)
3098 GtkWidget *widget = GTK_WIDGET (notebook);
3099 GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
3101 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
3102 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
3104 g_object_ref (page->tab_label);
3106 if (GTK_IS_WINDOW (parent))
3108 /* parent widget is the drag window */
3109 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
3112 gtk_widget_unparent (page->tab_label);
3114 gtk_widget_set_parent (page->tab_label, widget);
3115 g_object_unref (page->tab_label);
3118 if (priv->drag_window &&
3119 gdk_window_is_visible (priv->drag_window))
3120 gdk_window_hide (priv->drag_window);
3124 gtk_notebook_stop_reorder (GtkNotebook *notebook)
3126 GtkNotebookPrivate *priv = notebook->priv;
3127 GtkNotebookPage *page;
3129 if (priv->operation == DRAG_OPERATION_DETACH)
3130 page = priv->detached_tab;
3132 page = priv->cur_page;
3134 if (!page || !page->tab_label)
3137 priv->pressed_button = -1;
3139 if (page->reorderable || page->detachable)
3141 if (priv->during_reorder)
3143 gint old_page_num, page_num;
3146 element = get_drop_position (notebook);
3147 old_page_num = g_list_position (priv->children, priv->focus_tab);
3148 page_num = reorder_tab (notebook, element, priv->focus_tab);
3149 gtk_notebook_child_reordered (notebook, page);
3151 if (priv->has_scrolled || old_page_num != page_num)
3152 g_signal_emit (notebook,
3153 notebook_signals[PAGE_REORDERED], 0,
3154 page->child, page_num);
3156 priv->has_scrolled = FALSE;
3157 priv->during_reorder = FALSE;
3160 hide_drag_window (notebook, priv, page);
3162 priv->operation = DRAG_OPERATION_NONE;
3163 gtk_notebook_pages_allocate (notebook);
3165 if (priv->dnd_timer)
3167 g_source_remove (priv->dnd_timer);
3168 priv->dnd_timer = 0;
3174 gtk_notebook_button_release (GtkWidget *widget,
3175 GdkEventButton *event)
3177 GtkNotebook *notebook;
3178 GtkNotebookPrivate *priv;
3179 GtkNotebookPage *page;
3181 if (event->type != GDK_BUTTON_RELEASE)
3184 notebook = GTK_NOTEBOOK (widget);
3185 priv = notebook->priv;
3187 page = priv->cur_page;
3189 if (!priv->during_detach &&
3190 page->reorderable &&
3191 event->button == priv->pressed_button)
3192 gtk_notebook_stop_reorder (notebook);
3194 if (event->button == priv->button)
3196 stop_scrolling (notebook);
3204 gtk_notebook_leave_notify (GtkWidget *widget,
3205 GdkEventCrossing *event)
3207 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3208 GtkNotebookPrivate *priv = notebook->priv;
3211 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
3217 gtk_notebook_redraw_arrows (notebook);
3223 static GtkNotebookPointerPosition
3224 get_pointer_position (GtkNotebook *notebook)
3226 GtkNotebookPrivate *priv = notebook->priv;
3227 GtkWidget *widget = GTK_WIDGET (notebook);
3228 gint wx, wy, width, height;
3231 if (!priv->scrollable)
3232 return POINTER_BETWEEN;
3234 gdk_window_get_position (priv->event_window, &wx, &wy);
3235 width = gdk_window_get_width (priv->event_window);
3236 height = gdk_window_get_height (priv->event_window);
3238 if (priv->tab_pos == GTK_POS_TOP ||
3239 priv->tab_pos == GTK_POS_BOTTOM)
3243 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3244 x = priv->mouse_x - wx;
3246 if (x > width - SCROLL_THRESHOLD)
3247 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
3248 else if (x < SCROLL_THRESHOLD)
3249 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
3251 return POINTER_BETWEEN;
3257 y = priv->mouse_y - wy;
3258 if (y > height - SCROLL_THRESHOLD)
3259 return POINTER_AFTER;
3260 else if (y < SCROLL_THRESHOLD)
3261 return POINTER_BEFORE;
3263 return POINTER_BETWEEN;
3268 scroll_notebook_timer (gpointer data)
3270 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3271 GtkNotebookPrivate *priv = notebook->priv;
3272 GtkNotebookPointerPosition pointer_position;
3273 GList *element, *first_tab;
3275 pointer_position = get_pointer_position (notebook);
3277 element = get_drop_position (notebook);
3278 reorder_tab (notebook, element, priv->focus_tab);
3279 first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3280 (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3284 priv->first_tab = first_tab;
3285 gtk_notebook_pages_allocate (notebook);
3287 gdk_window_move_resize (priv->drag_window,
3288 priv->drag_window_x,
3289 priv->drag_window_y,
3290 priv->cur_page->allocation.width,
3291 priv->cur_page->allocation.height);
3292 gdk_window_raise (priv->drag_window);
3299 check_threshold (GtkNotebook *notebook,
3303 GtkNotebookPrivate *priv = notebook->priv;
3305 GdkRectangle rectangle = { 0, }; /* shut up gcc */
3306 GtkSettings *settings;
3308 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3309 g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3311 /* we want a large threshold */
3312 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3314 gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3315 rectangle.width = gdk_window_get_width (priv->event_window);
3316 rectangle.height = gdk_window_get_height (priv->event_window);
3318 rectangle.x -= dnd_threshold;
3319 rectangle.width += 2 * dnd_threshold;
3320 rectangle.y -= dnd_threshold;
3321 rectangle.height += 2 * dnd_threshold;
3323 return (current_x < rectangle.x ||
3324 current_x > rectangle.x + rectangle.width ||
3325 current_y < rectangle.y ||
3326 current_y > rectangle.y + rectangle.height);
3330 gtk_notebook_motion_notify (GtkWidget *widget,
3331 GdkEventMotion *event)
3333 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3334 GtkNotebookPrivate *priv = notebook->priv;
3335 GtkNotebookPage *page;
3336 GtkNotebookArrow arrow;
3337 GtkNotebookPointerPosition pointer_position;
3338 GtkSettings *settings;
3342 page = priv->cur_page;
3347 if (!(event->state & GDK_BUTTON1_MASK) &&
3348 priv->pressed_button != -1)
3350 gtk_notebook_stop_reorder (notebook);
3351 stop_scrolling (notebook);
3354 if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3357 priv->timestamp = event->time;
3359 /* While animating the move, event->x is relative to the flying tab
3360 * (priv->drag_window has a pointer grab), but we need coordinates relative to
3361 * the notebook widget.
3363 gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3364 priv->mouse_x = event->x_root - x_win;
3365 priv->mouse_y = event->y_root - y_win;
3367 arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3368 if (arrow != priv->in_child)
3370 priv->in_child = arrow;
3371 gtk_notebook_redraw_arrows (notebook);
3374 if (priv->pressed_button == -1)
3377 if (page->detachable &&
3378 check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3380 priv->detached_tab = priv->cur_page;
3381 priv->during_detach = TRUE;
3383 gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3384 priv->pressed_button, (GdkEvent*) event);
3388 if (page->reorderable &&
3389 (priv->during_reorder ||
3390 gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3392 priv->during_reorder = TRUE;
3393 pointer_position = get_pointer_position (notebook);
3395 if (event->window == priv->drag_window &&
3396 pointer_position != POINTER_BETWEEN &&
3397 gtk_notebook_show_arrows (notebook))
3400 if (!priv->dnd_timer)
3402 priv->has_scrolled = TRUE;
3403 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3404 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3406 priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3407 scroll_notebook_timer,
3408 (gpointer) notebook);
3413 if (priv->dnd_timer)
3415 g_source_remove (priv->dnd_timer);
3416 priv->dnd_timer = 0;
3420 if (event->window == priv->drag_window ||
3421 priv->operation != DRAG_OPERATION_REORDER)
3423 /* the drag operation is beginning, create the window */
3424 if (priv->operation != DRAG_OPERATION_REORDER)
3426 priv->operation = DRAG_OPERATION_REORDER;
3427 show_drag_window (notebook, priv, page, event->device);
3430 gtk_notebook_pages_allocate (notebook);
3431 gdk_window_move_resize (priv->drag_window,
3432 priv->drag_window_x,
3433 priv->drag_window_y,
3434 page->allocation.width,
3435 page->allocation.height);
3443 gtk_notebook_grab_notify (GtkWidget *widget,
3444 gboolean was_grabbed)
3446 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3450 gtk_notebook_stop_reorder (notebook);
3451 stop_scrolling (notebook);
3456 gtk_notebook_state_flags_changed (GtkWidget *widget,
3457 GtkStateFlags previous_state)
3459 if (!gtk_widget_is_sensitive (widget))
3460 stop_scrolling (GTK_NOTEBOOK (widget));
3464 gtk_notebook_focus_in (GtkWidget *widget,
3465 GdkEventFocus *event)
3467 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3473 gtk_notebook_focus_out (GtkWidget *widget,
3474 GdkEventFocus *event)
3476 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3482 gtk_notebook_style_updated (GtkWidget *widget)
3484 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3485 GtkNotebookPrivate *priv = notebook->priv;
3487 gboolean has_before_previous;
3488 gboolean has_before_next;
3489 gboolean has_after_previous;
3490 gboolean has_after_next;
3492 gtk_widget_style_get (widget,
3493 "has-backward-stepper", &has_before_previous,
3494 "has-secondary-forward-stepper", &has_before_next,
3495 "has-secondary-backward-stepper", &has_after_previous,
3496 "has-forward-stepper", &has_after_next,
3499 priv->has_before_previous = has_before_previous;
3500 priv->has_before_next = has_before_next;
3501 priv->has_after_previous = has_after_previous;
3502 priv->has_after_next = has_after_next;
3504 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_updated (widget);
3508 on_drag_icon_draw (GtkWidget *widget,
3512 GtkWidget *notebook, *child;
3513 GtkRequisition requisition;
3514 GtkStyleContext *context;
3517 notebook = GTK_WIDGET (data);
3518 child = gtk_bin_get_child (GTK_BIN (widget));
3519 context = gtk_widget_get_style_context (widget);
3521 gtk_style_context_save (context);
3522 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB, 0);
3524 gtk_widget_get_preferred_size (widget,
3525 &requisition, NULL);
3526 gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3528 gtk_render_extension (context, cr, 0, 0,
3529 requisition.width, requisition.height,
3533 gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3535 gtk_style_context_restore (context);
3541 gtk_notebook_drag_begin (GtkWidget *widget,
3542 GdkDragContext *context)
3544 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3545 GtkNotebookPrivate *priv = notebook->priv;
3546 GtkWidget *tab_label;
3548 if (priv->dnd_timer)
3550 g_source_remove (priv->dnd_timer);
3551 priv->dnd_timer = 0;
3554 priv->operation = DRAG_OPERATION_DETACH;
3555 gtk_notebook_pages_allocate (notebook);
3557 tab_label = priv->detached_tab->tab_label;
3559 hide_drag_window (notebook, priv, priv->cur_page);
3560 g_object_ref (tab_label);
3561 gtk_widget_unparent (tab_label);
3563 priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3564 gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3565 gtk_widget_get_screen (widget));
3566 gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3567 gtk_widget_set_size_request (priv->dnd_window,
3568 priv->detached_tab->allocation.width,
3569 priv->detached_tab->allocation.height);
3570 g_object_unref (tab_label);
3572 g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3573 G_CALLBACK (on_drag_icon_draw), notebook);
3575 gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3579 gtk_notebook_drag_end (GtkWidget *widget,
3580 GdkDragContext *context)
3582 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3583 GtkNotebookPrivate *priv = notebook->priv;
3585 gtk_notebook_stop_reorder (notebook);
3587 if (priv->detached_tab)
3588 gtk_notebook_switch_page (notebook, priv->detached_tab);
3590 _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3591 gtk_widget_destroy (priv->dnd_window);
3592 priv->dnd_window = NULL;
3594 priv->operation = DRAG_OPERATION_NONE;
3597 static GtkNotebook *
3598 gtk_notebook_create_window (GtkNotebook *notebook,
3607 gtk_notebook_drag_failed (GtkWidget *widget,
3608 GdkDragContext *context,
3609 GtkDragResult result)
3611 if (result == GTK_DRAG_RESULT_NO_TARGET)
3613 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3614 GtkNotebookPrivate *priv = notebook->priv;
3615 GtkNotebook *dest_notebook = NULL;
3618 gdk_device_get_position (gdk_drag_context_get_device (context),
3621 g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3622 priv->detached_tab->child, x, y, &dest_notebook);
3625 do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3634 gtk_notebook_switch_tab_timeout (gpointer data)
3636 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3637 GtkNotebookPrivate *priv = notebook->priv;
3641 priv->switch_tab_timer = 0;
3645 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
3647 /* FIXME: hack, we don't want the
3648 * focus to move fom the source widget
3650 priv->child_has_focus = FALSE;
3651 gtk_notebook_switch_focus_tab (notebook, tab);
3658 gtk_notebook_drag_motion (GtkWidget *widget,
3659 GdkDragContext *context,
3664 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3665 GtkNotebookPrivate *priv = notebook->priv;
3666 GtkAllocation allocation;
3667 GdkRectangle position;
3668 GtkSettings *settings;
3669 GtkNotebookArrow arrow;
3671 GdkAtom target, tab_target;
3673 gtk_widget_get_allocation (widget, &allocation);
3675 arrow = gtk_notebook_get_arrow (notebook,
3680 priv->click_child = arrow;
3681 gtk_notebook_set_scroll_timer (notebook);
3682 gdk_drag_status (context, 0, time);
3686 stop_scrolling (notebook);
3687 target = gtk_drag_dest_find_target (widget, context, NULL);
3688 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3690 if (target == tab_target)
3692 GQuark group, source_group;
3693 GtkNotebook *source;
3694 GtkWidget *source_child;
3696 source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3697 source_child = source->priv->cur_page->child;
3699 group = notebook->priv->group;
3700 source_group = source->priv->group;
3702 if (group != 0 && group == source_group &&
3703 !(widget == source_child ||
3704 gtk_widget_is_ancestor (widget, source_child)))
3706 gdk_drag_status (context, GDK_ACTION_MOVE, time);
3711 /* it's a tab, but doesn't share
3712 * ID with this notebook */
3713 gdk_drag_status (context, 0, time);
3720 if (gtk_notebook_get_event_window_position (notebook, &position) &&
3721 x >= position.x && x <= position.x + position.width &&
3722 y >= position.y && y <= position.y + position.height)
3727 if (!priv->switch_tab_timer)
3729 settings = gtk_widget_get_settings (widget);
3731 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3732 priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3733 gtk_notebook_switch_tab_timeout,
3739 if (priv->switch_tab_timer)
3741 g_source_remove (priv->switch_tab_timer);
3742 priv->switch_tab_timer = 0;
3746 return (target == tab_target) ? TRUE : FALSE;
3750 gtk_notebook_drag_leave (GtkWidget *widget,
3751 GdkDragContext *context,
3754 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3755 GtkNotebookPrivate *priv = notebook->priv;
3757 if (priv->switch_tab_timer)
3759 g_source_remove (priv->switch_tab_timer);
3760 priv->switch_tab_timer = 0;
3763 stop_scrolling (GTK_NOTEBOOK (widget));
3767 gtk_notebook_drag_drop (GtkWidget *widget,
3768 GdkDragContext *context,
3773 GdkAtom target, tab_target;
3775 target = gtk_drag_dest_find_target (widget, context, NULL);
3776 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3778 if (target == tab_target)
3780 gtk_drag_get_data (widget, context, target, time);
3788 do_detach_tab (GtkNotebook *from,
3794 GtkNotebookPrivate *to_priv = to->priv;
3795 GtkAllocation to_allocation;
3796 GtkWidget *tab_label, *menu_label;
3797 gboolean tab_expand, tab_fill, reorderable, detachable;
3801 menu_label = gtk_notebook_get_menu_label (from, child);
3804 g_object_ref (menu_label);
3806 tab_label = gtk_notebook_get_tab_label (from, child);
3809 g_object_ref (tab_label);
3811 g_object_ref (child);
3813 gtk_container_child_get (GTK_CONTAINER (from),
3815 "tab-expand", &tab_expand,
3816 "tab-fill", &tab_fill,
3817 "reorderable", &reorderable,
3818 "detachable", &detachable,
3821 gtk_container_remove (GTK_CONTAINER (from), child);
3823 gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3824 to_priv->mouse_x = x + to_allocation.x;
3825 to_priv->mouse_y = y + to_allocation.y;
3827 element = get_drop_position (to);
3828 page_num = g_list_position (to_priv->children, element);
3829 gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3831 gtk_container_child_set (GTK_CONTAINER (to), child,
3832 "tab-expand", tab_expand,
3833 "tab-fill", tab_fill,
3834 "reorderable", reorderable,
3835 "detachable", detachable,
3838 g_object_unref (child);
3841 g_object_unref (tab_label);
3844 g_object_unref (menu_label);
3846 gtk_notebook_set_current_page (to, page_num);
3850 gtk_notebook_drag_data_get (GtkWidget *widget,
3851 GdkDragContext *context,
3852 GtkSelectionData *data,
3858 target = gtk_selection_data_get_target (data);
3859 if (target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3861 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3862 GtkNotebookPrivate *priv = notebook->priv;
3864 gtk_selection_data_set (data,
3867 (void*) &priv->detached_tab->child,
3873 gtk_notebook_drag_data_received (GtkWidget *widget,
3874 GdkDragContext *context,
3877 GtkSelectionData *data,
3881 GtkNotebook *notebook;
3882 GtkWidget *source_widget;
3885 notebook = GTK_NOTEBOOK (widget);
3886 source_widget = gtk_drag_get_source_widget (context);
3888 if (source_widget &&
3889 gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3891 child = (void*) gtk_selection_data_get_data (data);
3893 do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3894 gtk_drag_finish (context, TRUE, FALSE, time);
3897 gtk_drag_finish (context, FALSE, FALSE, time);
3900 /* Private GtkContainer Methods :
3902 * gtk_notebook_set_child_arg
3903 * gtk_notebook_get_child_arg
3905 * gtk_notebook_remove
3906 * gtk_notebook_focus
3907 * gtk_notebook_set_focus_child
3908 * gtk_notebook_child_type
3909 * gtk_notebook_forall
3912 gtk_notebook_set_child_property (GtkContainer *container,
3915 const GValue *value,
3921 /* not finding child's page is valid for menus or labels */
3922 if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3925 switch (property_id)
3927 case CHILD_PROP_TAB_LABEL:
3928 /* a NULL pointer indicates a default_tab setting, otherwise
3929 * we need to set the associated label
3931 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
3932 g_value_get_string (value));
3934 case CHILD_PROP_MENU_LABEL:
3935 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
3936 g_value_get_string (value));
3938 case CHILD_PROP_POSITION:
3939 gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
3940 g_value_get_int (value));
3942 case CHILD_PROP_TAB_EXPAND:
3943 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3945 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3946 g_value_get_boolean (value),
3949 case CHILD_PROP_TAB_FILL:
3950 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3952 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3954 g_value_get_boolean (value));
3956 case CHILD_PROP_REORDERABLE:
3957 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
3958 g_value_get_boolean (value));
3960 case CHILD_PROP_DETACHABLE:
3961 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
3962 g_value_get_boolean (value));
3965 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3971 gtk_notebook_get_child_property (GtkContainer *container,
3977 GtkNotebook *notebook = GTK_NOTEBOOK (container);
3978 GtkNotebookPrivate *priv = notebook->priv;
3984 /* not finding child's page is valid for menus or labels */
3985 list = gtk_notebook_find_child (notebook, child, NULL);
3988 /* nothing to set on labels or menus */
3989 g_param_value_set_default (pspec, value);
3993 switch (property_id)
3995 case CHILD_PROP_TAB_LABEL:
3996 label = gtk_notebook_get_tab_label (notebook, child);
3998 if (GTK_IS_LABEL (label))
3999 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4001 g_value_set_string (value, NULL);
4003 case CHILD_PROP_MENU_LABEL:
4004 label = gtk_notebook_get_menu_label (notebook, child);
4006 if (GTK_IS_LABEL (label))
4007 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4009 g_value_set_string (value, NULL);
4011 case CHILD_PROP_POSITION:
4012 g_value_set_int (value, g_list_position (priv->children, list));
4014 case CHILD_PROP_TAB_EXPAND:
4015 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4017 g_value_set_boolean (value, expand);
4019 case CHILD_PROP_TAB_FILL:
4020 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4022 g_value_set_boolean (value, fill);
4024 case CHILD_PROP_REORDERABLE:
4025 g_value_set_boolean (value,
4026 gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
4028 case CHILD_PROP_DETACHABLE:
4029 g_value_set_boolean (value,
4030 gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
4033 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
4039 gtk_notebook_add (GtkContainer *container,
4042 gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget,
4047 gtk_notebook_remove (GtkContainer *container,
4050 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4051 GtkNotebookPrivate *priv = notebook->priv;
4052 GtkNotebookPage *page;
4056 children = priv->children;
4059 page = children->data;
4061 if (page->child == widget)
4065 children = children->next;
4068 if (children == NULL)
4071 g_object_ref (widget);
4073 gtk_notebook_real_remove (notebook, children);
4075 g_signal_emit (notebook,
4076 notebook_signals[PAGE_REMOVED],
4081 g_object_unref (widget);
4085 focus_tabs_in (GtkNotebook *notebook)
4087 GtkNotebookPrivate *priv = notebook->priv;
4089 if (priv->show_tabs && priv->cur_page)
4091 gtk_widget_grab_focus (GTK_WIDGET (notebook));
4093 gtk_notebook_switch_focus_tab (notebook,
4094 g_list_find (priv->children,
4104 focus_tabs_move (GtkNotebook *notebook,
4105 GtkDirectionType direction,
4106 gint search_direction)
4108 GtkNotebookPrivate *priv = notebook->priv;
4111 new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
4112 search_direction, TRUE);
4115 gboolean wrap_around;
4117 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
4118 "gtk-keynav-wrap-around", &wrap_around,
4122 new_page = gtk_notebook_search_page (notebook, NULL,
4123 search_direction, TRUE);
4127 gtk_notebook_switch_focus_tab (notebook, new_page);
4129 gtk_widget_error_bell (GTK_WIDGET (notebook));
4135 focus_child_in (GtkNotebook *notebook,
4136 GtkDirectionType direction)
4138 GtkNotebookPrivate *priv = notebook->priv;
4141 return gtk_widget_child_focus (priv->cur_page->child, direction);
4147 focus_action_in (GtkNotebook *notebook,
4149 GtkDirectionType direction)
4151 GtkNotebookPrivate *priv = notebook->priv;
4153 if (priv->action_widget[action] &&
4154 gtk_widget_get_visible (priv->action_widget[action]))
4155 return gtk_widget_child_focus (priv->action_widget[action], direction);
4160 /* Focus in the notebook can either be on the pages, or on
4161 * the tabs or on the action_widgets.
4164 gtk_notebook_focus (GtkWidget *widget,
4165 GtkDirectionType direction)
4167 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
4168 GtkNotebookPrivate *priv = notebook->priv;
4169 GtkWidget *old_focus_child;
4170 GtkDirectionType effective_direction;
4174 gboolean widget_is_focus;
4175 GtkContainer *container;
4177 container = GTK_CONTAINER (widget);
4179 if (priv->tab_pos == GTK_POS_TOP ||
4180 priv->tab_pos == GTK_POS_LEFT)
4182 first_action = ACTION_WIDGET_START;
4183 last_action = ACTION_WIDGET_END;
4187 first_action = ACTION_WIDGET_END;
4188 last_action = ACTION_WIDGET_START;
4191 if (priv->focus_out)
4193 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
4197 widget_is_focus = gtk_widget_is_focus (widget);
4198 old_focus_child = gtk_container_get_focus_child (container);
4200 effective_direction = get_effective_direction (notebook, direction);
4202 if (old_focus_child) /* Focus on page child or action widget */
4204 if (gtk_widget_child_focus (old_focus_child, direction))
4207 if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
4209 switch (effective_direction)
4212 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4214 return focus_tabs_in (notebook);
4222 case GTK_DIR_TAB_FORWARD:
4223 if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
4224 focus_child_in (notebook, direction))
4226 return focus_tabs_in (notebook);
4227 case GTK_DIR_TAB_BACKWARD:
4230 g_assert_not_reached ();
4234 else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
4236 switch (effective_direction)
4239 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4243 return focus_tabs_in (notebook);
4249 case GTK_DIR_TAB_FORWARD:
4251 case GTK_DIR_TAB_BACKWARD:
4252 if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
4253 focus_child_in (notebook, direction))
4255 return focus_tabs_in (notebook);
4257 g_assert_not_reached ();
4263 switch (effective_direction)
4265 case GTK_DIR_TAB_BACKWARD:
4267 /* Focus onto the tabs */
4268 return focus_tabs_in (notebook);
4273 case GTK_DIR_TAB_FORWARD:
4274 return focus_action_in (notebook, last_action, direction);
4278 else if (widget_is_focus) /* Focus was on tabs */
4280 switch (effective_direction)
4282 case GTK_DIR_TAB_BACKWARD:
4283 return focus_action_in (notebook, first_action, direction);
4286 case GTK_DIR_TAB_FORWARD:
4287 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4289 return focus_action_in (notebook, last_action, direction);
4291 /* We use TAB_FORWARD rather than direction so that we focus a more
4292 * predictable widget for the user; users may be using arrow focusing
4293 * in this situation even if they don't usually use arrow focusing.
4295 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4297 return focus_tabs_move (notebook, direction, STEP_PREV);
4299 return focus_tabs_move (notebook, direction, STEP_NEXT);
4302 else /* Focus was not on widget */
4304 switch (effective_direction)
4306 case GTK_DIR_TAB_FORWARD:
4308 if (focus_action_in (notebook, first_action, direction))
4310 if (focus_tabs_in (notebook))
4312 if (focus_action_in (notebook, last_action, direction))
4314 if (focus_child_in (notebook, direction))
4317 case GTK_DIR_TAB_BACKWARD:
4318 if (focus_action_in (notebook, last_action, direction))
4320 if (focus_child_in (notebook, direction))
4322 if (focus_tabs_in (notebook))
4324 if (focus_action_in (notebook, first_action, direction))
4329 return focus_child_in (notebook, direction);
4333 g_assert_not_reached ();
4338 gtk_notebook_set_focus_child (GtkContainer *container,
4341 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4342 GtkNotebookPrivate *priv = notebook->priv;
4343 GtkWidget *page_child;
4344 GtkWidget *toplevel;
4346 /* If the old focus widget was within a page of the notebook,
4347 * (child may either be NULL or not in this case), record it
4348 * for future use if we switch to the page with a mnemonic.
4351 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4352 if (toplevel && gtk_widget_is_toplevel (toplevel))
4354 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4357 if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4359 GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4362 GtkNotebookPage *page = list->data;
4364 if (page->last_focus_child)
4365 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4367 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4368 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4374 page_child = gtk_widget_get_parent (page_child);
4380 g_return_if_fail (GTK_IS_WIDGET (child));
4382 priv->child_has_focus = TRUE;
4383 if (!priv->focus_tab)
4386 GtkNotebookPage *page;
4388 children = priv->children;
4391 page = children->data;
4392 if (page->child == child || page->tab_label == child)
4393 gtk_notebook_switch_focus_tab (notebook, children);
4394 children = children->next;
4399 priv->child_has_focus = FALSE;
4401 GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4405 gtk_notebook_forall (GtkContainer *container,
4406 gboolean include_internals,
4407 GtkCallback callback,
4408 gpointer callback_data)
4410 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4411 GtkNotebookPrivate *priv = notebook->priv;
4415 children = priv->children;
4418 GtkNotebookPage *page;
4420 page = children->data;
4421 children = children->next;
4422 (* callback) (page->child, callback_data);
4424 if (include_internals)
4426 if (page->tab_label)
4427 (* callback) (page->tab_label, callback_data);
4431 if (include_internals) {
4432 for (i = 0; i < N_ACTION_WIDGETS; i++)
4434 if (priv->action_widget[i])
4435 (* callback) (priv->action_widget[i], callback_data);
4440 static GtkWidgetPath *
4441 gtk_notebook_get_path_for_child (GtkContainer *container,
4444 GtkNotebookPrivate *priv;
4445 GtkNotebook *notebook;
4446 GtkNotebookPage *page;
4447 GtkWidgetPath *path;
4450 path = GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->get_path_for_child (container, widget);
4452 notebook = GTK_NOTEBOOK (container);
4453 priv = notebook->priv;
4455 for (c = priv->children; c; c = c->next)
4459 if (page->tab_label == widget)
4463 /* Widget is not a tab label */
4467 gtk_widget_path_iter_add_region (path,
4468 gtk_widget_path_length (path) - 2,
4469 GTK_STYLE_REGION_TAB,
4470 _gtk_notebook_get_tab_flags (notebook, page));
4476 gtk_notebook_child_type (GtkContainer *container)
4478 return GTK_TYPE_WIDGET;
4481 /* Private GtkNotebook Methods:
4483 * gtk_notebook_real_insert_page
4486 page_visible_cb (GtkWidget *page,
4490 GtkNotebook *notebook = GTK_NOTEBOOK (data);
4491 GtkNotebookPrivate *priv = notebook->priv;
4495 if (priv->cur_page &&
4496 priv->cur_page->child == page &&
4497 !gtk_widget_get_visible (page))
4499 list = g_list_find (priv->children, priv->cur_page);
4502 next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4504 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4508 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4513 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4515 GtkWidget *tab_label,
4516 GtkWidget *menu_label,
4519 GtkNotebookPrivate *priv = notebook->priv;
4520 GtkNotebookPage *page;
4523 gtk_widget_freeze_child_notify (child);
4525 page = g_slice_new0 (GtkNotebookPage);
4526 page->child = child;
4528 nchildren = g_list_length (priv->children);
4529 if ((position < 0) || (position > nchildren))
4530 position = nchildren;
4532 priv->children = g_list_insert (priv->children, page, position);
4536 page->default_tab = TRUE;
4538 page->tab_label = tab_label;
4539 page->menu_label = menu_label;
4540 page->expand = FALSE;
4544 page->default_menu = TRUE;
4546 g_object_ref_sink (page->menu_label);
4549 gtk_notebook_menu_item_create (notebook,
4550 g_list_find (priv->children, page));
4552 /* child visible will be turned on by switch_page below */
4553 if (priv->cur_page != page)
4554 gtk_widget_set_child_visible (child, FALSE);
4556 gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4558 gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4560 gtk_notebook_update_labels (notebook);
4562 if (!priv->first_tab)
4563 priv->first_tab = priv->children;
4567 if (priv->show_tabs && gtk_widget_get_visible (child))
4568 gtk_widget_show (tab_label);
4570 gtk_widget_hide (tab_label);
4572 page->mnemonic_activate_signal =
4573 g_signal_connect (tab_label,
4574 "mnemonic-activate",
4575 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4579 page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4580 G_CALLBACK (page_visible_cb), notebook);
4582 g_signal_emit (notebook,
4583 notebook_signals[PAGE_ADDED],
4588 if (!priv->cur_page)
4590 gtk_notebook_switch_page (notebook, page);
4591 /* focus_tab is set in the switch_page method */
4592 gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4595 gtk_notebook_update_tab_states (notebook);
4597 if (priv->scrollable)
4598 gtk_notebook_redraw_arrows (notebook);
4600 gtk_widget_child_notify (child, "tab-expand");
4601 gtk_widget_child_notify (child, "tab-fill");
4602 gtk_widget_child_notify (child, "tab-label");
4603 gtk_widget_child_notify (child, "menu-label");
4604 gtk_widget_child_notify (child, "position");
4605 gtk_widget_thaw_child_notify (child);
4607 /* The page-added handler might have reordered the pages, re-get the position */
4608 return gtk_notebook_page_num (notebook, child);
4611 /* Private GtkNotebook Functions:
4613 * gtk_notebook_redraw_tabs
4614 * gtk_notebook_real_remove
4615 * gtk_notebook_update_labels
4616 * gtk_notebook_timer
4617 * gtk_notebook_set_scroll_timer
4618 * gtk_notebook_page_compare
4619 * gtk_notebook_search_page
4622 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4624 GtkNotebookPrivate *priv = notebook->priv;
4625 GtkAllocation allocation;
4627 GtkNotebookPage *page;
4628 GtkStyleContext *context;
4629 GdkRectangle redraw_rect;
4631 gint tab_pos = get_effective_tab_pos (notebook);
4634 widget = GTK_WIDGET (notebook);
4635 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4637 if (!gtk_widget_get_mapped (widget) || !priv->first_tab)
4640 page = priv->first_tab->data;
4642 redraw_rect.x = border;
4643 redraw_rect.y = border;
4645 gtk_widget_get_allocation (widget, &allocation);
4647 context = gtk_widget_get_style_context (widget);
4648 gtk_style_context_get_padding (context, 0, &padding);
4652 case GTK_POS_BOTTOM:
4653 redraw_rect.y = allocation.height - border -
4654 page->allocation.height - padding.bottom;
4656 if (page != priv->cur_page)
4657 redraw_rect.y -= padding.bottom;
4660 redraw_rect.width = allocation.width - 2 * border;
4661 redraw_rect.height = page->allocation.height + padding.top;
4663 if (page != priv->cur_page)
4664 redraw_rect.height += padding.top;
4667 redraw_rect.x = allocation.width - border -
4668 page->allocation.width - padding.right;
4670 if (page != priv->cur_page)
4671 redraw_rect.x -= padding.right;
4674 redraw_rect.width = page->allocation.width + padding.left;
4675 redraw_rect.height = allocation.height - 2 * border;
4677 if (page != priv->cur_page)
4678 redraw_rect.width += padding.left;
4682 redraw_rect.x += allocation.x;
4683 redraw_rect.y += allocation.y;
4685 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4686 &redraw_rect, TRUE);
4690 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4692 GtkNotebookPrivate *priv = notebook->priv;
4694 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4695 gtk_notebook_show_arrows (notebook))
4699 GtkNotebookArrow arrow[4];
4701 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4702 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4703 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4704 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4706 for (i = 0; i < 4; i++)
4708 if (arrow[i] == ARROW_NONE)
4711 gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4712 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4719 gtk_notebook_timer (GtkNotebook *notebook)
4721 GtkNotebookPrivate *priv = notebook->priv;
4722 gboolean retval = FALSE;
4726 gtk_notebook_do_arrow (notebook, priv->click_child);
4728 if (priv->need_timer)
4730 GtkSettings *settings;
4733 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4734 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4736 priv->need_timer = FALSE;
4737 priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4738 (GSourceFunc) gtk_notebook_timer,
4739 (gpointer) notebook);
4749 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4751 GtkNotebookPrivate *priv = notebook->priv;
4752 GtkWidget *widget = GTK_WIDGET (notebook);
4756 GtkSettings *settings = gtk_widget_get_settings (widget);
4759 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4761 priv->timer = gdk_threads_add_timeout (timeout,
4762 (GSourceFunc) gtk_notebook_timer,
4763 (gpointer) notebook);
4764 priv->need_timer = TRUE;
4769 gtk_notebook_page_compare (gconstpointer a,
4772 return (((GtkNotebookPage *) a)->child != b);
4776 gtk_notebook_find_child (GtkNotebook *notebook,
4778 const gchar *function)
4780 GtkNotebookPrivate *priv = notebook->priv;
4781 GList *list = g_list_find_custom (priv->children, child,
4782 gtk_notebook_page_compare);
4784 #ifndef G_DISABLE_CHECKS
4785 if (!list && function)
4786 g_warning ("%s: unable to find child %p in notebook %p",
4787 function, child, notebook);
4794 gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4795 GtkNotebookPage *page)
4797 if (page->tab_label)
4799 if (page->mnemonic_activate_signal)
4800 g_signal_handler_disconnect (page->tab_label,
4801 page->mnemonic_activate_signal);
4802 page->mnemonic_activate_signal = 0;
4804 gtk_widget_set_state_flags (page->tab_label, 0, TRUE);
4805 gtk_widget_unparent (page->tab_label);
4806 page->tab_label = NULL;
4811 gtk_notebook_real_remove (GtkNotebook *notebook,
4814 GtkNotebookPrivate *priv = notebook->priv;
4815 GtkNotebookPage *page;
4817 gint need_resize = FALSE;
4818 GtkWidget *tab_label;
4819 gboolean destroying;
4821 destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4823 next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4825 next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4827 priv->children = g_list_remove_link (priv->children, list);
4829 if (priv->cur_page == list->data)
4831 priv->cur_page = NULL;
4832 if (next_list && !destroying)
4833 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4836 if (priv->detached_tab == list->data)
4837 priv->detached_tab = NULL;
4839 if (list == priv->first_tab)
4840 priv->first_tab = next_list;
4841 if (list == priv->focus_tab && !destroying)
4842 gtk_notebook_switch_focus_tab (notebook, next_list);
4846 g_signal_handler_disconnect (page->child, page->notify_visible_handler);
4848 if (gtk_widget_get_visible (page->child) &&
4849 gtk_widget_get_visible (GTK_WIDGET (notebook)))
4852 gtk_widget_unparent (page->child);
4854 tab_label = page->tab_label;
4857 g_object_ref (tab_label);
4858 gtk_notebook_remove_tab_label (notebook, page);
4860 gtk_widget_destroy (tab_label);
4861 g_object_unref (tab_label);
4866 GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
4868 gtk_notebook_menu_label_unparent (parent, NULL);
4869 gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
4871 gtk_widget_queue_resize (priv->menu);
4873 if (!page->default_menu)
4874 g_object_unref (page->menu_label);
4878 if (page->last_focus_child)
4880 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4881 page->last_focus_child = NULL;
4884 g_slice_free (GtkNotebookPage, page);
4886 gtk_notebook_update_labels (notebook);
4888 gtk_widget_queue_resize (GTK_WIDGET (notebook));
4892 gtk_notebook_update_labels (GtkNotebook *notebook)
4894 GtkNotebookPrivate *priv = notebook->priv;
4895 GtkNotebookPage *page;
4900 if (!priv->show_tabs && !priv->menu)
4903 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
4905 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
4908 g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
4909 if (priv->show_tabs)
4911 if (page->default_tab)
4913 if (!page->tab_label)
4915 page->tab_label = gtk_label_new (string);
4916 gtk_widget_set_parent (page->tab_label,
4917 GTK_WIDGET (notebook));
4920 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
4923 if (gtk_widget_get_visible (page->child) &&
4924 !gtk_widget_get_visible (page->tab_label))
4925 gtk_widget_show (page->tab_label);
4926 else if (!gtk_widget_get_visible (page->child) &&
4927 gtk_widget_get_visible (page->tab_label))
4928 gtk_widget_hide (page->tab_label);
4930 if (priv->menu && page->default_menu)
4932 if (GTK_IS_LABEL (page->tab_label))
4933 gtk_label_set_text (GTK_LABEL (page->menu_label),
4934 gtk_label_get_label (GTK_LABEL (page->tab_label)));
4936 gtk_label_set_text (GTK_LABEL (page->menu_label), string);
4942 gtk_notebook_search_page (GtkNotebook *notebook,
4945 gboolean find_visible)
4947 GtkNotebookPrivate *priv = notebook->priv;
4948 GtkNotebookPage *page = NULL;
4949 GList *old_list = NULL;
4954 if (!page || direction == STEP_NEXT)
4962 list = priv->children;
4967 if (direction == STEP_NEXT &&
4969 (gtk_widget_get_visible (page->child) &&
4970 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4985 if (direction == STEP_PREV &&
4987 (gtk_widget_get_visible (page->child) &&
4988 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4996 /* Private GtkNotebook Drawing Functions:
4998 * gtk_notebook_paint
4999 * gtk_notebook_draw_tab
5000 * gtk_notebook_draw_arrow
5003 gtk_notebook_paint (GtkWidget *widget,
5006 GtkNotebook *notebook;
5007 GtkNotebookPrivate *priv;
5008 GtkNotebookPage *page;
5009 GtkAllocation allocation;
5014 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5015 gint gap_x = 0, gap_width = 0, step = STEP_PREV;
5018 GtkStyleContext *context;
5019 GtkRegionFlags tab_flags;
5021 notebook = GTK_NOTEBOOK (widget);
5022 priv = notebook->priv;
5023 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5024 tab_pos = get_effective_tab_pos (notebook);
5025 context = gtk_widget_get_style_context (widget);
5028 if ((!priv->show_tabs && !priv->show_border) ||
5029 !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
5032 gtk_widget_get_allocation (widget, &allocation);
5034 x = allocation.x + border_width;
5035 y = allocation.y + border_width;
5036 width = allocation.width - border_width * 2;
5037 height = allocation.height - border_width * 2;
5039 if (priv->show_border && (!priv->show_tabs || !priv->children))
5041 gtk_render_background (context, cr,
5042 x, y, width, height);
5043 gtk_render_frame (context, cr,
5044 x, y, width, height);
5048 if (!priv->first_tab)
5049 priv->first_tab = priv->children;
5051 if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
5052 page = GTK_NOTEBOOK_PAGE (priv->first_tab);
5054 page = priv->cur_page;
5059 y += page->allocation.height;
5061 case GTK_POS_BOTTOM:
5062 height -= page->allocation.height;
5065 x += page->allocation.width;
5068 width -= page->allocation.width;
5072 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
5073 !gtk_widget_get_mapped (priv->cur_page->tab_label))
5083 case GTK_POS_BOTTOM:
5084 if (priv->operation == DRAG_OPERATION_REORDER)
5085 gap_x = priv->drag_window_x - allocation.x - border_width;
5087 gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
5089 gap_width = priv->cur_page->allocation.width;
5090 step = is_rtl ? STEP_PREV : STEP_NEXT;
5094 if (priv->operation == DRAG_OPERATION_REORDER)
5095 gap_x = priv->drag_window_y - border_width - allocation.y;
5097 gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
5099 gap_width = priv->cur_page->allocation.height;
5105 for (children = priv->children; children; children = children->next)
5107 page = children->data;
5109 if (!gtk_widget_get_visible (page->child))
5112 if (!gtk_widget_get_mapped (page->tab_label))
5115 /* No point in keeping searching */
5120 gtk_style_context_save (context);
5122 if (!showarrow || !priv->scrollable)
5124 GtkJunctionSides junction = 0;
5126 /* Apply junction sides, if no arrows are shown,
5127 * then make corners with connecting tabs square.
5132 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_TOPRIGHT : GTK_JUNCTION_CORNER_TOPLEFT;
5135 case GTK_POS_BOTTOM:
5136 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_BOTTOMRIGHT : GTK_JUNCTION_CORNER_BOTTOMLEFT;
5140 junction |= GTK_JUNCTION_CORNER_TOPLEFT;
5144 junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
5149 gtk_style_context_set_junction_sides (context, junction);
5152 gtk_render_background (context, cr,
5153 x, y, width, height);
5154 gtk_render_frame_gap (context, cr,
5155 x, y, width, height,
5156 tab_pos, gap_x, gap_x + gap_width);
5158 gtk_style_context_restore (context);
5160 children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
5164 page = children->data;
5166 if (page == priv->cur_page)
5169 children = gtk_notebook_search_page (notebook, children,
5172 if (!gtk_widget_get_visible (page->child) ||
5173 !gtk_widget_get_mapped (page->tab_label))
5176 tab_flags = _gtk_notebook_get_tab_flags (notebook, page);
5177 gtk_notebook_draw_tab (notebook, page, cr, tab_flags);
5180 if (children != NULL)
5182 GList *other_order = NULL;
5186 page = children->data;
5187 children = gtk_notebook_search_page (notebook, children,
5189 if (!gtk_widget_get_visible (page->child) ||
5190 !gtk_widget_get_mapped (page->tab_label))
5193 if (children != NULL)
5194 other_order = g_list_prepend (other_order, children->data);
5197 /* draw them with the opposite order */
5198 for (children = other_order; children; children = children->next)
5200 page = children->data;
5202 tab_flags = _gtk_notebook_get_tab_flags (notebook, page);
5203 gtk_notebook_draw_tab (notebook, page, cr, tab_flags);
5206 g_list_free (other_order);
5209 if (showarrow && priv->scrollable)
5211 if (priv->has_before_previous)
5212 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
5213 if (priv->has_before_next)
5214 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
5215 if (priv->has_after_previous)
5216 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
5217 if (priv->has_after_next)
5218 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
5221 if (priv->operation != DRAG_OPERATION_REORDER)
5223 tab_flags = _gtk_notebook_get_tab_flags (notebook, priv->cur_page);
5224 gtk_notebook_draw_tab (notebook, priv->cur_page, cr, tab_flags);
5229 gtk_notebook_draw_tab (GtkNotebook *notebook,
5230 GtkNotebookPage *page,
5232 GtkRegionFlags flags)
5234 GtkNotebookPrivate *priv;
5235 GtkStateFlags state = 0;
5237 GtkStyleContext *context;
5239 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5240 !gtk_widget_get_mapped (page->tab_label) ||
5241 (page->allocation.width == 0) || (page->allocation.height == 0))
5244 widget = GTK_WIDGET (notebook);
5245 priv = notebook->priv;
5247 if (priv->cur_page == page)
5248 state = GTK_STATE_FLAG_ACTIVE;
5250 context = gtk_widget_get_style_context (widget);
5251 gtk_style_context_save (context);
5252 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB, flags);
5253 gtk_style_context_set_state (context, state);
5255 gtk_render_extension (context, cr,
5258 page->allocation.width,
5259 page->allocation.height,
5260 get_tab_gap_pos (notebook));
5262 if (gtk_widget_has_visible_focus (widget) &&
5263 priv->cur_page == page)
5265 gint focus_width, focus_pad;
5266 GtkAllocation allocation;
5268 gtk_widget_get_allocation (page->tab_label, &allocation);
5269 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
5270 gtk_widget_style_get (widget, "focus-padding", &focus_pad, NULL);
5272 gtk_render_focus (context, cr,
5273 allocation.x - focus_width - focus_pad,
5274 allocation.y - focus_width - focus_pad,
5275 allocation.width + 2 * (focus_width + focus_pad),
5276 allocation.height + 2 * (focus_width + focus_pad));
5279 gtk_style_context_restore (context);
5283 gtk_notebook_draw_arrow (GtkNotebook *notebook,
5285 GtkNotebookArrow nbarrow)
5287 GtkNotebookPrivate *priv = notebook->priv;
5288 GtkStyleContext *context;
5289 GtkStateFlags state = 0;
5291 GdkRectangle arrow_rect;
5292 gboolean is_rtl, left;
5293 gint scroll_arrow_hlength;
5294 gint scroll_arrow_vlength;
5298 widget = GTK_WIDGET (notebook);
5299 context = gtk_widget_get_style_context (widget);
5301 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
5303 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5304 left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
5305 (!ARROW_IS_LEFT (nbarrow) && is_rtl);
5307 gtk_widget_style_get (widget,
5308 "scroll-arrow-hlength", &scroll_arrow_hlength,
5309 "scroll-arrow-vlength", &scroll_arrow_vlength,
5312 if (priv->in_child == nbarrow)
5314 state |= GTK_STATE_FLAG_PRELIGHT;
5316 if (priv->click_child == nbarrow)
5317 state |= GTK_STATE_FLAG_ACTIVE;
5320 state = gtk_widget_get_state_flags (widget);
5322 if (priv->focus_tab &&
5323 !gtk_notebook_search_page (notebook, priv->focus_tab,
5324 left ? STEP_PREV : STEP_NEXT, TRUE))
5325 state = GTK_STATE_FLAG_INSENSITIVE;
5327 if (priv->tab_pos == GTK_POS_LEFT ||
5328 priv->tab_pos == GTK_POS_RIGHT)
5330 angle = (ARROW_IS_LEFT (nbarrow)) ? 0 : G_PI;
5331 arrow_size = scroll_arrow_vlength;
5335 angle = (ARROW_IS_LEFT (nbarrow)) ? 3 * (G_PI / 2) : G_PI / 2;
5336 arrow_size = scroll_arrow_hlength;
5339 gtk_style_context_save (context);
5340 gtk_style_context_set_state (context, state);
5342 gtk_render_arrow (context, cr, angle,
5343 arrow_rect.x, arrow_rect.y,
5346 gtk_style_context_restore (context);
5349 /* Private GtkNotebook Size Allocate Functions:
5351 * gtk_notebook_tab_space
5352 * gtk_notebook_calculate_shown_tabs
5353 * gtk_notebook_calculate_tabs_allocation
5354 * gtk_notebook_pages_allocate
5355 * gtk_notebook_page_allocate
5356 * gtk_notebook_calc_tabs
5359 gtk_notebook_tab_space (GtkNotebook *notebook,
5360 gboolean *show_arrows,
5365 GtkNotebookPrivate *priv = notebook->priv;
5366 GtkAllocation allocation, action_allocation;
5368 GtkStyleContext *context;
5370 gint tab_pos = get_effective_tab_pos (notebook);
5373 gint scroll_arrow_hlength;
5374 gint scroll_arrow_vlength;
5381 widget = GTK_WIDGET (notebook);
5382 children = priv->children;
5383 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5385 context = gtk_widget_get_style_context (widget);
5387 gtk_widget_style_get (GTK_WIDGET (notebook),
5388 "arrow-spacing", &arrow_spacing,
5389 "scroll-arrow-hlength", &scroll_arrow_hlength,
5390 "scroll-arrow-vlength", &scroll_arrow_vlength,
5391 "initial-gap", &initial_gap,
5394 border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5395 gtk_style_context_get_padding (context, 0, &padding);
5397 gtk_widget_get_allocation (widget, &allocation);
5399 allocation.x += initial_gap;
5400 allocation.width -= 2 * initial_gap;
5405 case GTK_POS_BOTTOM:
5406 *min = allocation.x + border_width;
5407 *max = allocation.x + allocation.width - border_width;
5409 for (i = 0; i < N_ACTION_WIDGETS; i++)
5411 if (priv->action_widget[i])
5413 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5415 if ((i == ACTION_WIDGET_START && !is_rtl) ||
5416 (i == ACTION_WIDGET_END && is_rtl))
5417 *min += action_allocation.width + padding.left;
5419 *max -= action_allocation.width + padding.right;
5425 GtkNotebookPage *page;
5427 page = children->data;
5428 children = children->next;
5430 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5431 gtk_widget_get_visible (page->child))
5432 *tab_space += page->requisition.width;
5437 *min = allocation.y + border_width;
5438 *max = allocation.y + allocation.height - border_width;
5440 for (i = 0; i < N_ACTION_WIDGETS; i++)
5442 if (priv->action_widget[i])
5444 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5446 if (i == ACTION_WIDGET_START)
5447 *min += action_allocation.height + padding.top;
5449 *max -= action_allocation.height + padding.bottom;
5455 GtkNotebookPage *page;
5457 page = children->data;
5458 children = children->next;
5460 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5461 gtk_widget_get_visible (page->child))
5462 *tab_space += page->requisition.height;
5467 if (!priv->scrollable)
5468 *show_arrows = FALSE;
5471 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5476 case GTK_POS_BOTTOM:
5477 if (*tab_space > *max - *min - tab_overlap)
5479 *show_arrows = TRUE;
5481 /* take arrows into account */
5482 *tab_space = *max - *min - tab_overlap;
5484 if (priv->has_after_previous)
5486 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5487 *max -= arrow_spacing + scroll_arrow_hlength;
5490 if (priv->has_after_next)
5492 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5493 *max -= arrow_spacing + scroll_arrow_hlength;
5496 if (priv->has_before_previous)
5498 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5499 *min += arrow_spacing + scroll_arrow_hlength;
5502 if (priv->has_before_next)
5504 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5505 *min += arrow_spacing + scroll_arrow_hlength;
5511 if (*tab_space > *max - *min - tab_overlap)
5513 *show_arrows = TRUE;
5515 /* take arrows into account */
5516 *tab_space = *max - *min - tab_overlap;
5518 if (priv->has_after_previous || priv->has_after_next)
5520 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5521 *max -= arrow_spacing + scroll_arrow_vlength;
5524 if (priv->has_before_previous || priv->has_before_next)
5526 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5527 *min += arrow_spacing + scroll_arrow_vlength;
5536 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5537 gboolean show_arrows,
5543 gint *remaining_space)
5545 GtkNotebookPrivate *priv = notebook->priv;
5548 GtkNotebookPage *page;
5551 widget = GTK_WIDGET (notebook);
5552 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5554 if (show_arrows) /* first_tab <- focus_tab */
5556 *remaining_space = tab_space;
5558 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5559 gtk_widget_get_visible (priv->cur_page->child))
5561 gtk_notebook_calc_tabs (notebook,
5564 remaining_space, STEP_NEXT);
5567 if (tab_space <= 0 || *remaining_space <= 0)
5570 priv->first_tab = priv->focus_tab;
5571 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5573 page = priv->first_tab->data;
5574 *remaining_space = tab_space - page->requisition.width;
5581 if (priv->first_tab && priv->first_tab != priv->focus_tab)
5583 /* Is first_tab really predecessor of focus_tab? */
5584 page = priv->first_tab->data;
5585 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5586 gtk_widget_get_visible (page->child))
5587 for (children = priv->focus_tab;
5588 children && children != priv->first_tab;
5589 children = gtk_notebook_search_page (notebook,
5597 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5598 priv->first_tab = priv->focus_tab;
5600 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5604 /* calculate shown tabs counting backwards from the focus tab */
5605 gtk_notebook_calc_tabs (notebook,
5606 gtk_notebook_search_page (notebook,
5614 if (*remaining_space < 0)
5617 gtk_notebook_search_page (notebook, priv->first_tab,
5619 if (!priv->first_tab)
5620 priv->first_tab = priv->focus_tab;
5622 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5625 else /* focus_tab -> end */
5627 if (!priv->first_tab)
5628 priv->first_tab = gtk_notebook_search_page (notebook,
5633 gtk_notebook_calc_tabs (notebook,
5634 gtk_notebook_search_page (notebook,
5642 if (*remaining_space <= 0)
5643 *last_child = children;
5644 else /* start <- first_tab */
5649 gtk_notebook_calc_tabs (notebook,
5650 gtk_notebook_search_page (notebook,
5658 if (*remaining_space == 0)
5659 priv->first_tab = children;
5661 priv->first_tab = gtk_notebook_search_page(notebook,
5668 if (*remaining_space < 0)
5670 /* calculate number of tabs */
5671 *remaining_space = - (*remaining_space);
5674 for (children = priv->first_tab;
5675 children && children != *last_child;
5676 children = gtk_notebook_search_page (notebook, children,
5681 *remaining_space = 0;
5684 /* unmap all non-visible tabs */
5685 for (children = gtk_notebook_search_page (notebook, NULL,
5687 children && children != priv->first_tab;
5688 children = gtk_notebook_search_page (notebook, children,
5691 page = children->data;
5693 if (page->tab_label &&
5694 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5695 gtk_widget_set_child_visible (page->tab_label, FALSE);
5698 for (children = *last_child; children;
5699 children = gtk_notebook_search_page (notebook, children,
5702 page = children->data;
5704 if (page->tab_label &&
5705 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5706 gtk_widget_set_child_visible (page->tab_label, FALSE);
5709 else /* !show_arrows */
5711 GtkOrientation tab_expand_orientation;
5715 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5716 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5718 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5719 *remaining_space = max - min - tab_overlap - tab_space;
5720 children = priv->children;
5721 priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5725 page = children->data;
5726 children = children->next;
5728 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5729 !gtk_widget_get_visible (page->child))
5735 (gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5742 get_allocate_at_bottom (GtkWidget *widget,
5743 gint search_direction)
5745 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5746 gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5751 case GTK_POS_BOTTOM:
5753 return (search_direction == STEP_PREV);
5755 return (search_direction == STEP_NEXT);
5760 return (search_direction == STEP_PREV);
5768 gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5773 gint *remaining_space,
5774 gint *expanded_tabs,
5778 GtkNotebookPrivate *priv = notebook->priv;
5779 GtkAllocation allocation;
5781 GtkContainer *container;
5782 GtkNotebookPage *page;
5783 GtkStyleContext *context;
5784 gboolean allocate_at_bottom;
5785 gint tab_overlap, tab_pos, tab_extra_space;
5786 gint left_x, right_x, top_y, bottom_y, anchor;
5788 gboolean gap_left, packing_changed;
5789 GtkAllocation child_allocation = { 0, };
5790 GtkOrientation tab_expand_orientation;
5793 widget = GTK_WIDGET (notebook);
5794 container = GTK_CONTAINER (notebook);
5795 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5796 tab_pos = get_effective_tab_pos (notebook);
5797 allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5800 gtk_widget_get_allocation (widget, &allocation);
5802 border_width = gtk_container_get_border_width (container);
5803 child_allocation.x = allocation.x + border_width;
5804 child_allocation.y = allocation.y + border_width;
5806 context = gtk_widget_get_style_context (widget);
5810 case GTK_POS_BOTTOM:
5811 child_allocation.y = allocation.y + allocation.height -
5812 priv->cur_page->requisition.height - border_width;
5815 child_allocation.x = (allocate_at_bottom) ? max : min;
5816 child_allocation.height = priv->cur_page->requisition.height;
5817 anchor = child_allocation.x;
5821 child_allocation.x = allocation.x + allocation.width -
5822 priv->cur_page->requisition.width - border_width;
5825 child_allocation.y = (allocate_at_bottom) ? max : min;
5826 child_allocation.width = priv->cur_page->requisition.width;
5827 anchor = child_allocation.y;
5831 left_x = CLAMP (priv->mouse_x - priv->drag_offset_x,
5832 min, max - priv->cur_page->allocation.width);
5833 top_y = CLAMP (priv->mouse_y - priv->drag_offset_y,
5834 min, max - priv->cur_page->allocation.height);
5835 right_x = left_x + priv->cur_page->allocation.width;
5836 bottom_y = top_y + priv->cur_page->allocation.height;
5837 gap_left = packing_changed = FALSE;
5839 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5840 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5842 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5844 gtk_style_context_save (context);
5846 while (*children && *children != last_child)
5848 page = (*children)->data;
5850 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB,
5851 _gtk_notebook_get_tab_flags (notebook, page));
5852 gtk_style_context_get_padding (context, 0, &padding);
5854 if (direction == STEP_NEXT)
5855 *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5858 *children = (*children)->next;
5862 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5865 tab_extra_space = 0;
5866 if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5868 tab_extra_space = *remaining_space / *expanded_tabs;
5869 *remaining_space -= tab_extra_space;
5876 case GTK_POS_BOTTOM:
5877 child_allocation.width = MAX (1, page->requisition.width + tab_overlap + tab_extra_space);
5879 /* make sure that the reordered tab doesn't go past the last position */
5880 if (priv->operation == DRAG_OPERATION_REORDER &&
5881 !gap_left && packing_changed)
5883 if (!allocate_at_bottom)
5885 if (left_x >= anchor)
5887 left_x = priv->drag_window_x = anchor;
5888 anchor += priv->cur_page->allocation.width - tab_overlap;
5893 if (right_x <= anchor)
5895 anchor -= priv->cur_page->allocation.width;
5896 left_x = priv->drag_window_x = anchor;
5897 anchor += tab_overlap;
5904 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5906 priv->drag_window_x = left_x;
5907 priv->drag_window_y = child_allocation.y;
5911 if (allocate_at_bottom)
5912 anchor -= child_allocation.width;
5914 if (priv->operation == DRAG_OPERATION_REORDER)
5916 if (!allocate_at_bottom &&
5918 left_x <= anchor + child_allocation.width / 2)
5919 anchor += priv->cur_page->allocation.width - tab_overlap;
5920 else if (allocate_at_bottom &&
5921 right_x >= anchor + child_allocation.width / 2 &&
5922 right_x <= anchor + child_allocation.width)
5923 anchor -= priv->cur_page->allocation.width - tab_overlap;
5926 child_allocation.x = anchor;
5932 child_allocation.height = MAX (1, page->requisition.height + tab_overlap + tab_extra_space);
5934 /* make sure that the reordered tab doesn't go past the last position */
5935 if (priv->operation == DRAG_OPERATION_REORDER &&
5936 !gap_left && packing_changed)
5938 if (!allocate_at_bottom && top_y >= anchor)
5940 top_y = priv->drag_window_y = anchor;
5941 anchor += priv->cur_page->allocation.height - tab_overlap;
5947 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5949 priv->drag_window_x = child_allocation.x;
5950 priv->drag_window_y = top_y;
5954 if (allocate_at_bottom)
5955 anchor -= child_allocation.height;
5957 if (priv->operation == DRAG_OPERATION_REORDER)
5959 if (!allocate_at_bottom &&
5961 top_y <= anchor + child_allocation.height / 2)
5962 anchor += priv->cur_page->allocation.height - tab_overlap;
5963 else if (allocate_at_bottom &&
5964 bottom_y >= anchor + child_allocation.height / 2 &&
5965 bottom_y <= anchor + child_allocation.height)
5966 anchor -= priv->cur_page->allocation.height - tab_overlap;
5969 child_allocation.y = anchor;
5975 page->allocation = child_allocation;
5977 if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
5978 (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
5980 /* needs to be allocated at 0,0
5981 * to be shown in the drag window */
5982 page->allocation.x = 0;
5983 page->allocation.y = 0;
5986 if (page != priv->cur_page)
5991 page->allocation.y += padding.top;
5992 page->allocation.height = MAX (1, page->allocation.height - padding.top);
5994 case GTK_POS_BOTTOM:
5995 page->allocation.height = MAX (1, page->allocation.height - padding.bottom);
5998 page->allocation.x += padding.left;
5999 page->allocation.width = MAX (1, page->allocation.width - padding.left);
6002 page->allocation.width = MAX (1, page->allocation.width - padding.right);
6007 /* calculate whether to leave a gap based on reorder operation or not */
6011 case GTK_POS_BOTTOM:
6012 if (priv->operation != DRAG_OPERATION_REORDER ||
6013 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6015 if (priv->operation == DRAG_OPERATION_REORDER)
6017 if (!allocate_at_bottom &&
6018 left_x > anchor + child_allocation.width / 2 &&
6019 left_x <= anchor + child_allocation.width)
6020 anchor += priv->cur_page->allocation.width - tab_overlap;
6021 else if (allocate_at_bottom &&
6022 right_x >= anchor &&
6023 right_x <= anchor + child_allocation.width / 2)
6024 anchor -= priv->cur_page->allocation.width - tab_overlap;
6027 if (!allocate_at_bottom)
6028 anchor += child_allocation.width - tab_overlap;
6030 anchor += tab_overlap;
6036 if (priv->operation != DRAG_OPERATION_REORDER ||
6037 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6039 if (priv->operation == DRAG_OPERATION_REORDER)
6041 if (!allocate_at_bottom &&
6042 top_y >= anchor + child_allocation.height / 2 &&
6043 top_y <= anchor + child_allocation.height)
6044 anchor += priv->cur_page->allocation.height - tab_overlap;
6045 else if (allocate_at_bottom &&
6046 bottom_y >= anchor &&
6047 bottom_y <= anchor + child_allocation.height / 2)
6048 anchor -= priv->cur_page->allocation.height - tab_overlap;
6051 if (!allocate_at_bottom)
6052 anchor += child_allocation.height - tab_overlap;
6054 anchor += tab_overlap;
6060 /* set child visible */
6061 if (page->tab_label)
6062 gtk_widget_set_child_visible (page->tab_label, TRUE);
6065 gtk_style_context_restore (context);
6067 /* Don't move the current tab past the last position during tabs reordering */
6069 priv->operation == DRAG_OPERATION_REORDER &&
6070 direction == STEP_NEXT)
6075 case GTK_POS_BOTTOM:
6076 if (allocate_at_bottom)
6077 anchor -= priv->cur_page->allocation.width;
6079 if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
6080 (allocate_at_bottom && priv->drag_window_x < anchor))
6081 priv->drag_window_x = anchor;
6085 if (allocate_at_bottom)
6086 anchor -= priv->cur_page->allocation.height;
6088 if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
6089 (allocate_at_bottom && priv->drag_window_y < anchor))
6090 priv->drag_window_y = anchor;
6097 gtk_notebook_pages_allocate (GtkNotebook *notebook)
6099 GtkNotebookPrivate *priv = notebook->priv;
6100 GList *children = NULL;
6101 GList *last_child = NULL;
6102 gboolean showarrow = FALSE;
6103 gint tab_space, min, max, remaining_space;
6105 gboolean tab_allocations_changed = FALSE;
6107 if (!priv->show_tabs || !priv->children || !priv->cur_page)
6110 min = max = tab_space = remaining_space = 0;
6113 gtk_notebook_tab_space (notebook, &showarrow,
6114 &min, &max, &tab_space);
6116 gtk_notebook_calculate_shown_tabs (notebook, showarrow,
6117 min, max, tab_space, &last_child,
6118 &expanded_tabs, &remaining_space);
6120 children = priv->first_tab;
6121 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6122 showarrow, STEP_NEXT,
6123 &remaining_space, &expanded_tabs, min, max);
6124 if (children && children != last_child)
6126 children = priv->children;
6127 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6128 showarrow, STEP_PREV,
6129 &remaining_space, &expanded_tabs, min, max);
6132 children = priv->children;
6136 if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
6137 tab_allocations_changed = TRUE;
6138 children = children->next;
6141 if (!priv->first_tab)
6142 priv->first_tab = priv->children;
6144 if (tab_allocations_changed)
6145 gtk_notebook_redraw_tabs (notebook);
6149 gtk_notebook_page_allocate (GtkNotebook *notebook,
6150 GtkNotebookPage *page)
6152 GtkWidget *widget = GTK_WIDGET (notebook);
6153 GtkNotebookPrivate *priv = notebook->priv;
6154 GtkAllocation child_allocation, label_allocation;
6155 GtkRequisition tab_requisition;
6156 GtkStyleContext *context;
6158 gint focus_width, focus_padding;
6159 gint tab_curvature, tab_overlap;
6160 gint tab_pos = get_effective_tab_pos (notebook);
6161 gboolean tab_allocation_changed;
6162 gboolean was_visible = page->tab_allocated_visible;
6163 GtkBorder tab_padding;
6164 GtkStateFlags state;
6166 if (!page->tab_label ||
6167 !gtk_widget_get_visible (page->tab_label) ||
6168 !gtk_widget_get_child_visible (page->tab_label))
6170 page->tab_allocated_visible = FALSE;
6174 if (page == priv->cur_page)
6175 state = GTK_STATE_FLAG_ACTIVE;
6177 state = GTK_STATE_FLAG_NORMAL;
6179 context = gtk_widget_get_style_context (widget);
6181 gtk_style_context_save (context);
6182 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB,
6183 _gtk_notebook_get_tab_flags (notebook, page));
6185 gtk_style_context_get_padding (context, state, &tab_padding);
6187 gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
6188 gtk_widget_style_get (widget,
6189 "focus-line-width", &focus_width,
6190 "focus-padding", &focus_padding,
6191 "tab-curvature", &tab_curvature,
6192 "tab-overlap", &tab_overlap,
6197 case GTK_POS_BOTTOM:
6198 padding = tab_curvature + priv->tab_hborder + focus_width + focus_padding;
6201 child_allocation.x = tab_padding.left + padding;
6202 child_allocation.width = MAX (1, (page->allocation.width -
6203 tab_padding.left - tab_padding.right -
6205 child_allocation.x += page->allocation.x;
6207 /* if we're drawing an inactive page, trim the allocation width
6208 * for the children by the difference between tab-curvature
6210 * if we're after the active tab, we need to trim the x
6211 * coordinate of the allocation too, to position it after
6212 * the end of the overlap.
6214 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.left, tab_padding.right))
6216 if (gtk_notebook_page_num (notebook, page->child) >
6217 gtk_notebook_page_num (notebook, priv->cur_page->child))
6219 child_allocation.x += tab_overlap - tab_curvature - tab_padding.left;
6220 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.left;
6224 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.right;
6230 child_allocation.x = page->allocation.x +
6231 (page->allocation.width - tab_requisition.width) / 2;
6233 child_allocation.width = tab_requisition.width;
6236 child_allocation.y = priv->tab_vborder + page->allocation.y;
6238 if (tab_pos == GTK_POS_TOP)
6239 child_allocation.y += tab_padding.top + focus_width + focus_padding;
6241 child_allocation.height = MAX (1, (page->allocation.height -
6242 tab_padding.top - tab_padding.bottom -
6243 2 * (priv->tab_vborder + focus_width + focus_padding)));
6247 padding = tab_curvature + priv->tab_vborder + focus_width + focus_padding;
6250 child_allocation.y = tab_padding.top + padding;
6251 child_allocation.height = MAX (1, (page->allocation.height -
6252 tab_padding.bottom - tab_padding.top -
6254 child_allocation.y += page->allocation.y;
6256 /* if we're drawing an inactive page, trim the allocation height
6257 * for the children by the difference between tab-curvature
6259 * if we're after the active tab, we need to trim the y
6260 * coordinate of the allocation too, to position it after
6261 * the end of the overlap.
6263 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.top, tab_padding.bottom))
6265 if (gtk_notebook_page_num (notebook, page->child) >
6266 gtk_notebook_page_num (notebook, priv->cur_page->child))
6268 child_allocation.y += tab_overlap - tab_curvature - tab_padding.top;
6269 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.top;
6273 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.bottom;
6279 child_allocation.y = page->allocation.y +
6280 (page->allocation.height - tab_requisition.height) / 2;
6282 child_allocation.height = tab_requisition.height;
6285 child_allocation.x = priv->tab_hborder + page->allocation.x;
6287 if (tab_pos == GTK_POS_LEFT)
6288 child_allocation.x += tab_padding.left + focus_width + focus_padding;
6290 child_allocation.width = MAX (1, (page->allocation.width -
6291 tab_padding.left - tab_padding.right -
6292 2 * (priv->tab_hborder + focus_width + focus_padding)));
6296 if (page != priv->cur_page)
6301 child_allocation.y -= tab_padding.top;
6302 child_allocation.height += tab_padding.top;
6304 case GTK_POS_BOTTOM:
6305 child_allocation.height += tab_padding.bottom;
6308 child_allocation.x -= tab_padding.left;
6309 child_allocation.width += tab_padding.left;
6312 child_allocation.width += tab_padding.right;
6317 gtk_widget_get_allocation (page->tab_label, &label_allocation);
6318 tab_allocation_changed = (child_allocation.x != label_allocation.x ||
6319 child_allocation.y != label_allocation.y ||
6320 child_allocation.width != label_allocation.width ||
6321 child_allocation.height != label_allocation.height);
6323 gtk_widget_size_allocate (page->tab_label, &child_allocation);
6327 page->tab_allocated_visible = TRUE;
6328 tab_allocation_changed = TRUE;
6331 gtk_style_context_restore (context);
6333 return tab_allocation_changed;
6337 gtk_notebook_calc_tabs (GtkNotebook *notebook,
6343 GtkNotebookPage *page = NULL;
6345 GList *last_calculated_child = NULL;
6346 gint tab_pos = get_effective_tab_pos (notebook);
6356 case GTK_POS_BOTTOM:
6359 page = children->data;
6360 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6361 gtk_widget_get_visible (page->child))
6363 *tab_space -= page->requisition.width;
6364 if (*tab_space < 0 || children == *end)
6368 *tab_space = - (*tab_space +
6369 page->requisition.width);
6371 if (*tab_space == 0 && direction == STEP_PREV)
6372 children = last_calculated_child;
6379 last_calculated_child = children;
6381 if (direction == STEP_NEXT)
6382 children = children->next;
6384 children = children->prev;
6391 page = children->data;
6392 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6393 gtk_widget_get_visible (page->child))
6395 *tab_space -= page->requisition.height;
6396 if (*tab_space < 0 || children == *end)
6400 *tab_space = - (*tab_space + page->requisition.height);
6402 if (*tab_space == 0 && direction == STEP_PREV)
6403 children = last_calculated_child;
6410 last_calculated_child = children;
6412 if (direction == STEP_NEXT)
6413 children = children->next;
6415 children = children->prev;
6422 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6424 GtkNotebookPrivate *priv = notebook->priv;
6428 pos = gtk_widget_path_length (gtk_widget_get_path (GTK_WIDGET (notebook))) - 1;
6430 for (list = priv->children; list != NULL; list = list->next)
6432 GtkNotebookPage *page = list->data;
6434 if (page->tab_label)
6436 GtkRegionFlags current_flags;
6438 if (page == priv->cur_page)
6439 gtk_widget_set_state_flags (page->tab_label, GTK_STATE_FLAG_ACTIVE, FALSE);
6441 gtk_widget_unset_state_flags (page->tab_label, GTK_STATE_FLAG_ACTIVE);
6443 /* FIXME: We should store these flags somewhere instead of poking
6444 * the widget's path */
6445 if (!gtk_widget_path_iter_has_region (gtk_widget_get_path (page->tab_label),
6447 GTK_STYLE_REGION_TAB,
6449 || current_flags != _gtk_notebook_get_tab_flags (notebook, page))
6450 gtk_widget_reset_style (page->tab_label);
6455 /* Private GtkNotebook Page Switch Methods:
6457 * gtk_notebook_real_switch_page
6460 gtk_notebook_real_switch_page (GtkNotebook *notebook,
6464 GtkNotebookPrivate *priv = notebook->priv;
6465 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6466 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6467 gboolean child_has_focus;
6469 if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6472 /* save the value here, changing visibility changes focus */
6473 child_has_focus = priv->child_has_focus;
6476 gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6478 priv->cur_page = page;
6480 if (!priv->focus_tab ||
6481 priv->focus_tab->data != (gpointer) priv->cur_page)
6483 g_list_find (priv->children, priv->cur_page);
6485 gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6487 /* If the focus was on the previous page, move it to the first
6488 * element on the new page, if possible, or if not, to the
6491 if (child_has_focus)
6493 if (priv->cur_page->last_focus_child &&
6494 gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6495 gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6497 if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6498 gtk_widget_grab_focus (GTK_WIDGET (notebook));
6501 gtk_notebook_update_tab_states (notebook);
6502 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6503 g_object_notify (G_OBJECT (notebook), "page");
6506 /* Private GtkNotebook Page Switch Functions:
6508 * gtk_notebook_switch_page
6509 * gtk_notebook_page_select
6510 * gtk_notebook_switch_focus_tab
6511 * gtk_notebook_menu_switch_page
6514 gtk_notebook_switch_page (GtkNotebook *notebook,
6515 GtkNotebookPage *page)
6517 GtkNotebookPrivate *priv = notebook->priv;
6520 if (priv->cur_page == page)
6523 page_num = g_list_index (priv->children, page);
6525 g_signal_emit (notebook,
6526 notebook_signals[SWITCH_PAGE],
6533 gtk_notebook_page_select (GtkNotebook *notebook,
6534 gboolean move_focus)
6536 GtkNotebookPrivate *priv = notebook->priv;
6537 GtkNotebookPage *page;
6538 GtkDirectionType dir = GTK_DIR_DOWN; /* Quiet GCC */
6539 gint tab_pos = get_effective_tab_pos (notebook);
6541 if (!priv->focus_tab)
6544 page = priv->focus_tab->data;
6545 gtk_notebook_switch_page (notebook, page);
6554 case GTK_POS_BOTTOM:
6558 dir = GTK_DIR_RIGHT;
6565 if (gtk_widget_child_focus (page->child, dir))
6572 gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
6575 GtkNotebookPrivate *priv = notebook->priv;
6576 GtkNotebookPage *page;
6578 if (priv->focus_tab == new_child)
6581 priv->focus_tab = new_child;
6583 if (priv->scrollable)
6584 gtk_notebook_redraw_arrows (notebook);
6586 if (!priv->show_tabs || !priv->focus_tab)
6589 page = priv->focus_tab->data;
6590 if (gtk_widget_get_mapped (page->tab_label))
6591 gtk_notebook_redraw_tabs (notebook);
6593 gtk_notebook_pages_allocate (notebook);
6595 gtk_notebook_switch_page (notebook, page);
6599 gtk_notebook_menu_switch_page (GtkWidget *widget,
6600 GtkNotebookPage *page)
6602 GtkNotebookPrivate *priv;
6603 GtkNotebook *notebook;
6608 parent = gtk_widget_get_parent (widget);
6609 notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6610 priv = notebook->priv;
6612 if (priv->cur_page == page)
6616 children = priv->children;
6617 while (children && children->data != page)
6619 children = children->next;
6623 g_signal_emit (notebook,
6624 notebook_signals[SWITCH_PAGE],
6630 /* Private GtkNotebook Menu Functions:
6632 * gtk_notebook_menu_item_create
6633 * gtk_notebook_menu_label_unparent
6634 * gtk_notebook_menu_detacher
6637 gtk_notebook_menu_item_create (GtkNotebook *notebook,
6640 GtkNotebookPrivate *priv = notebook->priv;
6641 GtkNotebookPage *page;
6642 GtkWidget *menu_item;
6645 if (page->default_menu)
6647 if (GTK_IS_LABEL (page->tab_label))
6648 page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6650 page->menu_label = gtk_label_new ("");
6651 gtk_widget_set_halign (page->menu_label, GTK_ALIGN_START);
6652 gtk_widget_set_valign (page->menu_label, GTK_ALIGN_CENTER);
6655 gtk_widget_show (page->menu_label);
6656 menu_item = gtk_menu_item_new ();
6657 gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6658 gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6659 g_list_position (priv->children, list));
6660 g_signal_connect (menu_item, "activate",
6661 G_CALLBACK (gtk_notebook_menu_switch_page), page);
6662 if (gtk_widget_get_visible (page->child))
6663 gtk_widget_show (menu_item);
6667 gtk_notebook_menu_label_unparent (GtkWidget *widget,
6670 gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6671 _gtk_bin_set_child (GTK_BIN (widget), NULL);
6675 gtk_notebook_menu_detacher (GtkWidget *widget,
6678 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6679 GtkNotebookPrivate *priv = notebook->priv;
6681 g_return_if_fail (priv->menu == (GtkWidget*) menu);
6686 /* Public GtkNotebook Page Insert/Remove Methods :
6688 * gtk_notebook_append_page
6689 * gtk_notebook_append_page_menu
6690 * gtk_notebook_prepend_page
6691 * gtk_notebook_prepend_page_menu
6692 * gtk_notebook_insert_page
6693 * gtk_notebook_insert_page_menu
6694 * gtk_notebook_remove_page
6697 * gtk_notebook_append_page:
6698 * @notebook: a #GtkNotebook
6699 * @child: the #GtkWidget to use as the contents of the page
6700 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6701 * for the page, or %NULL to use the default label, 'page N'
6703 * Appends a page to @notebook.
6705 * Return value: the index (starting from 0) of the appended
6706 * page in the notebook, or -1 if function fails
6709 gtk_notebook_append_page (GtkNotebook *notebook,
6711 GtkWidget *tab_label)
6713 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6714 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6715 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6717 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6721 * gtk_notebook_append_page_menu:
6722 * @notebook: a #GtkNotebook
6723 * @child: the #GtkWidget to use as the contents of the page
6724 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6725 * for the page, or %NULL to use the default label, 'page N'
6726 * @menu_label: (allow-none): the widget to use as a label for the
6727 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6728 * is a #GtkLabel or %NULL, then the menu label will be a newly
6729 * created label with the same text as @tab_label; if @tab_label
6730 * is not a #GtkLabel, @menu_label must be specified if the
6731 * page-switch menu is to be used.
6733 * Appends a page to @notebook, specifying the widget to use as the
6734 * label in the popup menu.
6736 * Return value: the index (starting from 0) of the appended
6737 * page in the notebook, or -1 if function fails
6740 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6742 GtkWidget *tab_label,
6743 GtkWidget *menu_label)
6745 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6746 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6747 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6748 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6750 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6754 * gtk_notebook_prepend_page:
6755 * @notebook: a #GtkNotebook
6756 * @child: the #GtkWidget to use as the contents of the page
6757 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6758 * for the page, or %NULL to use the default label, 'page N'
6760 * Prepends a page to @notebook.
6762 * Return value: the index (starting from 0) of the prepended
6763 * page in the notebook, or -1 if function fails
6766 gtk_notebook_prepend_page (GtkNotebook *notebook,
6768 GtkWidget *tab_label)
6770 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6771 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6772 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6774 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6778 * gtk_notebook_prepend_page_menu:
6779 * @notebook: a #GtkNotebook
6780 * @child: the #GtkWidget to use as the contents of the page
6781 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6782 * for the page, or %NULL to use the default label, 'page N'
6783 * @menu_label: (allow-none): the widget to use as a label for the
6784 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6785 * is a #GtkLabel or %NULL, then the menu label will be a newly
6786 * created label with the same text as @tab_label; if @tab_label
6787 * is not a #GtkLabel, @menu_label must be specified if the
6788 * page-switch menu is to be used.
6790 * Prepends a page to @notebook, specifying the widget to use as the
6791 * label in the popup menu.
6793 * Return value: the index (starting from 0) of the prepended
6794 * page in the notebook, or -1 if function fails
6797 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6799 GtkWidget *tab_label,
6800 GtkWidget *menu_label)
6802 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6803 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6804 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6805 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6807 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6811 * gtk_notebook_insert_page:
6812 * @notebook: a #GtkNotebook
6813 * @child: the #GtkWidget to use as the contents of the page
6814 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6815 * for the page, or %NULL to use the default label, 'page N'
6816 * @position: the index (starting at 0) at which to insert the page,
6817 * or -1 to append the page after all other pages
6819 * Insert a page into @notebook at the given position.
6821 * Return value: the index (starting from 0) of the inserted
6822 * page in the notebook, or -1 if function fails
6825 gtk_notebook_insert_page (GtkNotebook *notebook,
6827 GtkWidget *tab_label,
6830 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6831 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6832 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6834 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6839 gtk_notebook_page_compare_tab (gconstpointer a,
6842 return (((GtkNotebookPage *) a)->tab_label != b);
6846 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6850 GtkNotebook *notebook = GTK_NOTEBOOK (data);
6851 GtkNotebookPrivate *priv = notebook->priv;
6854 list = g_list_find_custom (priv->children, child,
6855 gtk_notebook_page_compare_tab);
6858 GtkNotebookPage *page = list->data;
6860 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
6861 gtk_notebook_switch_page (notebook, page);
6862 focus_tabs_in (notebook);
6869 * gtk_notebook_insert_page_menu:
6870 * @notebook: a #GtkNotebook
6871 * @child: the #GtkWidget to use as the contents of the page
6872 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6873 * for the page, or %NULL to use the default label, 'page N'
6874 * @menu_label: (allow-none): the widget to use as a label for the
6875 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6876 * is a #GtkLabel or %NULL, then the menu label will be a newly
6877 * created label with the same text as @tab_label; if @tab_label
6878 * is not a #GtkLabel, @menu_label must be specified if the
6879 * page-switch menu is to be used.
6880 * @position: the index (starting at 0) at which to insert the page,
6881 * or -1 to append the page after all other pages.
6883 * Insert a page into @notebook at the given position, specifying
6884 * the widget to use as the label in the popup menu.
6886 * Return value: the index (starting from 0) of the inserted
6887 * page in the notebook
6890 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
6892 GtkWidget *tab_label,
6893 GtkWidget *menu_label,
6896 GtkNotebookClass *class;
6898 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6899 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6900 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6901 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6903 class = GTK_NOTEBOOK_GET_CLASS (notebook);
6905 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
6909 * gtk_notebook_remove_page:
6910 * @notebook: a #GtkNotebook
6911 * @page_num: the index of a notebook page, starting
6912 * from 0. If -1, the last page will be removed.
6914 * Removes a page from the notebook given its index
6918 gtk_notebook_remove_page (GtkNotebook *notebook,
6921 GtkNotebookPrivate *priv;
6924 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6926 priv = notebook->priv;
6929 list = g_list_nth (priv->children, page_num);
6931 list = g_list_last (priv->children);
6934 gtk_container_remove (GTK_CONTAINER (notebook),
6935 ((GtkNotebookPage *) list->data)->child);
6938 /* Public GtkNotebook Page Switch Methods :
6939 * gtk_notebook_get_current_page
6940 * gtk_notebook_page_num
6941 * gtk_notebook_set_current_page
6942 * gtk_notebook_next_page
6943 * gtk_notebook_prev_page
6946 * gtk_notebook_get_current_page:
6947 * @notebook: a #GtkNotebook
6949 * Returns the page number of the current page.
6951 * Return value: the index (starting from 0) of the current
6952 * page in the notebook. If the notebook has no pages,
6953 * then -1 will be returned.
6956 gtk_notebook_get_current_page (GtkNotebook *notebook)
6958 GtkNotebookPrivate *priv;
6960 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6962 priv = notebook->priv;
6964 if (!priv->cur_page)
6967 return g_list_index (priv->children, priv->cur_page);
6971 * gtk_notebook_get_nth_page:
6972 * @notebook: a #GtkNotebook
6973 * @page_num: the index of a page in the notebook, or -1
6974 * to get the last page
6976 * Returns the child widget contained in page number @page_num.
6978 * Return value: (transfer none): the child widget, or %NULL
6979 * if @page_num is out of bounds
6982 gtk_notebook_get_nth_page (GtkNotebook *notebook,
6985 GtkNotebookPrivate *priv;
6986 GtkNotebookPage *page;
6989 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6991 priv = notebook->priv;
6994 list = g_list_nth (priv->children, page_num);
6996 list = g_list_last (priv->children);
7008 * gtk_notebook_get_n_pages:
7009 * @notebook: a #GtkNotebook
7011 * Gets the number of pages in a notebook.
7013 * Return value: the number of pages in the notebook
7018 gtk_notebook_get_n_pages (GtkNotebook *notebook)
7020 GtkNotebookPrivate *priv;
7022 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
7024 priv = notebook->priv;
7026 return g_list_length (priv->children);
7030 * gtk_notebook_page_num:
7031 * @notebook: a #GtkNotebook
7032 * @child: a #GtkWidget
7034 * Finds the index of the page which contains the given child
7037 * Return value: the index of the page containing @child, or
7038 * -1 if @child is not in the notebook
7041 gtk_notebook_page_num (GtkNotebook *notebook,
7044 GtkNotebookPrivate *priv;
7048 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7050 priv = notebook->priv;
7053 children = priv->children;
7056 GtkNotebookPage *page = children->data;
7058 if (page->child == child)
7061 children = children->next;
7069 * gtk_notebook_set_current_page:
7070 * @notebook: a #GtkNotebook
7071 * @page_num: index of the page to switch to, starting from 0.
7072 * If negative, the last page will be used. If greater
7073 * than the number of pages in the notebook, nothing
7076 * Switches to the page number @page_num.
7078 * Note that due to historical reasons, GtkNotebook refuses
7079 * to switch to a page unless the child widget is visible.
7080 * Therefore, it is recommended to show child widgets before
7081 * adding them to a notebook.
7084 gtk_notebook_set_current_page (GtkNotebook *notebook,
7087 GtkNotebookPrivate *priv;
7090 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7092 priv = notebook->priv;
7095 page_num = g_list_length (priv->children) - 1;
7097 list = g_list_nth (priv->children, page_num);
7099 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7103 * gtk_notebook_next_page:
7104 * @notebook: a #GtkNotebook
7106 * Switches to the next page. Nothing happens if the current page is
7110 gtk_notebook_next_page (GtkNotebook *notebook)
7112 GtkNotebookPrivate *priv;
7115 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7117 priv = notebook->priv;
7119 list = g_list_find (priv->children, priv->cur_page);
7123 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
7127 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7131 * gtk_notebook_prev_page:
7132 * @notebook: a #GtkNotebook
7134 * Switches to the previous page. Nothing happens if the current page
7135 * is the first page.
7138 gtk_notebook_prev_page (GtkNotebook *notebook)
7140 GtkNotebookPrivate *priv;
7143 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7145 priv = notebook->priv;
7147 list = g_list_find (priv->children, priv->cur_page);
7151 list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
7155 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7158 /* Public GtkNotebook/Tab Style Functions
7160 * gtk_notebook_set_show_border
7161 * gtk_notebook_get_show_border
7162 * gtk_notebook_set_show_tabs
7163 * gtk_notebook_get_show_tabs
7164 * gtk_notebook_set_tab_pos
7165 * gtk_notebook_get_tab_pos
7166 * gtk_notebook_set_scrollable
7167 * gtk_notebook_get_scrollable
7168 * gtk_notebook_get_tab_hborder
7169 * gtk_notebook_get_tab_vborder
7172 * gtk_notebook_set_show_border:
7173 * @notebook: a #GtkNotebook
7174 * @show_border: %TRUE if a bevel should be drawn around the notebook
7176 * Sets whether a bevel will be drawn around the notebook pages.
7177 * This only has a visual effect when the tabs are not shown.
7178 * See gtk_notebook_set_show_tabs().
7181 gtk_notebook_set_show_border (GtkNotebook *notebook,
7182 gboolean show_border)
7184 GtkNotebookPrivate *priv;
7186 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7188 priv = notebook->priv;
7190 if (priv->show_border != show_border)
7192 priv->show_border = show_border;
7194 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7195 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7197 g_object_notify (G_OBJECT (notebook), "show-border");
7202 * gtk_notebook_get_show_border:
7203 * @notebook: a #GtkNotebook
7205 * Returns whether a bevel will be drawn around the notebook pages.
7206 * See gtk_notebook_set_show_border().
7208 * Return value: %TRUE if the bevel is drawn
7211 gtk_notebook_get_show_border (GtkNotebook *notebook)
7213 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7215 return notebook->priv->show_border;
7219 * gtk_notebook_set_show_tabs:
7220 * @notebook: a #GtkNotebook
7221 * @show_tabs: %TRUE if the tabs should be shown
7223 * Sets whether to show the tabs for the notebook or not.
7226 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
7229 GtkNotebookPrivate *priv;
7230 GtkNotebookPage *page;
7231 GtkStyleContext *context;
7235 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7237 priv = notebook->priv;
7239 show_tabs = show_tabs != FALSE;
7241 if (priv->show_tabs == show_tabs)
7244 priv->show_tabs = show_tabs;
7245 children = priv->children;
7246 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
7250 gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
7254 page = children->data;
7255 children = children->next;
7256 if (page->default_tab)
7258 gtk_widget_destroy (page->tab_label);
7259 page->tab_label = NULL;
7262 gtk_widget_hide (page->tab_label);
7265 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7269 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
7270 gtk_notebook_update_labels (notebook);
7271 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7274 for (i = 0; i < N_ACTION_WIDGETS; i++)
7276 if (priv->action_widget[i])
7277 gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
7280 gtk_widget_reset_style (GTK_WIDGET (notebook));
7281 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7283 g_object_notify (G_OBJECT (notebook), "show-tabs");
7287 * gtk_notebook_get_show_tabs:
7288 * @notebook: a #GtkNotebook
7290 * Returns whether the tabs of the notebook are shown.
7291 * See gtk_notebook_set_show_tabs().
7293 * Return value: %TRUE if the tabs are shown
7296 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
7298 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7300 return notebook->priv->show_tabs;
7304 * gtk_notebook_set_tab_pos:
7305 * @notebook: a #GtkNotebook.
7306 * @pos: the edge to draw the tabs at
7308 * Sets the edge at which the tabs for switching pages in the
7309 * notebook are drawn.
7312 gtk_notebook_set_tab_pos (GtkNotebook *notebook,
7313 GtkPositionType pos)
7315 GtkNotebookPrivate *priv;
7317 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7319 priv = notebook->priv;
7321 if (priv->tab_pos != pos)
7323 priv->tab_pos = pos;
7324 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7325 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7328 g_object_notify (G_OBJECT (notebook), "tab-pos");
7332 * gtk_notebook_get_tab_pos:
7333 * @notebook: a #GtkNotebook
7335 * Gets the edge at which the tabs for switching pages in the
7336 * notebook are drawn.
7338 * Return value: the edge at which the tabs are drawn
7341 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
7343 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
7345 return notebook->priv->tab_pos;
7349 * gtk_notebook_set_scrollable:
7350 * @notebook: a #GtkNotebook
7351 * @scrollable: %TRUE if scroll arrows should be added
7353 * Sets whether the tab label area will have arrows for
7354 * scrolling if there are too many tabs to fit in the area.
7357 gtk_notebook_set_scrollable (GtkNotebook *notebook,
7358 gboolean scrollable)
7360 GtkNotebookPrivate *priv;
7362 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7364 priv = notebook->priv;
7366 scrollable = (scrollable != FALSE);
7368 if (scrollable != priv->scrollable)
7370 priv->scrollable = scrollable;
7372 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7373 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7375 g_object_notify (G_OBJECT (notebook), "scrollable");
7380 * gtk_notebook_get_scrollable:
7381 * @notebook: a #GtkNotebook
7383 * Returns whether the tab label area has arrows for scrolling.
7384 * See gtk_notebook_set_scrollable().
7386 * Return value: %TRUE if arrows for scrolling are present
7389 gtk_notebook_get_scrollable (GtkNotebook *notebook)
7391 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7393 return notebook->priv->scrollable;
7397 * gtk_notebook_get_tab_hborder:
7398 * @notebook: a #GtkNotebook
7400 * Returns the horizontal width of a tab border.
7402 * Return value: horizontal width of a tab border
7407 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
7409 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7411 return notebook->priv->tab_hborder;
7415 * gtk_notebook_get_tab_vborder:
7416 * @notebook: a #GtkNotebook
7418 * Returns the vertical width of a tab border.
7420 * Return value: vertical width of a tab border
7425 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7427 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7429 return notebook->priv->tab_vborder;
7433 /* Public GtkNotebook Popup Menu Methods:
7435 * gtk_notebook_popup_enable
7436 * gtk_notebook_popup_disable
7441 * gtk_notebook_popup_enable:
7442 * @notebook: a #GtkNotebook
7444 * Enables the popup menu: if the user clicks with the right
7445 * mouse button on the tab labels, a menu with all the pages
7446 * will be popped up.
7449 gtk_notebook_popup_enable (GtkNotebook *notebook)
7451 GtkNotebookPrivate *priv;
7454 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7456 priv = notebook->priv;
7461 priv->menu = gtk_menu_new ();
7462 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7464 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7465 gtk_notebook_menu_item_create (notebook, list);
7467 gtk_notebook_update_labels (notebook);
7468 gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7469 GTK_WIDGET (notebook),
7470 gtk_notebook_menu_detacher);
7472 g_object_notify (G_OBJECT (notebook), "enable-popup");
7476 * gtk_notebook_popup_disable:
7477 * @notebook: a #GtkNotebook
7479 * Disables the popup menu.
7482 gtk_notebook_popup_disable (GtkNotebook *notebook)
7484 GtkNotebookPrivate *priv;
7486 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7488 priv = notebook->priv;
7493 gtk_container_foreach (GTK_CONTAINER (priv->menu),
7494 (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7495 gtk_widget_destroy (priv->menu);
7497 g_object_notify (G_OBJECT (notebook), "enable-popup");
7500 /* Public GtkNotebook Page Properties Functions:
7502 * gtk_notebook_get_tab_label
7503 * gtk_notebook_set_tab_label
7504 * gtk_notebook_set_tab_label_text
7505 * gtk_notebook_get_menu_label
7506 * gtk_notebook_set_menu_label
7507 * gtk_notebook_set_menu_label_text
7508 * gtk_notebook_get_tab_reorderable
7509 * gtk_notebook_set_tab_reorderable
7510 * gtk_notebook_get_tab_detachable
7511 * gtk_notebook_set_tab_detachable
7515 * gtk_notebook_get_tab_label:
7516 * @notebook: a #GtkNotebook
7519 * Returns the tab label widget for the page @child.
7520 * %NULL is returned if @child is not in @notebook or
7521 * if no tab label has specifically been set for @child.
7523 * Return value: (transfer none): the tab label
7526 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7531 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7532 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7534 list = CHECK_FIND_CHILD (notebook, child);
7538 if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7541 return GTK_NOTEBOOK_PAGE (list)->tab_label;
7545 * gtk_notebook_set_tab_label:
7546 * @notebook: a #GtkNotebook
7548 * @tab_label: (allow-none): the tab label widget to use, or %NULL
7549 * for default tab label
7551 * Changes the tab label for @child.
7552 * If %NULL is specified for @tab_label, then the page will
7553 * have the label 'page N'.
7556 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7558 GtkWidget *tab_label)
7560 GtkNotebookPrivate *priv;
7561 GtkNotebookPage *page;
7564 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7565 g_return_if_fail (GTK_IS_WIDGET (child));
7567 priv = notebook->priv;
7569 list = CHECK_FIND_CHILD (notebook, child);
7573 /* a NULL pointer indicates a default_tab setting, otherwise
7574 * we need to set the associated label
7578 if (page->tab_label == tab_label)
7582 gtk_notebook_remove_tab_label (notebook, page);
7586 page->default_tab = FALSE;
7587 page->tab_label = tab_label;
7588 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7592 page->default_tab = TRUE;
7593 page->tab_label = NULL;
7595 if (priv->show_tabs)
7599 g_snprintf (string, sizeof(string), _("Page %u"),
7600 g_list_position (priv->children, list));
7601 page->tab_label = gtk_label_new (string);
7602 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7606 if (page->tab_label)
7607 page->mnemonic_activate_signal =
7608 g_signal_connect (page->tab_label,
7609 "mnemonic-activate",
7610 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7613 if (priv->show_tabs && gtk_widget_get_visible (child))
7615 gtk_widget_show (page->tab_label);
7616 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7619 gtk_notebook_update_tab_states (notebook);
7620 gtk_widget_child_notify (child, "tab-label");
7624 * gtk_notebook_set_tab_label_text:
7625 * @notebook: a #GtkNotebook
7627 * @tab_text: the label text
7629 * Creates a new label and sets it as the tab label for the page
7630 * containing @child.
7633 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7635 const gchar *tab_text)
7637 GtkWidget *tab_label = NULL;
7639 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7642 tab_label = gtk_label_new (tab_text);
7643 gtk_notebook_set_tab_label (notebook, child, tab_label);
7644 gtk_widget_child_notify (child, "tab-label");
7648 * gtk_notebook_get_tab_label_text:
7649 * @notebook: a #GtkNotebook
7650 * @child: a widget contained in a page of @notebook
7652 * Retrieves the text of the tab label for the page containing
7655 * Return value: the text of the tab label, or %NULL if the
7656 * tab label widget is not a #GtkLabel. The string is owned
7657 * by the widget and must not be freed.
7660 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7663 GtkWidget *tab_label;
7665 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7666 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7668 tab_label = gtk_notebook_get_tab_label (notebook, child);
7670 if (GTK_IS_LABEL (tab_label))
7671 return gtk_label_get_text (GTK_LABEL (tab_label));
7677 * gtk_notebook_get_menu_label:
7678 * @notebook: a #GtkNotebook
7679 * @child: a widget contained in a page of @notebook
7681 * Retrieves the menu label widget of the page containing @child.
7683 * Return value: (transfer none): the menu label, or %NULL if the
7684 * notebook page does not have a menu label other than the
7685 * default (the tab label).
7688 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7693 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7694 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7696 list = CHECK_FIND_CHILD (notebook, child);
7700 if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7703 return GTK_NOTEBOOK_PAGE (list)->menu_label;
7707 * gtk_notebook_set_menu_label:
7708 * @notebook: a #GtkNotebook
7709 * @child: the child widget
7710 * @menu_label: (allow-none): the menu label, or %NULL for default
7712 * Changes the menu label for the page containing @child.
7715 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7717 GtkWidget *menu_label)
7719 GtkNotebookPrivate *priv;
7720 GtkNotebookPage *page;
7723 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7724 g_return_if_fail (GTK_IS_WIDGET (child));
7726 priv = notebook->priv;
7728 list = CHECK_FIND_CHILD (notebook, child);
7733 if (page->menu_label)
7736 gtk_container_remove (GTK_CONTAINER (priv->menu),
7737 gtk_widget_get_parent (page->menu_label));
7739 if (!page->default_menu)
7740 g_object_unref (page->menu_label);
7745 page->menu_label = menu_label;
7746 g_object_ref_sink (page->menu_label);
7747 page->default_menu = FALSE;
7750 page->default_menu = TRUE;
7753 gtk_notebook_menu_item_create (notebook, list);
7754 gtk_widget_child_notify (child, "menu-label");
7758 * gtk_notebook_set_menu_label_text:
7759 * @notebook: a #GtkNotebook
7760 * @child: the child widget
7761 * @menu_text: the label text
7763 * Creates a new label and sets it as the menu label of @child.
7766 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7768 const gchar *menu_text)
7770 GtkWidget *menu_label = NULL;
7772 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7776 menu_label = gtk_label_new (menu_text);
7777 gtk_widget_set_halign (menu_label, GTK_ALIGN_START);
7778 gtk_widget_set_valign (menu_label, GTK_ALIGN_CENTER);
7780 gtk_notebook_set_menu_label (notebook, child, menu_label);
7781 gtk_widget_child_notify (child, "menu-label");
7785 * gtk_notebook_get_menu_label_text:
7786 * @notebook: a #GtkNotebook
7787 * @child: the child widget of a page of the notebook.
7789 * Retrieves the text of the menu label for the page containing
7792 * Return value: the text of the tab label, or %NULL if the
7793 * widget does not have a menu label other than the default
7794 * menu label, or the menu label widget is not a #GtkLabel.
7795 * The string is owned by the widget and must not be freed.
7798 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7801 GtkWidget *menu_label;
7803 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7804 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7806 menu_label = gtk_notebook_get_menu_label (notebook, child);
7808 if (GTK_IS_LABEL (menu_label))
7809 return gtk_label_get_text (GTK_LABEL (menu_label));
7814 /* Helper function called when pages are reordered
7817 gtk_notebook_child_reordered (GtkNotebook *notebook,
7818 GtkNotebookPage *page)
7820 GtkNotebookPrivate *priv = notebook->priv;
7824 GtkWidget *menu_item;
7826 menu_item = gtk_widget_get_parent (page->menu_label);
7827 gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7828 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7829 gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7832 gtk_notebook_update_tab_states (notebook);
7833 gtk_notebook_update_labels (notebook);
7837 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7842 GtkNotebookPrivate *priv;
7843 GtkNotebookPage *page;
7846 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7847 g_return_if_fail (GTK_IS_WIDGET (child));
7849 priv = notebook->priv;
7851 list = CHECK_FIND_CHILD (notebook, child);
7856 expand = expand != FALSE;
7857 fill = fill != FALSE;
7858 if (page->expand == expand && page->fill == fill)
7861 gtk_widget_freeze_child_notify (child);
7862 page->expand = expand;
7863 gtk_widget_child_notify (child, "tab-expand");
7865 gtk_widget_child_notify (child, "tab-fill");
7866 gtk_widget_child_notify (child, "position");
7867 if (priv->show_tabs)
7868 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7869 gtk_widget_thaw_child_notify (child);
7873 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
7880 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7881 g_return_if_fail (GTK_IS_WIDGET (child));
7883 list = CHECK_FIND_CHILD (notebook, child);
7888 *expand = GTK_NOTEBOOK_PAGE (list)->expand;
7890 *fill = GTK_NOTEBOOK_PAGE (list)->fill;
7894 * gtk_notebook_reorder_child:
7895 * @notebook: a #GtkNotebook
7896 * @child: the child to move
7897 * @position: the new position, or -1 to move to the end
7899 * Reorders the page containing @child, so that it appears in position
7900 * @position. If @position is greater than or equal to the number of
7901 * children in the list or negative, @child will be moved to the end
7905 gtk_notebook_reorder_child (GtkNotebook *notebook,
7909 GtkNotebookPrivate *priv;
7910 GList *list, *new_list;
7911 GtkNotebookPage *page;
7915 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7916 g_return_if_fail (GTK_IS_WIDGET (child));
7918 priv = notebook->priv;
7920 list = CHECK_FIND_CHILD (notebook, child);
7924 max_pos = g_list_length (priv->children) - 1;
7925 if (position < 0 || position > max_pos)
7928 old_pos = g_list_position (priv->children, list);
7930 if (old_pos == position)
7934 priv->children = g_list_delete_link (priv->children, list);
7936 priv->children = g_list_insert (priv->children, page, position);
7937 new_list = g_list_nth (priv->children, position);
7939 /* Fix up GList references in GtkNotebook structure */
7940 if (priv->first_tab == list)
7941 priv->first_tab = new_list;
7942 if (priv->focus_tab == list)
7943 priv->focus_tab = new_list;
7945 gtk_widget_freeze_child_notify (child);
7947 /* Move around the menu items if necessary */
7948 gtk_notebook_child_reordered (notebook, page);
7949 gtk_widget_child_notify (child, "position");
7951 if (priv->show_tabs)
7952 gtk_notebook_pages_allocate (notebook);
7954 gtk_widget_thaw_child_notify (child);
7956 g_signal_emit (notebook,
7957 notebook_signals[PAGE_REORDERED],
7964 * gtk_notebook_set_group_name:
7965 * @notebook: a #GtkNotebook
7966 * @group_name: (allow-none): the name of the notebook group,
7967 * or %NULL to unset it
7969 * Sets a group name for @notebook.
7971 * Notebooks with the same name will be able to exchange tabs
7972 * via drag and drop. A notebook with a %NULL group name will
7973 * not be able to exchange tabs with any other notebook.
7978 gtk_notebook_set_group_name (GtkNotebook *notebook,
7979 const gchar *group_name)
7981 GtkNotebookPrivate *priv;
7984 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7986 priv = notebook->priv;
7988 group = g_quark_from_string (group_name);
7990 if (priv->group != group)
7992 priv->group = group;
7993 g_object_notify (G_OBJECT (notebook), "group-name");
7998 * gtk_notebook_get_group_name:
7999 * @notebook: a #GtkNotebook
8001 * Gets the current group name for @notebook.
8003 * Return Value: (transfer none): the group name,
8004 * or %NULL if none is set.
8009 gtk_notebook_get_group_name (GtkNotebook *notebook)
8011 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8013 return g_quark_to_string (notebook->priv->group);
8017 * gtk_notebook_get_tab_reorderable:
8018 * @notebook: a #GtkNotebook
8019 * @child: a child #GtkWidget
8021 * Gets whether the tab can be reordered via drag and drop or not.
8023 * Return Value: %TRUE if the tab is reorderable.
8028 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
8033 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8034 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8036 list = CHECK_FIND_CHILD (notebook, child);
8040 return GTK_NOTEBOOK_PAGE (list)->reorderable;
8044 * gtk_notebook_set_tab_reorderable:
8045 * @notebook: a #GtkNotebook
8046 * @child: a child #GtkWidget
8047 * @reorderable: whether the tab is reorderable or not
8049 * Sets whether the notebook tab can be reordered
8050 * via drag and drop or not.
8055 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
8057 gboolean reorderable)
8061 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8062 g_return_if_fail (GTK_IS_WIDGET (child));
8064 list = CHECK_FIND_CHILD (notebook, child);
8068 if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
8070 GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
8071 gtk_widget_child_notify (child, "reorderable");
8076 * gtk_notebook_get_tab_detachable:
8077 * @notebook: a #GtkNotebook
8078 * @child: a child #GtkWidget
8080 * Returns whether the tab contents can be detached from @notebook.
8082 * Return Value: %TRUE if the tab is detachable.
8087 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
8092 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8093 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8095 list = CHECK_FIND_CHILD (notebook, child);
8099 return GTK_NOTEBOOK_PAGE (list)->detachable;
8103 * gtk_notebook_set_tab_detachable:
8104 * @notebook: a #GtkNotebook
8105 * @child: a child #GtkWidget
8106 * @detachable: whether the tab is detachable or not
8108 * Sets whether the tab can be detached from @notebook to another
8109 * notebook or widget.
8111 * Note that 2 notebooks must share a common group identificator
8112 * (see gtk_notebook_set_group_name()) to allow automatic tabs
8113 * interchange between them.
8115 * If you want a widget to interact with a notebook through DnD
8116 * (i.e.: accept dragged tabs from it) it must be set as a drop
8117 * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
8118 * will fill the selection with a GtkWidget** pointing to the child
8119 * widget that corresponds to the dropped tab.
8122 * on_drop_zone_drag_data_received (GtkWidget *widget,
8123 * GdkDragContext *context,
8126 * GtkSelectionData *selection_data,
8129 * gpointer user_data)
8131 * GtkWidget *notebook;
8132 * GtkWidget **child;
8134 * notebook = gtk_drag_get_source_widget (context);
8135 * child = (void*) gtk_selection_data_get_data (selection_data);
8137 * process_widget (*child);
8138 * gtk_container_remove (GTK_CONTAINER (notebook), *child);
8142 * If you want a notebook to accept drags from other widgets,
8143 * you will have to set your own DnD code to do it.
8148 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
8150 gboolean detachable)
8154 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8155 g_return_if_fail (GTK_IS_WIDGET (child));
8157 list = CHECK_FIND_CHILD (notebook, child);
8161 if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
8163 GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
8164 gtk_widget_child_notify (child, "detachable");
8169 * gtk_notebook_get_action_widget:
8170 * @notebook: a #GtkNotebook
8171 * @pack_type: pack type of the action widget to receive
8173 * Gets one of the action widgets. See gtk_notebook_set_action_widget().
8175 * Returns: (transfer none): The action widget with the given @pack_type
8176 * or %NULL when this action widget has not been set
8181 gtk_notebook_get_action_widget (GtkNotebook *notebook,
8182 GtkPackType pack_type)
8184 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8186 return notebook->priv->action_widget[pack_type];
8190 * gtk_notebook_set_action_widget:
8191 * @notebook: a #GtkNotebook
8192 * @widget: a #GtkWidget
8193 * @pack_type: pack type of the action widget
8195 * Sets @widget as one of the action widgets. Depending on the pack type
8196 * the widget will be placed before or after the tabs. You can use
8197 * a #GtkBox if you need to pack more than one widget on the same side.
8199 * Note that action widgets are "internal" children of the notebook and thus
8200 * not included in the list returned from gtk_container_foreach().
8205 gtk_notebook_set_action_widget (GtkNotebook *notebook,
8207 GtkPackType pack_type)
8209 GtkNotebookPrivate *priv;
8211 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8212 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
8213 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
8215 priv = notebook->priv;
8217 if (priv->action_widget[pack_type])
8218 gtk_widget_unparent (priv->action_widget[pack_type]);
8220 priv->action_widget[pack_type] = widget;
8224 gtk_widget_set_child_visible (widget, priv->show_tabs);
8225 gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
8228 gtk_widget_queue_resize (GTK_WIDGET (notebook));