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;
158 guint child_has_focus : 1;
159 guint click_child : 3;
160 guint during_detach : 1;
161 guint during_reorder : 1;
162 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
163 guint has_scrolled : 1;
165 guint need_timer : 1;
166 guint show_border : 1;
168 guint scrollable : 1;
171 guint has_before_previous : 1;
172 guint has_before_next : 1;
173 guint has_after_previous : 1;
174 guint has_after_next : 1;
210 } GtkNotebookPointerPosition;
212 #define ARROW_IS_LEFT(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
213 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
228 CHILD_PROP_TAB_LABEL,
229 CHILD_PROP_MENU_LABEL,
231 CHILD_PROP_TAB_EXPAND,
233 CHILD_PROP_REORDERABLE,
234 CHILD_PROP_DETACHABLE
237 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
239 /* some useful defines for calculating coords */
240 #define PAGE_LEFT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x)
241 #define PAGE_RIGHT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
242 #define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
243 #define PAGE_TOP_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y)
244 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
245 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
246 #define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent (((GtkNotebookPage *) (_page_))->tab_label) == ((GtkWidget *) (_notebook_)))
248 struct _GtkNotebookPage
251 GtkWidget *tab_label;
252 GtkWidget *menu_label;
253 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
255 guint default_menu : 1; /* If true, we create the menu label ourself */
256 guint default_tab : 1; /* If true, we create the tab label ourself */
259 guint reorderable : 1;
260 guint detachable : 1;
262 /* if true, the tab label was visible on last allocation; we track this so
263 * that we know to redraw the tab area if a tab label was hidden then shown
264 * without changing position */
265 guint tab_allocated_visible : 1;
267 GtkRequisition requisition;
268 GtkAllocation allocation;
270 gulong mnemonic_activate_signal;
271 gulong notify_visible_handler;
274 static const GtkTargetEntry notebook_targets [] = {
275 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
278 #ifdef G_DISABLE_CHECKS
279 #define CHECK_FIND_CHILD(notebook, child) \
280 gtk_notebook_find_child (notebook, child, G_STRLOC)
282 #define CHECK_FIND_CHILD(notebook, child) \
283 gtk_notebook_find_child (notebook, child, NULL)
286 /*** GtkNotebook Methods ***/
287 static gboolean gtk_notebook_select_page (GtkNotebook *notebook,
288 gboolean move_focus);
289 static gboolean gtk_notebook_focus_tab (GtkNotebook *notebook,
290 GtkNotebookTab type);
291 static gboolean gtk_notebook_change_current_page (GtkNotebook *notebook,
293 static void gtk_notebook_move_focus_out (GtkNotebook *notebook,
294 GtkDirectionType direction_type);
295 static gboolean gtk_notebook_reorder_tab (GtkNotebook *notebook,
296 GtkDirectionType direction_type,
297 gboolean move_to_last);
298 static void gtk_notebook_remove_tab_label (GtkNotebook *notebook,
299 GtkNotebookPage *page);
300 static void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
304 static void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
309 /*** GObject Methods ***/
310 static void gtk_notebook_set_property (GObject *object,
314 static void gtk_notebook_get_property (GObject *object,
319 /*** GtkWidget Methods ***/
320 static void gtk_notebook_destroy (GtkWidget *widget);
321 static void gtk_notebook_map (GtkWidget *widget);
322 static void gtk_notebook_unmap (GtkWidget *widget);
323 static void gtk_notebook_realize (GtkWidget *widget);
324 static void gtk_notebook_unrealize (GtkWidget *widget);
325 static void gtk_notebook_get_preferred_width (GtkWidget *widget,
328 static void gtk_notebook_get_preferred_height(GtkWidget *widget,
331 static void gtk_notebook_get_preferred_width_for_height
336 static void gtk_notebook_get_preferred_height_for_width
341 static void gtk_notebook_size_allocate (GtkWidget *widget,
342 GtkAllocation *allocation);
343 static gint gtk_notebook_draw (GtkWidget *widget,
345 static gint gtk_notebook_button_press (GtkWidget *widget,
346 GdkEventButton *event);
347 static gint gtk_notebook_button_release (GtkWidget *widget,
348 GdkEventButton *event);
349 static gboolean gtk_notebook_popup_menu (GtkWidget *widget);
350 static gint gtk_notebook_leave_notify (GtkWidget *widget,
351 GdkEventCrossing *event);
352 static gint gtk_notebook_motion_notify (GtkWidget *widget,
353 GdkEventMotion *event);
354 static gint gtk_notebook_focus_in (GtkWidget *widget,
355 GdkEventFocus *event);
356 static gint gtk_notebook_focus_out (GtkWidget *widget,
357 GdkEventFocus *event);
358 static void gtk_notebook_grab_notify (GtkWidget *widget,
359 gboolean was_grabbed);
360 static void gtk_notebook_state_flags_changed (GtkWidget *widget,
361 GtkStateFlags previous_state);
362 static gint gtk_notebook_focus (GtkWidget *widget,
363 GtkDirectionType direction);
364 static void gtk_notebook_style_updated (GtkWidget *widget);
366 /*** Drag and drop Methods ***/
367 static void gtk_notebook_drag_begin (GtkWidget *widget,
368 GdkDragContext *context);
369 static void gtk_notebook_drag_end (GtkWidget *widget,
370 GdkDragContext *context);
371 static gboolean gtk_notebook_drag_failed (GtkWidget *widget,
372 GdkDragContext *context,
373 GtkDragResult result);
374 static gboolean gtk_notebook_drag_motion (GtkWidget *widget,
375 GdkDragContext *context,
379 static void gtk_notebook_drag_leave (GtkWidget *widget,
380 GdkDragContext *context,
382 static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
383 GdkDragContext *context,
387 static void gtk_notebook_drag_data_get (GtkWidget *widget,
388 GdkDragContext *context,
389 GtkSelectionData *data,
392 static void gtk_notebook_drag_data_received (GtkWidget *widget,
393 GdkDragContext *context,
396 GtkSelectionData *data,
400 /*** GtkContainer Methods ***/
401 static void gtk_notebook_set_child_property (GtkContainer *container,
406 static void gtk_notebook_get_child_property (GtkContainer *container,
411 static void gtk_notebook_add (GtkContainer *container,
413 static void gtk_notebook_remove (GtkContainer *container,
415 static void gtk_notebook_set_focus_child (GtkContainer *container,
417 static GType gtk_notebook_child_type (GtkContainer *container);
418 static void gtk_notebook_forall (GtkContainer *container,
419 gboolean include_internals,
420 GtkCallback callback,
421 gpointer callback_data);
422 static GtkWidgetPath * gtk_notebook_get_path_for_child (GtkContainer *container,
425 /*** GtkNotebook Methods ***/
426 static gint gtk_notebook_real_insert_page (GtkNotebook *notebook,
428 GtkWidget *tab_label,
429 GtkWidget *menu_label,
432 static GtkNotebook *gtk_notebook_create_window (GtkNotebook *notebook,
437 /*** GtkNotebook Private Functions ***/
438 static void gtk_notebook_redraw_tabs (GtkNotebook *notebook);
439 static void gtk_notebook_redraw_arrows (GtkNotebook *notebook);
440 static void gtk_notebook_real_remove (GtkNotebook *notebook,
442 static void gtk_notebook_update_labels (GtkNotebook *notebook);
443 static gint gtk_notebook_timer (GtkNotebook *notebook);
444 static void gtk_notebook_set_scroll_timer (GtkNotebook *notebook);
445 static gint gtk_notebook_page_compare (gconstpointer a,
447 static GList* gtk_notebook_find_child (GtkNotebook *notebook,
449 const gchar *function);
450 static GList * gtk_notebook_search_page (GtkNotebook *notebook,
453 gboolean find_visible);
454 static void gtk_notebook_child_reordered (GtkNotebook *notebook,
455 GtkNotebookPage *page);
457 /*** GtkNotebook Drawing Functions ***/
458 static void gtk_notebook_paint (GtkWidget *widget,
460 static void gtk_notebook_draw_tab (GtkNotebook *notebook,
461 GtkNotebookPage *page,
464 static void gtk_notebook_draw_arrow (GtkNotebook *notebook,
466 GtkNotebookArrow arrow);
468 /*** GtkNotebook Size Allocate Functions ***/
469 static void gtk_notebook_pages_allocate (GtkNotebook *notebook);
470 static gboolean gtk_notebook_page_allocate (GtkNotebook *notebook,
471 GtkNotebookPage *page);
472 static void gtk_notebook_calc_tabs (GtkNotebook *notebook,
478 /*** GtkNotebook Page Switch Methods ***/
479 static void gtk_notebook_real_switch_page (GtkNotebook *notebook,
483 /*** GtkNotebook Page Switch Functions ***/
484 static void gtk_notebook_switch_page (GtkNotebook *notebook,
485 GtkNotebookPage *page);
486 static gint gtk_notebook_page_select (GtkNotebook *notebook,
487 gboolean move_focus);
488 static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
490 static void gtk_notebook_menu_switch_page (GtkWidget *widget,
491 GtkNotebookPage *page);
493 /*** GtkNotebook Menu Functions ***/
494 static void gtk_notebook_menu_item_create (GtkNotebook *notebook,
496 static void gtk_notebook_menu_label_unparent (GtkWidget *widget,
498 static void gtk_notebook_menu_detacher (GtkWidget *widget,
501 /*** GtkNotebook Private Setters ***/
502 static void gtk_notebook_update_tab_states (GtkNotebook *notebook);
503 static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
507 static gboolean focus_tabs_in (GtkNotebook *notebook);
508 static gboolean focus_child_in (GtkNotebook *notebook,
509 GtkDirectionType direction);
511 static void stop_scrolling (GtkNotebook *notebook);
512 static void do_detach_tab (GtkNotebook *from,
519 static void gtk_notebook_buildable_init (GtkBuildableIface *iface);
520 static void gtk_notebook_buildable_add_child (GtkBuildable *buildable,
525 static guint notebook_signals[LAST_SIGNAL] = { 0 };
527 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
528 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
529 gtk_notebook_buildable_init))
532 add_tab_bindings (GtkBindingSet *binding_set,
533 GdkModifierType modifiers,
534 GtkDirectionType direction)
536 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
538 GTK_TYPE_DIRECTION_TYPE, direction);
539 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
541 GTK_TYPE_DIRECTION_TYPE, direction);
545 add_arrow_bindings (GtkBindingSet *binding_set,
547 GtkDirectionType direction)
549 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
551 gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
553 GTK_TYPE_DIRECTION_TYPE, direction);
554 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
556 GTK_TYPE_DIRECTION_TYPE, direction);
560 add_reorder_bindings (GtkBindingSet *binding_set,
562 GtkDirectionType direction,
563 gboolean move_to_last)
565 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
567 gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
569 GTK_TYPE_DIRECTION_TYPE, direction,
570 G_TYPE_BOOLEAN, move_to_last);
571 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
573 GTK_TYPE_DIRECTION_TYPE, direction,
574 G_TYPE_BOOLEAN, move_to_last);
578 gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
580 const GValue *handler_return,
583 gboolean continue_emission;
586 object = g_value_get_object (handler_return);
587 g_value_set_object (return_accu, object);
588 continue_emission = !object;
590 return continue_emission;
594 gtk_notebook_compute_expand (GtkWidget *widget,
598 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
599 GtkNotebookPrivate *priv = notebook->priv;
603 GtkNotebookPage *page;
608 for (list = priv->children; list; list = list->next)
613 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_HORIZONTAL);
616 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);
618 if (hexpand & vexpand)
622 *hexpand_p = hexpand;
623 *vexpand_p = vexpand;
627 gtk_notebook_class_init (GtkNotebookClass *class)
629 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
630 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
631 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
632 GtkBindingSet *binding_set;
634 gobject_class->set_property = gtk_notebook_set_property;
635 gobject_class->get_property = gtk_notebook_get_property;
637 widget_class->destroy = gtk_notebook_destroy;
638 widget_class->map = gtk_notebook_map;
639 widget_class->unmap = gtk_notebook_unmap;
640 widget_class->realize = gtk_notebook_realize;
641 widget_class->unrealize = gtk_notebook_unrealize;
642 widget_class->get_preferred_width = gtk_notebook_get_preferred_width;
643 widget_class->get_preferred_height = gtk_notebook_get_preferred_height;
644 widget_class->get_preferred_width_for_height = gtk_notebook_get_preferred_width_for_height;
645 widget_class->get_preferred_height_for_width = gtk_notebook_get_preferred_height_for_width;
646 widget_class->size_allocate = gtk_notebook_size_allocate;
647 widget_class->draw = gtk_notebook_draw;
648 widget_class->button_press_event = gtk_notebook_button_press;
649 widget_class->button_release_event = gtk_notebook_button_release;
650 widget_class->popup_menu = gtk_notebook_popup_menu;
651 widget_class->leave_notify_event = gtk_notebook_leave_notify;
652 widget_class->motion_notify_event = gtk_notebook_motion_notify;
653 widget_class->grab_notify = gtk_notebook_grab_notify;
654 widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
655 widget_class->focus_in_event = gtk_notebook_focus_in;
656 widget_class->focus_out_event = gtk_notebook_focus_out;
657 widget_class->focus = gtk_notebook_focus;
658 widget_class->style_updated = gtk_notebook_style_updated;
659 widget_class->drag_begin = gtk_notebook_drag_begin;
660 widget_class->drag_end = gtk_notebook_drag_end;
661 widget_class->drag_motion = gtk_notebook_drag_motion;
662 widget_class->drag_leave = gtk_notebook_drag_leave;
663 widget_class->drag_drop = gtk_notebook_drag_drop;
664 widget_class->drag_data_get = gtk_notebook_drag_data_get;
665 widget_class->drag_data_received = gtk_notebook_drag_data_received;
666 widget_class->drag_failed = gtk_notebook_drag_failed;
667 widget_class->compute_expand = gtk_notebook_compute_expand;
669 container_class->add = gtk_notebook_add;
670 container_class->remove = gtk_notebook_remove;
671 container_class->forall = gtk_notebook_forall;
672 container_class->set_focus_child = gtk_notebook_set_focus_child;
673 container_class->get_child_property = gtk_notebook_get_child_property;
674 container_class->set_child_property = gtk_notebook_set_child_property;
675 container_class->child_type = gtk_notebook_child_type;
676 container_class->get_path_for_child = gtk_notebook_get_path_for_child;
678 class->switch_page = gtk_notebook_real_switch_page;
679 class->insert_page = gtk_notebook_real_insert_page;
681 class->focus_tab = gtk_notebook_focus_tab;
682 class->select_page = gtk_notebook_select_page;
683 class->change_current_page = gtk_notebook_change_current_page;
684 class->move_focus_out = gtk_notebook_move_focus_out;
685 class->reorder_tab = gtk_notebook_reorder_tab;
686 class->create_window = gtk_notebook_create_window;
688 g_object_class_install_property (gobject_class,
690 g_param_spec_int ("page",
692 P_("The index of the current page"),
696 GTK_PARAM_READWRITE));
697 g_object_class_install_property (gobject_class,
699 g_param_spec_enum ("tab-pos",
701 P_("Which side of the notebook holds the tabs"),
702 GTK_TYPE_POSITION_TYPE,
704 GTK_PARAM_READWRITE));
705 g_object_class_install_property (gobject_class,
707 g_param_spec_boolean ("show-tabs",
709 P_("Whether tabs should be shown"),
711 GTK_PARAM_READWRITE));
712 g_object_class_install_property (gobject_class,
714 g_param_spec_boolean ("show-border",
716 P_("Whether the border should be shown"),
718 GTK_PARAM_READWRITE));
719 g_object_class_install_property (gobject_class,
721 g_param_spec_boolean ("scrollable",
723 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
725 GTK_PARAM_READWRITE));
726 g_object_class_install_property (gobject_class,
728 g_param_spec_boolean ("enable-popup",
730 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
732 GTK_PARAM_READWRITE));
735 * GtkNotebook:group-name:
737 * Group name for tab drag and drop.
741 g_object_class_install_property (gobject_class,
743 g_param_spec_string ("group-name",
745 P_("Group name for tab drag and drop"),
747 GTK_PARAM_READWRITE));
749 gtk_container_class_install_child_property (container_class,
750 CHILD_PROP_TAB_LABEL,
751 g_param_spec_string ("tab-label",
753 P_("The string displayed on the child's tab label"),
755 GTK_PARAM_READWRITE));
756 gtk_container_class_install_child_property (container_class,
757 CHILD_PROP_MENU_LABEL,
758 g_param_spec_string ("menu-label",
760 P_("The string displayed in the child's menu entry"),
762 GTK_PARAM_READWRITE));
763 gtk_container_class_install_child_property (container_class,
765 g_param_spec_int ("position",
767 P_("The index of the child in the parent"),
769 GTK_PARAM_READWRITE));
770 gtk_container_class_install_child_property (container_class,
771 CHILD_PROP_TAB_EXPAND,
772 g_param_spec_boolean ("tab-expand",
774 P_("Whether to expand the child's tab"),
776 GTK_PARAM_READWRITE));
777 gtk_container_class_install_child_property (container_class,
779 g_param_spec_boolean ("tab-fill",
781 P_("Whether the child's tab should fill the allocated area"),
783 GTK_PARAM_READWRITE));
785 gtk_container_class_install_child_property (container_class,
786 CHILD_PROP_REORDERABLE,
787 g_param_spec_boolean ("reorderable",
788 P_("Tab reorderable"),
789 P_("Whether the tab is reorderable by user action"),
791 GTK_PARAM_READWRITE));
792 gtk_container_class_install_child_property (container_class,
793 CHILD_PROP_DETACHABLE,
794 g_param_spec_boolean ("detachable",
795 P_("Tab detachable"),
796 P_("Whether the tab is detachable"),
798 GTK_PARAM_READWRITE));
801 * GtkNotebook:has-secondary-backward-stepper:
803 * The "has-secondary-backward-stepper" property determines whether
804 * a second backward arrow button is displayed on the opposite end
809 gtk_widget_class_install_style_property (widget_class,
810 g_param_spec_boolean ("has-secondary-backward-stepper",
811 P_("Secondary backward stepper"),
812 P_("Display a second backward arrow button on the opposite end of the tab area"),
814 GTK_PARAM_READABLE));
817 * GtkNotebook:has-secondary-forward-stepper:
819 * The "has-secondary-forward-stepper" property determines whether
820 * a second forward arrow button is displayed on the opposite end
825 gtk_widget_class_install_style_property (widget_class,
826 g_param_spec_boolean ("has-secondary-forward-stepper",
827 P_("Secondary forward stepper"),
828 P_("Display a second forward arrow button on the opposite end of the tab area"),
830 GTK_PARAM_READABLE));
833 * GtkNotebook:has-backward-stepper:
835 * The "has-backward-stepper" property determines whether
836 * the standard backward arrow button is displayed.
840 gtk_widget_class_install_style_property (widget_class,
841 g_param_spec_boolean ("has-backward-stepper",
842 P_("Backward stepper"),
843 P_("Display the standard backward arrow button"),
845 GTK_PARAM_READABLE));
848 * GtkNotebook:has-forward-stepper:
850 * The "has-forward-stepper" property determines whether
851 * the standard forward arrow button is displayed.
855 gtk_widget_class_install_style_property (widget_class,
856 g_param_spec_boolean ("has-forward-stepper",
857 P_("Forward stepper"),
858 P_("Display the standard forward arrow button"),
860 GTK_PARAM_READABLE));
863 * GtkNotebook:tab-overlap:
865 * The "tab-overlap" property defines size of tab overlap
870 gtk_widget_class_install_style_property (widget_class,
871 g_param_spec_int ("tab-overlap",
873 P_("Size of tab overlap area"),
877 GTK_PARAM_READABLE));
880 * GtkNotebook:tab-curvature:
882 * The "tab-curvature" property defines size of tab curvature.
886 gtk_widget_class_install_style_property (widget_class,
887 g_param_spec_int ("tab-curvature",
889 P_("Size of tab curvature"),
893 GTK_PARAM_READABLE));
896 * GtkNotebook:arrow-spacing:
898 * The "arrow-spacing" property defines the spacing between the scroll
899 * arrows and the tabs.
903 gtk_widget_class_install_style_property (widget_class,
904 g_param_spec_int ("arrow-spacing",
906 P_("Scroll arrow spacing"),
910 GTK_PARAM_READABLE));
913 * GtkNotebook:initial-gap:
915 * The "initial-gap" property defines the minimum size for the initial
916 * gap between the first tab.
920 gtk_widget_class_install_style_property (widget_class,
921 g_param_spec_int ("initial-gap",
923 P_("Initial gap before the first tab"),
927 GTK_PARAM_READABLE));
930 * GtkNotebook::switch-page:
931 * @notebook: the object which received the signal.
932 * @page: the new current page
933 * @page_num: the index of the page
935 * Emitted when the user or a function changes the current page.
937 notebook_signals[SWITCH_PAGE] =
938 g_signal_new (I_("switch-page"),
939 G_TYPE_FROM_CLASS (gobject_class),
941 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
943 _gtk_marshal_VOID__OBJECT_UINT,
947 notebook_signals[FOCUS_TAB] =
948 g_signal_new (I_("focus-tab"),
949 G_TYPE_FROM_CLASS (gobject_class),
950 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
951 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
953 _gtk_marshal_BOOLEAN__ENUM,
955 GTK_TYPE_NOTEBOOK_TAB);
956 notebook_signals[SELECT_PAGE] =
957 g_signal_new (I_("select-page"),
958 G_TYPE_FROM_CLASS (gobject_class),
959 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
960 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
962 _gtk_marshal_BOOLEAN__BOOLEAN,
965 notebook_signals[CHANGE_CURRENT_PAGE] =
966 g_signal_new (I_("change-current-page"),
967 G_TYPE_FROM_CLASS (gobject_class),
968 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
969 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
971 _gtk_marshal_BOOLEAN__INT,
974 notebook_signals[MOVE_FOCUS_OUT] =
975 g_signal_new (I_("move-focus-out"),
976 G_TYPE_FROM_CLASS (gobject_class),
977 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
978 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
980 _gtk_marshal_VOID__ENUM,
982 GTK_TYPE_DIRECTION_TYPE);
983 notebook_signals[REORDER_TAB] =
984 g_signal_new (I_("reorder-tab"),
985 G_TYPE_FROM_CLASS (gobject_class),
986 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
987 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
989 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
991 GTK_TYPE_DIRECTION_TYPE,
994 * GtkNotebook::page-reordered:
995 * @notebook: the #GtkNotebook
996 * @child: the child #GtkWidget affected
997 * @page_num: the new page number for @child
999 * the ::page-reordered signal is emitted in the notebook
1000 * right after a page has been reordered.
1004 notebook_signals[PAGE_REORDERED] =
1005 g_signal_new (I_("page-reordered"),
1006 G_TYPE_FROM_CLASS (gobject_class),
1008 G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1010 _gtk_marshal_VOID__OBJECT_UINT,
1015 * GtkNotebook::page-removed:
1016 * @notebook: the #GtkNotebook
1017 * @child: the child #GtkWidget affected
1018 * @page_num: the @child page number
1020 * the ::page-removed signal is emitted in the notebook
1021 * right after a page is removed from the notebook.
1025 notebook_signals[PAGE_REMOVED] =
1026 g_signal_new (I_("page-removed"),
1027 G_TYPE_FROM_CLASS (gobject_class),
1029 G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1031 _gtk_marshal_VOID__OBJECT_UINT,
1036 * GtkNotebook::page-added:
1037 * @notebook: the #GtkNotebook
1038 * @child: the child #GtkWidget affected
1039 * @page_num: the new page number for @child
1041 * the ::page-added signal is emitted in the notebook
1042 * right after a page is added to the notebook.
1046 notebook_signals[PAGE_ADDED] =
1047 g_signal_new (I_("page-added"),
1048 G_TYPE_FROM_CLASS (gobject_class),
1050 G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1052 _gtk_marshal_VOID__OBJECT_UINT,
1058 * GtkNotebook::create-window:
1059 * @notebook: the #GtkNotebook emitting the signal
1060 * @page: the tab of @notebook that is being detached
1061 * @x: the X coordinate where the drop happens
1062 * @y: the Y coordinate where the drop happens
1064 * The ::create-window signal is emitted when a detachable
1065 * tab is dropped on the root window.
1067 * A handler for this signal can create a window containing
1068 * a notebook where the tab will be attached. It is also
1069 * responsible for moving/resizing the window and adding the
1070 * necessary properties to the notebook (e.g. the
1071 * #GtkNotebook:group ).
1073 * Returns: (transfer none): a #GtkNotebook that @page should be
1074 * added to, or %NULL.
1078 notebook_signals[CREATE_WINDOW] =
1079 g_signal_new (I_("create-window"),
1080 G_TYPE_FROM_CLASS (gobject_class),
1082 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1083 gtk_object_handled_accumulator, NULL,
1084 _gtk_marshal_OBJECT__OBJECT_INT_INT,
1085 GTK_TYPE_NOTEBOOK, 3,
1086 GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
1088 binding_set = gtk_binding_set_by_class (class);
1089 gtk_binding_entry_add_signal (binding_set,
1092 G_TYPE_BOOLEAN, FALSE);
1093 gtk_binding_entry_add_signal (binding_set,
1094 GDK_KEY_KP_Space, 0,
1096 G_TYPE_BOOLEAN, FALSE);
1098 gtk_binding_entry_add_signal (binding_set,
1101 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1102 gtk_binding_entry_add_signal (binding_set,
1105 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1106 gtk_binding_entry_add_signal (binding_set,
1109 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1110 gtk_binding_entry_add_signal (binding_set,
1113 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1115 gtk_binding_entry_add_signal (binding_set,
1116 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1117 "change-current-page", 1,
1119 gtk_binding_entry_add_signal (binding_set,
1120 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1121 "change-current-page", 1,
1124 gtk_binding_entry_add_signal (binding_set,
1125 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1126 "change-current-page", 1,
1128 gtk_binding_entry_add_signal (binding_set,
1129 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1130 "change-current-page", 1,
1133 add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1134 add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1135 add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1136 add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1138 add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1139 add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1140 add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1141 add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1142 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1143 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1144 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1145 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1147 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1148 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1150 g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1152 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_NOTEBOOK_ACCESSIBLE);
1156 gtk_notebook_init (GtkNotebook *notebook)
1158 GtkNotebookPrivate *priv;
1159 GtkStyleContext *context;
1161 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1162 gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1164 notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1166 GtkNotebookPrivate);
1167 priv = notebook->priv;
1169 priv->cur_page = NULL;
1170 priv->children = NULL;
1171 priv->first_tab = NULL;
1172 priv->focus_tab = NULL;
1173 priv->event_window = NULL;
1176 priv->show_tabs = TRUE;
1177 priv->show_border = TRUE;
1178 priv->tab_pos = GTK_POS_TOP;
1179 priv->scrollable = FALSE;
1181 priv->click_child = 0;
1183 priv->need_timer = 0;
1184 priv->child_has_focus = FALSE;
1185 priv->focus_out = FALSE;
1187 priv->has_before_previous = 1;
1188 priv->has_before_next = 0;
1189 priv->has_after_previous = 0;
1190 priv->has_after_next = 1;
1193 priv->pressed_button = -1;
1194 priv->dnd_timer = 0;
1195 priv->switch_tab_timer = 0;
1196 priv->source_targets = gtk_target_list_new (notebook_targets,
1197 G_N_ELEMENTS (notebook_targets));
1198 priv->operation = DRAG_OPERATION_NONE;
1199 priv->detached_tab = NULL;
1200 priv->during_detach = FALSE;
1201 priv->has_scrolled = FALSE;
1203 gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1204 notebook_targets, G_N_ELEMENTS (notebook_targets),
1207 gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1209 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
1210 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
1214 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1216 iface->add_child = gtk_notebook_buildable_add_child;
1220 gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1221 GtkBuilder *builder,
1225 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1227 if (type && strcmp (type, "tab") == 0)
1231 page = gtk_notebook_get_nth_page (notebook, -1);
1232 /* To set the tab label widget, we must have already a child
1233 * inside the tab container. */
1234 g_assert (page != NULL);
1235 /* warn when Glade tries to overwrite label */
1236 if (gtk_notebook_get_tab_label (notebook, page))
1237 g_warning ("Overriding tab label for notebook");
1238 gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1240 else if (type && strcmp (type, "action-start") == 0)
1242 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1244 else if (type && strcmp (type, "action-end") == 0)
1246 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1249 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1251 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1255 gtk_notebook_select_page (GtkNotebook *notebook,
1256 gboolean move_focus)
1258 GtkNotebookPrivate *priv = notebook->priv;
1260 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1262 gtk_notebook_page_select (notebook, move_focus);
1270 gtk_notebook_focus_tab (GtkNotebook *notebook,
1271 GtkNotebookTab type)
1273 GtkNotebookPrivate *priv = notebook->priv;
1276 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1280 case GTK_NOTEBOOK_TAB_FIRST:
1281 list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1283 gtk_notebook_switch_focus_tab (notebook, list);
1285 case GTK_NOTEBOOK_TAB_LAST:
1286 list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1288 gtk_notebook_switch_focus_tab (notebook, list);
1299 gtk_notebook_change_current_page (GtkNotebook *notebook,
1302 GtkNotebookPrivate *priv = notebook->priv;
1303 GList *current = NULL;
1305 if (!priv->show_tabs)
1309 current = g_list_find (priv->children, priv->cur_page);
1313 current = gtk_notebook_search_page (notebook, current,
1314 offset < 0 ? STEP_PREV : STEP_NEXT,
1319 gboolean wrap_around;
1321 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1322 "gtk-keynav-wrap-around", &wrap_around,
1326 current = gtk_notebook_search_page (notebook, NULL,
1327 offset < 0 ? STEP_PREV : STEP_NEXT,
1333 offset += offset < 0 ? 1 : -1;
1337 gtk_notebook_switch_page (notebook, current->data);
1339 gtk_widget_error_bell (GTK_WIDGET (notebook));
1344 static GtkDirectionType
1345 get_effective_direction (GtkNotebook *notebook,
1346 GtkDirectionType direction)
1348 GtkNotebookPrivate *priv = notebook->priv;
1350 /* Remap the directions into the effective direction it would be for a
1351 * GTK_POS_TOP notebook
1354 #define D(rest) GTK_DIR_##rest
1356 static const GtkDirectionType translate_direction[2][4][6] = {
1357 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1358 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1359 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1360 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1361 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1362 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1363 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1364 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1369 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1371 return translate_direction[text_dir][priv->tab_pos][direction];
1375 get_effective_tab_pos (GtkNotebook *notebook)
1377 GtkNotebookPrivate *priv = notebook->priv;
1379 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1381 switch (priv->tab_pos)
1384 return GTK_POS_RIGHT;
1386 return GTK_POS_LEFT;
1391 return priv->tab_pos;
1395 get_tab_gap_pos (GtkNotebook *notebook)
1397 gint tab_pos = get_effective_tab_pos (notebook);
1398 gint gap_side = GTK_POS_BOTTOM;
1403 gap_side = GTK_POS_BOTTOM;
1405 case GTK_POS_BOTTOM:
1406 gap_side = GTK_POS_TOP;
1409 gap_side = GTK_POS_RIGHT;
1412 gap_side = GTK_POS_LEFT;
1420 gtk_notebook_move_focus_out (GtkNotebook *notebook,
1421 GtkDirectionType direction_type)
1423 GtkNotebookPrivate *priv = notebook->priv;
1424 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1425 GtkWidget *toplevel;
1427 if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1428 if (focus_tabs_in (notebook))
1430 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1431 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1434 /* At this point, we know we should be focusing out of the notebook entirely. We
1435 * do this by setting a flag, then propagating the focus motion to the notebook.
1437 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1438 if (!gtk_widget_is_toplevel (toplevel))
1441 g_object_ref (notebook);
1443 priv->focus_out = TRUE;
1444 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1445 priv->focus_out = FALSE;
1447 g_object_unref (notebook);
1451 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1453 GtkNotebookPrivate *priv = notebook->priv;
1456 if (position == tab)
1457 return g_list_position (priv->children, tab);
1459 /* check that we aren't inserting the tab in the
1460 * same relative position, taking packing into account */
1461 elem = (position) ? position->prev : g_list_last (priv->children);
1464 return g_list_position (priv->children, tab);
1466 /* now actually reorder the tab */
1467 if (priv->first_tab == tab)
1468 priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1471 priv->children = g_list_remove_link (priv->children, tab);
1474 elem = g_list_last (priv->children);
1477 elem = position->prev;
1478 position->prev = tab;
1484 priv->children = tab;
1487 tab->next = position;
1489 return g_list_position (priv->children, tab);
1493 gtk_notebook_reorder_tab (GtkNotebook *notebook,
1494 GtkDirectionType direction_type,
1495 gboolean move_to_last)
1497 GtkNotebookPrivate *priv = notebook->priv;
1498 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1499 GList *last, *child;
1502 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1505 if (!priv->cur_page ||
1506 !priv->cur_page->reorderable)
1509 if (effective_direction != GTK_DIR_LEFT &&
1510 effective_direction != GTK_DIR_RIGHT)
1515 child = priv->focus_tab;
1520 child = gtk_notebook_search_page (notebook, last,
1521 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1529 child = gtk_notebook_search_page (notebook, priv->focus_tab,
1530 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1533 if (!child || child->data == priv->cur_page)
1536 if (effective_direction == GTK_DIR_RIGHT)
1537 page_num = reorder_tab (notebook, child->next, priv->focus_tab);
1539 page_num = reorder_tab (notebook, child, priv->focus_tab);
1541 gtk_notebook_pages_allocate (notebook);
1543 g_signal_emit (notebook,
1544 notebook_signals[PAGE_REORDERED],
1546 ((GtkNotebookPage *) priv->focus_tab->data)->child,
1555 * Creates a new #GtkNotebook widget with no pages.
1557 * Return value: the newly created #GtkNotebook
1560 gtk_notebook_new (void)
1562 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1565 /* Private GObject Methods :
1567 * gtk_notebook_set_property
1568 * gtk_notebook_get_property
1571 gtk_notebook_set_property (GObject *object,
1573 const GValue *value,
1576 GtkNotebook *notebook;
1578 notebook = GTK_NOTEBOOK (object);
1582 case PROP_SHOW_TABS:
1583 gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1585 case PROP_SHOW_BORDER:
1586 gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1588 case PROP_SCROLLABLE:
1589 gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1591 case PROP_ENABLE_POPUP:
1592 if (g_value_get_boolean (value))
1593 gtk_notebook_popup_enable (notebook);
1595 gtk_notebook_popup_disable (notebook);
1598 gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1601 gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1603 case PROP_GROUP_NAME:
1604 gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1607 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1613 gtk_notebook_get_property (GObject *object,
1618 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1619 GtkNotebookPrivate *priv = notebook->priv;
1623 case PROP_SHOW_TABS:
1624 g_value_set_boolean (value, priv->show_tabs);
1626 case PROP_SHOW_BORDER:
1627 g_value_set_boolean (value, priv->show_border);
1629 case PROP_SCROLLABLE:
1630 g_value_set_boolean (value, priv->scrollable);
1632 case PROP_ENABLE_POPUP:
1633 g_value_set_boolean (value, priv->menu != NULL);
1636 g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1639 g_value_set_enum (value, priv->tab_pos);
1641 case PROP_GROUP_NAME:
1642 g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1645 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1650 /* Private GtkWidget Methods :
1652 * gtk_notebook_destroy
1654 * gtk_notebook_unmap
1655 * gtk_notebook_realize
1656 * gtk_notebook_size_request
1657 * gtk_notebook_size_allocate
1659 * gtk_notebook_scroll
1660 * gtk_notebook_button_press
1661 * gtk_notebook_button_release
1662 * gtk_notebook_popup_menu
1663 * gtk_notebook_leave_notify
1664 * gtk_notebook_motion_notify
1665 * gtk_notebook_focus_in
1666 * gtk_notebook_focus_out
1667 * gtk_notebook_style_updated
1668 * gtk_notebook_drag_begin
1669 * gtk_notebook_drag_end
1670 * gtk_notebook_drag_failed
1671 * gtk_notebook_drag_motion
1672 * gtk_notebook_drag_drop
1673 * gtk_notebook_drag_data_get
1674 * gtk_notebook_drag_data_received
1677 gtk_notebook_destroy (GtkWidget *widget)
1679 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1680 GtkNotebookPrivate *priv = notebook->priv;
1683 gtk_notebook_popup_disable (notebook);
1685 if (priv->source_targets)
1687 gtk_target_list_unref (priv->source_targets);
1688 priv->source_targets = NULL;
1691 if (priv->switch_tab_timer)
1693 g_source_remove (priv->switch_tab_timer);
1694 priv->switch_tab_timer = 0;
1697 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->destroy (widget);
1701 gtk_notebook_get_event_window_position (GtkNotebook *notebook,
1702 GdkRectangle *rectangle)
1704 GtkNotebookPrivate *priv = notebook->priv;
1705 GtkAllocation allocation, action_allocation;
1706 GtkWidget *widget = GTK_WIDGET (notebook);
1707 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1708 GtkNotebookPage *visible_page = NULL;
1710 gint tab_pos = get_effective_tab_pos (notebook);
1714 for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1716 GtkNotebookPage *page = tmp_list->data;
1717 if (gtk_widget_get_visible (page->child))
1719 visible_page = page;
1724 if (priv->show_tabs && visible_page)
1728 gtk_widget_get_allocation (widget, &allocation);
1730 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1731 rectangle->x = allocation.x + border_width;
1732 rectangle->y = allocation.y + border_width;
1737 case GTK_POS_BOTTOM:
1738 rectangle->width = allocation.width - 2 * border_width;
1739 rectangle->height = visible_page->requisition.height;
1740 if (tab_pos == GTK_POS_BOTTOM)
1741 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1743 for (i = 0; i < N_ACTION_WIDGETS; i++)
1745 if (priv->action_widget[i] &&
1746 gtk_widget_get_visible (priv->action_widget[i]))
1748 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1750 rectangle->width -= action_allocation.width;
1751 if ((!is_rtl && i == ACTION_WIDGET_START) ||
1752 (is_rtl && i == ACTION_WIDGET_END))
1753 rectangle->x += action_allocation.width;
1759 rectangle->width = visible_page->requisition.width;
1760 rectangle->height = allocation.height - 2 * border_width;
1761 if (tab_pos == GTK_POS_RIGHT)
1762 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1764 for (i = 0; i < N_ACTION_WIDGETS; i++)
1766 if (priv->action_widget[i] &&
1767 gtk_widget_get_visible (priv->action_widget[i]))
1769 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1771 rectangle->height -= action_allocation.height;
1773 if (i == ACTION_WIDGET_START)
1774 rectangle->y += action_allocation.height;
1787 rectangle->x = rectangle->y = 0;
1788 rectangle->width = rectangle->height = 10;
1796 gtk_notebook_map (GtkWidget *widget)
1798 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1799 GtkNotebookPrivate *priv = notebook->priv;
1800 GtkNotebookPage *page;
1804 gtk_widget_set_mapped (widget, TRUE);
1806 if (priv->cur_page &&
1807 gtk_widget_get_visible (priv->cur_page->child) &&
1808 !gtk_widget_get_mapped (priv->cur_page->child))
1809 gtk_widget_map (priv->cur_page->child);
1811 for (i = 0; i < N_ACTION_WIDGETS; i++)
1813 if (priv->action_widget[i] &&
1814 gtk_widget_get_visible (priv->action_widget[i]) &&
1815 gtk_widget_get_child_visible (priv->action_widget[i]) &&
1816 !gtk_widget_get_mapped (priv->action_widget[i]))
1817 gtk_widget_map (priv->action_widget[i]);
1820 if (priv->scrollable)
1821 gtk_notebook_pages_allocate (notebook);
1824 children = priv->children;
1828 page = children->data;
1829 children = children->next;
1831 if (page->tab_label &&
1832 gtk_widget_get_visible (page->tab_label) &&
1833 !gtk_widget_get_mapped (page->tab_label))
1834 gtk_widget_map (page->tab_label);
1838 if (gtk_notebook_get_event_window_position (notebook, NULL))
1839 gdk_window_show_unraised (priv->event_window);
1843 gtk_notebook_unmap (GtkWidget *widget)
1845 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1846 GtkNotebookPrivate *priv = notebook->priv;
1848 stop_scrolling (notebook);
1850 gtk_widget_set_mapped (widget, FALSE);
1852 gdk_window_hide (priv->event_window);
1854 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1858 gtk_notebook_realize (GtkWidget *widget)
1860 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1861 GtkNotebookPrivate *priv = notebook->priv;
1863 GdkWindowAttr attributes;
1864 gint attributes_mask;
1865 GdkRectangle event_window_pos;
1867 gtk_widget_set_realized (widget, TRUE);
1869 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1871 window = gtk_widget_get_parent_window (widget);
1872 gtk_widget_set_window (widget, window);
1873 g_object_ref (window);
1875 attributes.window_type = GDK_WINDOW_CHILD;
1876 attributes.x = event_window_pos.x;
1877 attributes.y = event_window_pos.y;
1878 attributes.width = event_window_pos.width;
1879 attributes.height = event_window_pos.height;
1880 attributes.wclass = GDK_INPUT_ONLY;
1881 attributes.event_mask = gtk_widget_get_events (widget);
1882 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1883 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1884 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1885 attributes_mask = GDK_WA_X | GDK_WA_Y;
1887 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1888 &attributes, attributes_mask);
1889 gdk_window_set_user_data (priv->event_window, notebook);
1893 gtk_notebook_unrealize (GtkWidget *widget)
1895 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1896 GtkNotebookPrivate *priv = notebook->priv;
1898 gdk_window_set_user_data (priv->event_window, NULL);
1899 gdk_window_destroy (priv->event_window);
1900 priv->event_window = NULL;
1902 if (priv->drag_window)
1904 gdk_window_set_user_data (priv->drag_window, NULL);
1905 gdk_window_destroy (priv->drag_window);
1906 priv->drag_window = NULL;
1909 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1912 static GtkRegionFlags
1913 _gtk_notebook_get_tab_flags (GtkNotebook *notebook,
1914 GtkNotebookPage *page)
1916 GtkNotebookPrivate *priv = notebook->priv;
1917 gint i = 0, page_num = -1;
1918 GtkRegionFlags flags = 0;
1919 gboolean is_last = FALSE;
1922 for (pages = priv->children; pages; pages = pages->next)
1924 GtkNotebookPage *p = pages->data;
1926 if (!p->tab_label || !gtk_widget_get_visible (p->tab_label))
1931 /* No need to keep counting tabs after it */
1935 is_last = pages->next == NULL;
1943 if ((page_num) % 2 == 0)
1944 flags |= GTK_REGION_EVEN;
1946 flags |= GTK_REGION_ODD;
1949 flags |= GTK_REGION_FIRST;
1952 flags |= GTK_REGION_LAST;
1957 static GtkStateFlags
1958 notebook_tab_prepare_style_context (GtkNotebook *notebook,
1959 GtkNotebookPage *page,
1960 GtkStyleContext *context,
1963 gint tab_pos = get_effective_tab_pos (notebook);
1964 GtkRegionFlags flags = 0;
1965 GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
1968 page == notebook->priv->cur_page)
1969 state = GTK_STATE_FLAG_ACTIVE;
1971 gtk_style_context_set_state (context, state);
1973 if (use_flags && (page != NULL))
1974 flags = _gtk_notebook_get_tab_flags (notebook, page);
1976 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB, flags);
1981 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
1983 case GTK_POS_BOTTOM:
1984 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
1987 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
1990 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
2000 gtk_notebook_get_preferred_tabs_size (GtkNotebook *notebook,
2001 GtkRequisition *requisition)
2003 GtkNotebookPrivate *priv;
2006 gint tab_height = 0;
2010 gint action_width = 0;
2011 gint action_height = 0;
2012 guint vis_pages = 0;
2014 GtkNotebookPage *page;
2015 GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
2016 GtkRequisition child_requisition;
2017 GtkStyleContext *context;
2023 gint scroll_arrow_hlength;
2024 gint scroll_arrow_vlength;
2026 priv = notebook->priv;
2027 widget = GTK_WIDGET (notebook);
2028 context = gtk_widget_get_style_context (widget);
2029 gtk_widget_style_get (widget,
2030 "focus-line-width", &focus_width,
2031 "focus-padding", &focus_pad,
2032 "tab-overlap", &tab_overlap,
2033 "tab-curvature", &tab_curvature,
2034 "arrow-spacing", &arrow_spacing,
2035 "scroll-arrow-hlength", &scroll_arrow_hlength,
2036 "scroll-arrow-vlength", &scroll_arrow_vlength,
2039 for (children = priv->children; children;
2040 children = children->next)
2042 page = children->data;
2044 if (gtk_widget_get_visible (page->child))
2046 GtkBorder tab_padding;
2047 GtkStateFlags state;
2051 if (!gtk_widget_get_visible (page->tab_label))
2052 gtk_widget_show (page->tab_label);
2054 gtk_widget_get_preferred_size (page->tab_label,
2055 &child_requisition, NULL);
2057 /* Get border/padding for tab */
2058 gtk_style_context_save (context);
2059 state = notebook_tab_prepare_style_context (notebook, page, context, TRUE);
2060 gtk_style_context_get_padding (context, state, &tab_padding);
2061 gtk_style_context_restore (context);
2063 page->requisition.width = child_requisition.width +
2064 tab_padding.left + tab_padding.right + 2 * (focus_width + focus_pad);
2066 page->requisition.height = child_requisition.height +
2067 tab_padding.top + tab_padding.bottom + 2 * (focus_width + focus_pad);
2069 switch (priv->tab_pos)
2072 case GTK_POS_BOTTOM:
2073 tab_height = MAX (tab_height, page->requisition.height);
2074 tab_max = MAX (tab_max, page->requisition.width);
2078 tab_width = MAX (tab_width, page->requisition.width);
2079 tab_max = MAX (tab_max, page->requisition.height);
2083 else if (gtk_widget_get_visible (page->tab_label))
2084 gtk_widget_hide (page->tab_label);
2087 children = priv->children;
2091 for (i = 0; i < N_ACTION_WIDGETS; i++)
2093 if (priv->action_widget[i])
2095 gtk_widget_get_preferred_size (priv->action_widget[i],
2096 &action_widget_requisition[i], NULL);
2100 switch (priv->tab_pos)
2103 case GTK_POS_BOTTOM:
2104 if (tab_height == 0)
2107 if (priv->scrollable)
2108 tab_height = MAX (tab_height, scroll_arrow_hlength);
2110 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
2111 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
2113 padding = 2 * tab_curvature - tab_overlap;
2117 page = children->data;
2118 children = children->next;
2120 if (!gtk_widget_get_visible (page->child))
2123 page->requisition.width += padding;
2125 tab_width += page->requisition.width;
2126 page->requisition.height = tab_height;
2129 if (priv->scrollable)
2130 tab_width = MIN (tab_width,
2131 tab_max + 2 * (scroll_arrow_hlength + arrow_spacing));
2133 action_width += action_widget_requisition[ACTION_WIDGET_START].width;
2134 action_width += action_widget_requisition[ACTION_WIDGET_END].width;
2135 requisition->width = tab_width + tab_overlap + action_width;
2137 requisition->height = tab_height;
2144 if (priv->scrollable)
2145 tab_width = MAX (tab_width, arrow_spacing + 2 * scroll_arrow_vlength);
2147 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2148 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2150 padding = 2 * tab_curvature - tab_overlap;
2155 page = children->data;
2156 children = children->next;
2158 if (!gtk_widget_get_visible (page->child))
2161 page->requisition.width = tab_width;
2163 page->requisition.height += padding;
2165 tab_height += page->requisition.height;
2168 if (priv->scrollable)
2169 tab_height = MIN (tab_height,
2170 tab_max + (2 * scroll_arrow_vlength + arrow_spacing));
2171 action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2172 action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2174 requisition->height = tab_height + tab_overlap + action_height;
2176 requisition->height = MAX (requisition->height, tab_max + tab_overlap);
2178 requisition->width = tab_width;
2181 g_assert_not_reached ();
2182 requisition->width = 0;
2183 requisition->height = 0;
2188 requisition->width = 0;
2189 requisition->height = 0;
2194 get_preferred_size_for_size (GtkWidget *widget,
2195 GtkOrientation orientation,
2200 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2202 gtk_widget_get_preferred_width (widget, minimum, natural);
2204 gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
2207 gtk_widget_get_preferred_height (widget, minimum, natural);
2209 gtk_widget_get_preferred_height_for_width (widget, size, minimum, natural);
2213 gtk_notebook_size_request (GtkWidget *widget,
2214 GtkOrientation orientation,
2219 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2220 GtkNotebookPrivate *priv = notebook->priv;
2221 GtkNotebookPage *page;
2223 gint child_minimum, child_natural;
2224 gboolean switch_page = FALSE;
2231 for (children = priv->children, vis_pages = 0; children;
2232 children = children->next)
2235 page = children->data;
2237 if (gtk_widget_get_visible (page->child))
2240 get_preferred_size_for_size (page->child,
2246 *minimum = MAX (*minimum, child_minimum);
2247 *natural = MAX (*natural, child_natural);
2249 if (priv->menu && page->menu_label)
2251 parent = gtk_widget_get_parent (page->menu_label);
2252 if (parent && !gtk_widget_get_visible (parent))
2253 gtk_widget_show (parent);
2258 if (page == priv->cur_page)
2261 if (priv->menu && page->menu_label)
2263 parent = gtk_widget_get_parent (page->menu_label);
2264 if (parent && gtk_widget_get_visible (parent))
2265 gtk_widget_hide (parent);
2270 if (priv->show_border || priv->show_tabs)
2272 GtkStyleContext *context;
2273 GtkBorder notebook_padding;
2275 context = gtk_widget_get_style_context (widget);
2276 gtk_style_context_get_padding (context, 0, ¬ebook_padding);
2278 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2280 *minimum += notebook_padding.left + notebook_padding.right;
2281 *natural += notebook_padding.left + notebook_padding.right;
2285 *minimum += notebook_padding.top + notebook_padding.bottom;
2286 *natural += notebook_padding.top + notebook_padding.bottom;
2289 if (priv->show_tabs)
2291 GtkRequisition tabs_requisition = { 0, 0 };
2293 gtk_notebook_get_preferred_tabs_size (notebook, &tabs_requisition);
2294 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2296 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
2298 *minimum = MAX (*minimum, tabs_requisition.width);
2299 *natural = MAX (*minimum, *natural);
2303 *minimum += tabs_requisition.width;
2304 *natural += tabs_requisition.width;
2309 if (priv->tab_pos == GTK_POS_LEFT || priv->tab_pos == GTK_POS_RIGHT)
2311 *minimum = MAX (*minimum, tabs_requisition.height);
2312 *natural = MAX (*minimum, *natural);
2316 *minimum += tabs_requisition.height;
2317 *natural += tabs_requisition.height;
2323 for (children = priv->children; children;
2324 children = children->next)
2326 page = children->data;
2328 if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2329 gtk_widget_hide (page->tab_label);
2334 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2336 *minimum += border_width * 2;
2337 *natural += border_width * 2;
2343 for (children = priv->children; children;
2344 children = children->next)
2346 page = children->data;
2347 if (gtk_widget_get_visible (page->child))
2349 gtk_notebook_switch_page (notebook, page);
2354 else if (gtk_widget_get_visible (widget))
2356 *minimum = border_width * 2;
2359 if (vis_pages && !priv->cur_page)
2361 children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2364 priv->first_tab = children;
2365 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2371 gtk_notebook_get_preferred_width_for_height (GtkWidget *widget,
2376 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
2380 gtk_notebook_get_preferred_height_for_width (GtkWidget *widget,
2385 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
2389 gtk_notebook_get_preferred_width (GtkWidget *widget,
2393 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural);
2397 gtk_notebook_get_preferred_height (GtkWidget *widget,
2401 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural);
2405 gtk_notebook_size_allocate (GtkWidget *widget,
2406 GtkAllocation *allocation)
2408 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2409 GtkNotebookPrivate *priv = notebook->priv;
2410 gint tab_pos = get_effective_tab_pos (notebook);
2414 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2416 gtk_widget_set_allocation (widget, allocation);
2418 if (gtk_widget_get_realized (widget))
2420 GdkRectangle position;
2422 if (gtk_notebook_get_event_window_position (notebook, &position))
2424 gdk_window_move_resize (priv->event_window,
2425 position.x, position.y,
2426 position.width, position.height);
2427 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2428 gdk_window_show_unraised (priv->event_window);
2431 gdk_window_hide (priv->event_window);
2436 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2437 GtkNotebookPage *page;
2438 GtkAllocation child_allocation;
2442 child_allocation.x = allocation->x + border_width;
2443 child_allocation.y = allocation->y + border_width;
2444 child_allocation.width = MAX (1, allocation->width - border_width * 2);
2445 child_allocation.height = MAX (1, allocation->height - border_width * 2);
2447 if (priv->show_tabs || priv->show_border)
2449 GtkStyleContext *context;
2452 context = gtk_widget_get_style_context (widget);
2453 gtk_style_context_get_padding (context, 0, &padding);
2455 child_allocation.x += padding.left;
2456 child_allocation.y += padding.top;
2457 child_allocation.width = MAX (1, child_allocation.width - padding.left - padding.right);
2458 child_allocation.height = MAX (1, child_allocation.height - padding.top - padding.bottom);
2460 if (priv->show_tabs && priv->children && priv->cur_page)
2465 child_allocation.y += priv->cur_page->requisition.height;
2466 case GTK_POS_BOTTOM:
2467 child_allocation.height =
2468 MAX (1, child_allocation.height -
2469 priv->cur_page->requisition.height);
2472 child_allocation.x += priv->cur_page->requisition.width;
2474 child_allocation.width =
2475 MAX (1, child_allocation.width -
2476 priv->cur_page->requisition.width);
2480 for (i = 0; i < N_ACTION_WIDGETS; i++)
2482 GtkAllocation widget_allocation;
2483 GtkRequisition requisition;
2485 if (!priv->action_widget[i])
2488 widget_allocation.x = allocation->x + border_width;
2489 widget_allocation.y = allocation->y + border_width;
2490 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2492 gtk_widget_get_preferred_size (priv->action_widget[i],
2493 &requisition, NULL);
2497 case GTK_POS_BOTTOM:
2498 widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2501 widget_allocation.width = requisition.width;
2502 widget_allocation.height = priv->cur_page->requisition.height - padding.top;
2504 if ((i == ACTION_WIDGET_START && is_rtl) ||
2505 (i == ACTION_WIDGET_END && !is_rtl))
2506 widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2507 if (tab_pos == GTK_POS_TOP) /* no fall through */
2508 widget_allocation.y += 2 * focus_width;
2511 widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2514 widget_allocation.height = requisition.height;
2515 widget_allocation.width = priv->cur_page->requisition.width - padding.left;
2517 if (i == ACTION_WIDGET_END)
2518 widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2519 if (tab_pos == GTK_POS_LEFT) /* no fall through */
2520 widget_allocation.x += 2 * focus_width;
2524 gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2529 children = priv->children;
2532 page = children->data;
2533 children = children->next;
2535 if (gtk_widget_get_visible (page->child))
2536 gtk_widget_size_allocate (page->child, &child_allocation);
2539 gtk_notebook_pages_allocate (notebook);
2544 gtk_notebook_draw (GtkWidget *widget,
2547 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2548 GtkNotebookPrivate *priv = notebook->priv;
2549 GtkAllocation allocation;
2553 gtk_widget_get_allocation (widget, &allocation);
2555 window = gtk_widget_get_window (widget);
2556 if (gtk_cairo_should_draw_window (cr, window))
2560 cairo_translate (cr, -allocation.x, -allocation.y);
2561 gtk_notebook_paint (widget, cr);
2565 if (priv->show_tabs)
2567 GtkNotebookPage *page;
2570 for (pages = priv->children; pages; pages = pages->next)
2572 page = GTK_NOTEBOOK_PAGE (pages);
2574 if (gtk_widget_get_parent (page->tab_label) == widget)
2575 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2576 page->tab_label, cr);
2580 if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2581 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2582 priv->cur_page->child,
2584 if (priv->show_tabs)
2586 for (i = 0; i < N_ACTION_WIDGETS; i++)
2588 if (priv->action_widget[i])
2589 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2590 priv->action_widget[i], cr);
2595 if (priv->operation == DRAG_OPERATION_REORDER &&
2596 gtk_cairo_should_draw_window (cr, priv->drag_window))
2598 GtkStyleContext *context;
2602 gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2603 context = gtk_widget_get_style_context (widget);
2605 /* FIXME: This is a workaround to make tabs reordering work better
2606 * with engines with rounded tabs. If the drag window background
2607 * isn't set, the rounded corners would be black.
2609 * Ideally, these corners should be made transparent, Either by using
2610 * ARGB visuals or shape windows.
2612 gtk_style_context_get_background_color (context, 0, &bg_color);
2613 gdk_cairo_set_source_rgba (cr, &bg_color);
2616 gtk_notebook_draw_tab (notebook,
2622 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2623 priv->cur_page->tab_label, cr);
2630 gtk_notebook_show_arrows (GtkNotebook *notebook)
2632 GtkNotebookPrivate *priv = notebook->priv;
2633 gboolean show_arrow = FALSE;
2636 if (!priv->scrollable)
2639 children = priv->children;
2642 GtkNotebookPage *page = children->data;
2644 if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2647 children = children->next;
2654 gtk_notebook_get_arrow_rect (GtkNotebook *notebook,
2655 GdkRectangle *rectangle,
2656 GtkNotebookArrow arrow)
2658 GtkNotebookPrivate *priv = notebook->priv;
2659 GdkRectangle event_window_pos;
2660 gboolean before = ARROW_IS_BEFORE (arrow);
2661 gboolean left = ARROW_IS_LEFT (arrow);
2663 if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2665 gint scroll_arrow_hlength;
2666 gint scroll_arrow_vlength;
2668 gtk_widget_style_get (GTK_WIDGET (notebook),
2669 "scroll-arrow-hlength", &scroll_arrow_hlength,
2670 "scroll-arrow-vlength", &scroll_arrow_vlength,
2673 switch (priv->tab_pos)
2677 rectangle->width = scroll_arrow_vlength;
2678 rectangle->height = scroll_arrow_vlength;
2680 if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2681 (!before && (priv->has_after_previous != priv->has_after_next)))
2682 rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2684 rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2686 rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2687 rectangle->y = event_window_pos.y;
2689 rectangle->y += event_window_pos.height - rectangle->height;
2693 case GTK_POS_BOTTOM:
2694 rectangle->width = scroll_arrow_hlength;
2695 rectangle->height = scroll_arrow_hlength;
2699 if (left || !priv->has_before_previous)
2700 rectangle->x = event_window_pos.x;
2702 rectangle->x = event_window_pos.x + rectangle->width;
2706 if (!left || !priv->has_after_next)
2707 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2709 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2711 rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2717 static GtkNotebookArrow
2718 gtk_notebook_get_arrow (GtkNotebook *notebook,
2722 GtkNotebookPrivate *priv = notebook->priv;
2723 GdkRectangle arrow_rect;
2724 GdkRectangle event_window_pos;
2727 GtkNotebookArrow arrow[4];
2729 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2730 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2731 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2732 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2734 if (gtk_notebook_show_arrows (notebook))
2736 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2737 for (i = 0; i < 4; i++)
2739 if (arrow[i] == ARROW_NONE)
2742 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2744 x0 = x - arrow_rect.x;
2745 y0 = y - arrow_rect.y;
2747 if (y0 >= 0 && y0 < arrow_rect.height &&
2748 x0 >= 0 && x0 < arrow_rect.width)
2757 gtk_notebook_do_arrow (GtkNotebook *notebook,
2758 GtkNotebookArrow arrow)
2760 GtkNotebookPrivate *priv = notebook->priv;
2761 GtkWidget *widget = GTK_WIDGET (notebook);
2762 gboolean is_rtl, left;
2764 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2765 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2766 (!ARROW_IS_LEFT (arrow) && is_rtl);
2768 if (!priv->focus_tab ||
2769 gtk_notebook_search_page (notebook, priv->focus_tab,
2770 left ? STEP_PREV : STEP_NEXT,
2773 gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2774 gtk_widget_grab_focus (widget);
2779 gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2780 GtkNotebookArrow arrow,
2783 GtkNotebookPrivate *priv = notebook->priv;
2784 GtkWidget *widget = GTK_WIDGET (notebook);
2785 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2786 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2787 (!ARROW_IS_LEFT (arrow) && is_rtl);
2789 if (!gtk_widget_has_focus (widget))
2790 gtk_widget_grab_focus (widget);
2792 priv->button = button;
2793 priv->click_child = arrow;
2797 gtk_notebook_do_arrow (notebook, arrow);
2798 gtk_notebook_set_scroll_timer (notebook);
2800 else if (button == 2)
2801 gtk_notebook_page_select (notebook, TRUE);
2802 else if (button == 3)
2803 gtk_notebook_switch_focus_tab (notebook,
2804 gtk_notebook_search_page (notebook,
2806 left ? STEP_NEXT : STEP_PREV,
2808 gtk_notebook_redraw_arrows (notebook);
2814 get_widget_coordinates (GtkWidget *widget,
2819 GdkWindow *window = ((GdkEventAny *)event)->window;
2822 if (!gdk_event_get_coords (event, &tx, &ty))
2825 while (window && window != gtk_widget_get_window (widget))
2827 gint window_x, window_y;
2829 gdk_window_get_position (window, &window_x, &window_y);
2833 window = gdk_window_get_parent (window);
2848 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2850 GtkNotebookPrivate *priv = notebook->priv;
2851 GtkNotebookPage *page;
2854 children = priv->children;
2857 page = children->data;
2859 if (gtk_widget_get_visible (page->child) &&
2860 page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2861 (x >= page->allocation.x) &&
2862 (y >= page->allocation.y) &&
2863 (x <= (page->allocation.x + page->allocation.width)) &&
2864 (y <= (page->allocation.y + page->allocation.height)))
2867 children = children->next;
2874 gtk_notebook_button_press (GtkWidget *widget,
2875 GdkEventButton *event)
2877 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2878 GtkNotebookPrivate *priv = notebook->priv;
2879 GtkNotebookPage *page;
2881 GtkNotebookArrow arrow;
2884 if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2888 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2891 arrow = gtk_notebook_get_arrow (notebook, x, y);
2893 return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2895 if (priv->menu && gdk_event_triggers_context_menu ((GdkEvent *) event))
2897 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2898 NULL, NULL, 3, event->time);
2902 if (event->button != 1)
2905 priv->button = event->button;
2907 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2909 gboolean page_changed, was_focus;
2912 page_changed = page != priv->cur_page;
2913 was_focus = gtk_widget_is_focus (widget);
2915 gtk_notebook_switch_focus_tab (notebook, tab);
2916 gtk_widget_grab_focus (widget);
2918 if (page_changed && !was_focus)
2919 gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2921 /* save press to possibly begin a drag */
2922 if (page->reorderable || page->detachable)
2924 priv->during_detach = FALSE;
2925 priv->during_reorder = FALSE;
2926 priv->pressed_button = event->button;
2931 priv->drag_begin_x = priv->mouse_x;
2932 priv->drag_begin_y = priv->mouse_y;
2933 priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2934 priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2942 popup_position_func (GtkMenu *menu,
2948 GtkNotebook *notebook = data;
2949 GtkNotebookPrivate *priv = notebook->priv;
2950 GtkAllocation allocation;
2952 GtkRequisition requisition;
2954 if (priv->focus_tab)
2956 GtkNotebookPage *page;
2958 page = priv->focus_tab->data;
2959 w = page->tab_label;
2963 w = GTK_WIDGET (notebook);
2966 gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2968 gtk_widget_get_allocation (w, &allocation);
2969 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2970 &requisition, NULL);
2972 if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
2973 *x += allocation.x + allocation.width - requisition.width;
2977 *y += allocation.y + allocation.height;
2983 gtk_notebook_popup_menu (GtkWidget *widget)
2985 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2986 GtkNotebookPrivate *priv = notebook->priv;
2990 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2991 popup_position_func, notebook,
2992 0, gtk_get_current_event_time ());
2993 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
3001 stop_scrolling (GtkNotebook *notebook)
3003 GtkNotebookPrivate *priv = notebook->priv;
3007 g_source_remove (priv->timer);
3009 priv->need_timer = FALSE;
3011 priv->click_child = 0;
3013 gtk_notebook_redraw_arrows (notebook);
3017 get_drop_position (GtkNotebook *notebook)
3019 GtkNotebookPrivate *priv = notebook->priv;
3020 GList *children, *last_child;
3021 GtkNotebookPage *page;
3028 is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
3029 children = priv->children;
3034 page = children->data;
3036 if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
3037 gtk_widget_get_visible (page->child) &&
3039 gtk_widget_get_mapped (page->tab_label))
3041 switch (priv->tab_pos)
3044 case GTK_POS_BOTTOM:
3047 if (PAGE_MIDDLE_X (page) > x)
3052 if (PAGE_MIDDLE_X (page) < x)
3059 if (PAGE_MIDDLE_Y (page) > y)
3065 last_child = children->next;
3068 children = children->next;
3075 show_drag_window (GtkNotebook *notebook,
3076 GtkNotebookPrivate *priv,
3077 GtkNotebookPage *page,
3080 GtkWidget *widget = GTK_WIDGET (notebook);
3082 if (!priv->drag_window)
3084 GdkWindowAttr attributes;
3085 guint attributes_mask;
3087 attributes.x = page->allocation.x;
3088 attributes.y = page->allocation.y;
3089 attributes.width = page->allocation.width;
3090 attributes.height = page->allocation.height;
3091 attributes.window_type = GDK_WINDOW_CHILD;
3092 attributes.wclass = GDK_INPUT_OUTPUT;
3093 attributes.visual = gtk_widget_get_visual (widget);
3094 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3095 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3097 priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
3100 gdk_window_set_user_data (priv->drag_window, widget);
3103 g_object_ref (page->tab_label);
3104 gtk_widget_unparent (page->tab_label);
3105 gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
3106 gtk_widget_set_parent (page->tab_label, widget);
3107 g_object_unref (page->tab_label);
3109 gdk_window_show (priv->drag_window);
3111 /* the grab will dissapear when the window is hidden */
3112 gdk_device_grab (device, priv->drag_window,
3113 GDK_OWNERSHIP_WINDOW, FALSE,
3114 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3115 NULL, GDK_CURRENT_TIME);
3118 /* This function undoes the reparenting that happens both when drag_window
3119 * is shown for reordering and when the DnD icon is shown for detaching
3122 hide_drag_window (GtkNotebook *notebook,
3123 GtkNotebookPrivate *priv,
3124 GtkNotebookPage *page)
3126 GtkWidget *widget = GTK_WIDGET (notebook);
3127 GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
3129 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
3130 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
3132 g_object_ref (page->tab_label);
3134 if (GTK_IS_WINDOW (parent))
3136 /* parent widget is the drag window */
3137 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
3140 gtk_widget_unparent (page->tab_label);
3142 gtk_widget_set_parent (page->tab_label, widget);
3143 g_object_unref (page->tab_label);
3146 if (priv->drag_window &&
3147 gdk_window_is_visible (priv->drag_window))
3148 gdk_window_hide (priv->drag_window);
3152 gtk_notebook_stop_reorder (GtkNotebook *notebook)
3154 GtkNotebookPrivate *priv = notebook->priv;
3155 GtkNotebookPage *page;
3157 if (priv->operation == DRAG_OPERATION_DETACH)
3158 page = priv->detached_tab;
3160 page = priv->cur_page;
3162 if (!page || !page->tab_label)
3165 priv->pressed_button = -1;
3167 if (page->reorderable || page->detachable)
3169 if (priv->during_reorder)
3171 gint old_page_num, page_num;
3174 element = get_drop_position (notebook);
3175 old_page_num = g_list_position (priv->children, priv->focus_tab);
3176 page_num = reorder_tab (notebook, element, priv->focus_tab);
3177 gtk_notebook_child_reordered (notebook, page);
3179 if (priv->has_scrolled || old_page_num != page_num)
3180 g_signal_emit (notebook,
3181 notebook_signals[PAGE_REORDERED], 0,
3182 page->child, page_num);
3184 priv->has_scrolled = FALSE;
3185 priv->during_reorder = FALSE;
3188 hide_drag_window (notebook, priv, page);
3190 priv->operation = DRAG_OPERATION_NONE;
3191 gtk_notebook_pages_allocate (notebook);
3193 if (priv->dnd_timer)
3195 g_source_remove (priv->dnd_timer);
3196 priv->dnd_timer = 0;
3202 gtk_notebook_button_release (GtkWidget *widget,
3203 GdkEventButton *event)
3205 GtkNotebook *notebook;
3206 GtkNotebookPrivate *priv;
3207 GtkNotebookPage *page;
3209 if (event->type != GDK_BUTTON_RELEASE)
3212 notebook = GTK_NOTEBOOK (widget);
3213 priv = notebook->priv;
3215 page = priv->cur_page;
3217 if (!priv->during_detach &&
3218 page->reorderable &&
3219 event->button == priv->pressed_button)
3220 gtk_notebook_stop_reorder (notebook);
3222 if (event->button == priv->button)
3224 stop_scrolling (notebook);
3232 gtk_notebook_leave_notify (GtkWidget *widget,
3233 GdkEventCrossing *event)
3235 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3236 GtkNotebookPrivate *priv = notebook->priv;
3239 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
3245 gtk_notebook_redraw_arrows (notebook);
3251 static GtkNotebookPointerPosition
3252 get_pointer_position (GtkNotebook *notebook)
3254 GtkNotebookPrivate *priv = notebook->priv;
3255 GtkWidget *widget = GTK_WIDGET (notebook);
3256 gint wx, wy, width, height;
3259 if (!priv->scrollable)
3260 return POINTER_BETWEEN;
3262 gdk_window_get_position (priv->event_window, &wx, &wy);
3263 width = gdk_window_get_width (priv->event_window);
3264 height = gdk_window_get_height (priv->event_window);
3266 if (priv->tab_pos == GTK_POS_TOP ||
3267 priv->tab_pos == GTK_POS_BOTTOM)
3271 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3272 x = priv->mouse_x - wx;
3274 if (x > width - SCROLL_THRESHOLD)
3275 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
3276 else if (x < SCROLL_THRESHOLD)
3277 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
3279 return POINTER_BETWEEN;
3285 y = priv->mouse_y - wy;
3286 if (y > height - SCROLL_THRESHOLD)
3287 return POINTER_AFTER;
3288 else if (y < SCROLL_THRESHOLD)
3289 return POINTER_BEFORE;
3291 return POINTER_BETWEEN;
3296 scroll_notebook_timer (gpointer data)
3298 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3299 GtkNotebookPrivate *priv = notebook->priv;
3300 GtkNotebookPointerPosition pointer_position;
3301 GList *element, *first_tab;
3303 pointer_position = get_pointer_position (notebook);
3305 element = get_drop_position (notebook);
3306 reorder_tab (notebook, element, priv->focus_tab);
3307 first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3308 (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3312 priv->first_tab = first_tab;
3313 gtk_notebook_pages_allocate (notebook);
3315 gdk_window_move_resize (priv->drag_window,
3316 priv->drag_window_x,
3317 priv->drag_window_y,
3318 priv->cur_page->allocation.width,
3319 priv->cur_page->allocation.height);
3320 gdk_window_raise (priv->drag_window);
3327 check_threshold (GtkNotebook *notebook,
3331 GtkNotebookPrivate *priv = notebook->priv;
3333 GdkRectangle rectangle = { 0, }; /* shut up gcc */
3334 GtkSettings *settings;
3336 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3337 g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3339 /* we want a large threshold */
3340 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3342 gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3343 rectangle.width = gdk_window_get_width (priv->event_window);
3344 rectangle.height = gdk_window_get_height (priv->event_window);
3346 rectangle.x -= dnd_threshold;
3347 rectangle.width += 2 * dnd_threshold;
3348 rectangle.y -= dnd_threshold;
3349 rectangle.height += 2 * dnd_threshold;
3351 return (current_x < rectangle.x ||
3352 current_x > rectangle.x + rectangle.width ||
3353 current_y < rectangle.y ||
3354 current_y > rectangle.y + rectangle.height);
3358 gtk_notebook_motion_notify (GtkWidget *widget,
3359 GdkEventMotion *event)
3361 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3362 GtkNotebookPrivate *priv = notebook->priv;
3363 GtkNotebookPage *page;
3364 GtkNotebookArrow arrow;
3365 GtkNotebookPointerPosition pointer_position;
3366 GtkSettings *settings;
3370 page = priv->cur_page;
3375 if (!(event->state & GDK_BUTTON1_MASK) &&
3376 priv->pressed_button != -1)
3378 gtk_notebook_stop_reorder (notebook);
3379 stop_scrolling (notebook);
3382 if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3385 priv->timestamp = event->time;
3387 /* While animating the move, event->x is relative to the flying tab
3388 * (priv->drag_window has a pointer grab), but we need coordinates relative to
3389 * the notebook widget.
3391 gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3392 priv->mouse_x = event->x_root - x_win;
3393 priv->mouse_y = event->y_root - y_win;
3395 arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3396 if (arrow != priv->in_child)
3398 priv->in_child = arrow;
3399 gtk_notebook_redraw_arrows (notebook);
3402 if (priv->pressed_button == -1)
3405 if (page->detachable &&
3406 check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3408 priv->detached_tab = priv->cur_page;
3409 priv->during_detach = TRUE;
3411 gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3412 priv->pressed_button, (GdkEvent*) event);
3416 if (page->reorderable &&
3417 (priv->during_reorder ||
3418 gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3420 priv->during_reorder = TRUE;
3421 pointer_position = get_pointer_position (notebook);
3423 if (event->window == priv->drag_window &&
3424 pointer_position != POINTER_BETWEEN &&
3425 gtk_notebook_show_arrows (notebook))
3428 if (!priv->dnd_timer)
3430 priv->has_scrolled = TRUE;
3431 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3432 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3434 priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3435 scroll_notebook_timer,
3436 (gpointer) notebook);
3441 if (priv->dnd_timer)
3443 g_source_remove (priv->dnd_timer);
3444 priv->dnd_timer = 0;
3448 if (event->window == priv->drag_window ||
3449 priv->operation != DRAG_OPERATION_REORDER)
3451 /* the drag operation is beginning, create the window */
3452 if (priv->operation != DRAG_OPERATION_REORDER)
3454 priv->operation = DRAG_OPERATION_REORDER;
3455 show_drag_window (notebook, priv, page, event->device);
3458 gtk_notebook_pages_allocate (notebook);
3459 gdk_window_move_resize (priv->drag_window,
3460 priv->drag_window_x,
3461 priv->drag_window_y,
3462 page->allocation.width,
3463 page->allocation.height);
3471 gtk_notebook_grab_notify (GtkWidget *widget,
3472 gboolean was_grabbed)
3474 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3478 gtk_notebook_stop_reorder (notebook);
3479 stop_scrolling (notebook);
3484 gtk_notebook_state_flags_changed (GtkWidget *widget,
3485 GtkStateFlags previous_state)
3487 if (!gtk_widget_is_sensitive (widget))
3488 stop_scrolling (GTK_NOTEBOOK (widget));
3492 gtk_notebook_focus_in (GtkWidget *widget,
3493 GdkEventFocus *event)
3495 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3501 gtk_notebook_focus_out (GtkWidget *widget,
3502 GdkEventFocus *event)
3504 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3510 gtk_notebook_style_updated (GtkWidget *widget)
3512 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3513 GtkNotebookPrivate *priv = notebook->priv;
3515 gboolean has_before_previous;
3516 gboolean has_before_next;
3517 gboolean has_after_previous;
3518 gboolean has_after_next;
3520 gtk_widget_style_get (widget,
3521 "has-backward-stepper", &has_before_previous,
3522 "has-secondary-forward-stepper", &has_before_next,
3523 "has-secondary-backward-stepper", &has_after_previous,
3524 "has-forward-stepper", &has_after_next,
3527 priv->has_before_previous = has_before_previous;
3528 priv->has_before_next = has_before_next;
3529 priv->has_after_previous = has_after_previous;
3530 priv->has_after_next = has_after_next;
3532 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_updated (widget);
3536 on_drag_icon_draw (GtkWidget *widget,
3540 GtkWidget *notebook, *child;
3541 GtkRequisition requisition;
3542 GtkStyleContext *context;
3545 notebook = GTK_WIDGET (data);
3546 child = gtk_bin_get_child (GTK_BIN (widget));
3547 context = gtk_widget_get_style_context (widget);
3549 gtk_style_context_save (context);
3550 notebook_tab_prepare_style_context (GTK_NOTEBOOK (notebook), NULL, context, FALSE);
3552 gtk_widget_get_preferred_size (widget,
3553 &requisition, NULL);
3554 gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3556 gtk_render_extension (context, cr, 0, 0,
3557 requisition.width, requisition.height,
3561 gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3563 gtk_style_context_restore (context);
3569 gtk_notebook_drag_begin (GtkWidget *widget,
3570 GdkDragContext *context)
3572 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3573 GtkNotebookPrivate *priv = notebook->priv;
3574 GtkWidget *tab_label;
3576 if (priv->dnd_timer)
3578 g_source_remove (priv->dnd_timer);
3579 priv->dnd_timer = 0;
3582 priv->operation = DRAG_OPERATION_DETACH;
3583 gtk_notebook_pages_allocate (notebook);
3585 tab_label = priv->detached_tab->tab_label;
3587 hide_drag_window (notebook, priv, priv->cur_page);
3588 g_object_ref (tab_label);
3589 gtk_widget_unparent (tab_label);
3591 priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3592 gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3593 gtk_widget_get_screen (widget));
3594 gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3595 gtk_widget_set_size_request (priv->dnd_window,
3596 priv->detached_tab->allocation.width,
3597 priv->detached_tab->allocation.height);
3598 g_object_unref (tab_label);
3600 g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3601 G_CALLBACK (on_drag_icon_draw), notebook);
3603 gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3607 gtk_notebook_drag_end (GtkWidget *widget,
3608 GdkDragContext *context)
3610 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3611 GtkNotebookPrivate *priv = notebook->priv;
3613 gtk_notebook_stop_reorder (notebook);
3615 if (priv->detached_tab)
3616 gtk_notebook_switch_page (notebook, priv->detached_tab);
3618 _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3619 gtk_widget_destroy (priv->dnd_window);
3620 priv->dnd_window = NULL;
3622 priv->operation = DRAG_OPERATION_NONE;
3625 static GtkNotebook *
3626 gtk_notebook_create_window (GtkNotebook *notebook,
3635 gtk_notebook_drag_failed (GtkWidget *widget,
3636 GdkDragContext *context,
3637 GtkDragResult result)
3639 if (result == GTK_DRAG_RESULT_NO_TARGET)
3641 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3642 GtkNotebookPrivate *priv = notebook->priv;
3643 GtkNotebook *dest_notebook = NULL;
3646 gdk_device_get_position (gdk_drag_context_get_device (context),
3649 g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3650 priv->detached_tab->child, x, y, &dest_notebook);
3653 do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3662 gtk_notebook_switch_tab_timeout (gpointer data)
3664 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3665 GtkNotebookPrivate *priv = notebook->priv;
3669 priv->switch_tab_timer = 0;
3673 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
3675 /* FIXME: hack, we don't want the
3676 * focus to move fom the source widget
3678 priv->child_has_focus = FALSE;
3679 gtk_notebook_switch_focus_tab (notebook, tab);
3686 gtk_notebook_drag_motion (GtkWidget *widget,
3687 GdkDragContext *context,
3692 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3693 GtkNotebookPrivate *priv = notebook->priv;
3694 GtkAllocation allocation;
3695 GdkRectangle position;
3696 GtkSettings *settings;
3697 GtkNotebookArrow arrow;
3699 GdkAtom target, tab_target;
3701 gtk_widget_get_allocation (widget, &allocation);
3703 arrow = gtk_notebook_get_arrow (notebook,
3708 priv->click_child = arrow;
3709 gtk_notebook_set_scroll_timer (notebook);
3710 gdk_drag_status (context, 0, time);
3714 stop_scrolling (notebook);
3715 target = gtk_drag_dest_find_target (widget, context, NULL);
3716 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3718 if (target == tab_target)
3720 GQuark group, source_group;
3721 GtkNotebook *source;
3722 GtkWidget *source_child;
3724 source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3725 source_child = source->priv->cur_page->child;
3727 group = notebook->priv->group;
3728 source_group = source->priv->group;
3730 if (group != 0 && group == source_group &&
3731 !(widget == source_child ||
3732 gtk_widget_is_ancestor (widget, source_child)))
3734 gdk_drag_status (context, GDK_ACTION_MOVE, time);
3739 /* it's a tab, but doesn't share
3740 * ID with this notebook */
3741 gdk_drag_status (context, 0, time);
3748 if (gtk_notebook_get_event_window_position (notebook, &position) &&
3749 x >= position.x && x <= position.x + position.width &&
3750 y >= position.y && y <= position.y + position.height)
3755 if (!priv->switch_tab_timer)
3757 settings = gtk_widget_get_settings (widget);
3759 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3760 priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3761 gtk_notebook_switch_tab_timeout,
3767 if (priv->switch_tab_timer)
3769 g_source_remove (priv->switch_tab_timer);
3770 priv->switch_tab_timer = 0;
3774 return (target == tab_target) ? TRUE : FALSE;
3778 gtk_notebook_drag_leave (GtkWidget *widget,
3779 GdkDragContext *context,
3782 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3783 GtkNotebookPrivate *priv = notebook->priv;
3785 if (priv->switch_tab_timer)
3787 g_source_remove (priv->switch_tab_timer);
3788 priv->switch_tab_timer = 0;
3791 stop_scrolling (GTK_NOTEBOOK (widget));
3795 gtk_notebook_drag_drop (GtkWidget *widget,
3796 GdkDragContext *context,
3801 GdkAtom target, tab_target;
3803 target = gtk_drag_dest_find_target (widget, context, NULL);
3804 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3806 if (target == tab_target)
3808 gtk_drag_get_data (widget, context, target, time);
3816 do_detach_tab (GtkNotebook *from,
3822 GtkNotebookPrivate *to_priv = to->priv;
3823 GtkAllocation to_allocation;
3824 GtkWidget *tab_label, *menu_label;
3825 gboolean tab_expand, tab_fill, reorderable, detachable;
3829 menu_label = gtk_notebook_get_menu_label (from, child);
3832 g_object_ref (menu_label);
3834 tab_label = gtk_notebook_get_tab_label (from, child);
3837 g_object_ref (tab_label);
3839 g_object_ref (child);
3841 gtk_container_child_get (GTK_CONTAINER (from),
3843 "tab-expand", &tab_expand,
3844 "tab-fill", &tab_fill,
3845 "reorderable", &reorderable,
3846 "detachable", &detachable,
3849 gtk_container_remove (GTK_CONTAINER (from), child);
3851 gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3852 to_priv->mouse_x = x + to_allocation.x;
3853 to_priv->mouse_y = y + to_allocation.y;
3855 element = get_drop_position (to);
3856 page_num = g_list_position (to_priv->children, element);
3857 gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3859 gtk_container_child_set (GTK_CONTAINER (to), child,
3860 "tab-expand", tab_expand,
3861 "tab-fill", tab_fill,
3862 "reorderable", reorderable,
3863 "detachable", detachable,
3866 g_object_unref (child);
3869 g_object_unref (tab_label);
3872 g_object_unref (menu_label);
3874 gtk_notebook_set_current_page (to, page_num);
3878 gtk_notebook_drag_data_get (GtkWidget *widget,
3879 GdkDragContext *context,
3880 GtkSelectionData *data,
3886 target = gtk_selection_data_get_target (data);
3887 if (target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3889 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3890 GtkNotebookPrivate *priv = notebook->priv;
3892 gtk_selection_data_set (data,
3895 (void*) &priv->detached_tab->child,
3901 gtk_notebook_drag_data_received (GtkWidget *widget,
3902 GdkDragContext *context,
3905 GtkSelectionData *data,
3909 GtkNotebook *notebook;
3910 GtkWidget *source_widget;
3913 notebook = GTK_NOTEBOOK (widget);
3914 source_widget = gtk_drag_get_source_widget (context);
3916 if (source_widget &&
3917 gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3919 child = (void*) gtk_selection_data_get_data (data);
3921 do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3922 gtk_drag_finish (context, TRUE, FALSE, time);
3925 gtk_drag_finish (context, FALSE, FALSE, time);
3928 /* Private GtkContainer Methods :
3930 * gtk_notebook_set_child_arg
3931 * gtk_notebook_get_child_arg
3933 * gtk_notebook_remove
3934 * gtk_notebook_focus
3935 * gtk_notebook_set_focus_child
3936 * gtk_notebook_child_type
3937 * gtk_notebook_forall
3940 gtk_notebook_set_child_property (GtkContainer *container,
3943 const GValue *value,
3949 /* not finding child's page is valid for menus or labels */
3950 if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3953 switch (property_id)
3955 case CHILD_PROP_TAB_LABEL:
3956 /* a NULL pointer indicates a default_tab setting, otherwise
3957 * we need to set the associated label
3959 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
3960 g_value_get_string (value));
3962 case CHILD_PROP_MENU_LABEL:
3963 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
3964 g_value_get_string (value));
3966 case CHILD_PROP_POSITION:
3967 gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
3968 g_value_get_int (value));
3970 case CHILD_PROP_TAB_EXPAND:
3971 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3973 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3974 g_value_get_boolean (value),
3977 case CHILD_PROP_TAB_FILL:
3978 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3980 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3982 g_value_get_boolean (value));
3984 case CHILD_PROP_REORDERABLE:
3985 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
3986 g_value_get_boolean (value));
3988 case CHILD_PROP_DETACHABLE:
3989 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
3990 g_value_get_boolean (value));
3993 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3999 gtk_notebook_get_child_property (GtkContainer *container,
4005 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4006 GtkNotebookPrivate *priv = notebook->priv;
4012 /* not finding child's page is valid for menus or labels */
4013 list = gtk_notebook_find_child (notebook, child, NULL);
4016 /* nothing to set on labels or menus */
4017 g_param_value_set_default (pspec, value);
4021 switch (property_id)
4023 case CHILD_PROP_TAB_LABEL:
4024 label = gtk_notebook_get_tab_label (notebook, child);
4026 if (GTK_IS_LABEL (label))
4027 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4029 g_value_set_string (value, NULL);
4031 case CHILD_PROP_MENU_LABEL:
4032 label = gtk_notebook_get_menu_label (notebook, child);
4034 if (GTK_IS_LABEL (label))
4035 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4037 g_value_set_string (value, NULL);
4039 case CHILD_PROP_POSITION:
4040 g_value_set_int (value, g_list_position (priv->children, list));
4042 case CHILD_PROP_TAB_EXPAND:
4043 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4045 g_value_set_boolean (value, expand);
4047 case CHILD_PROP_TAB_FILL:
4048 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4050 g_value_set_boolean (value, fill);
4052 case CHILD_PROP_REORDERABLE:
4053 g_value_set_boolean (value,
4054 gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
4056 case CHILD_PROP_DETACHABLE:
4057 g_value_set_boolean (value,
4058 gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
4061 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
4067 gtk_notebook_add (GtkContainer *container,
4070 gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget,
4075 gtk_notebook_remove (GtkContainer *container,
4078 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4079 GtkNotebookPrivate *priv = notebook->priv;
4080 GtkNotebookPage *page;
4084 children = priv->children;
4087 page = children->data;
4089 if (page->child == widget)
4093 children = children->next;
4096 if (children == NULL)
4099 g_object_ref (widget);
4101 gtk_notebook_real_remove (notebook, children);
4103 g_signal_emit (notebook,
4104 notebook_signals[PAGE_REMOVED],
4109 g_object_unref (widget);
4113 focus_tabs_in (GtkNotebook *notebook)
4115 GtkNotebookPrivate *priv = notebook->priv;
4117 if (priv->show_tabs && priv->cur_page)
4119 gtk_widget_grab_focus (GTK_WIDGET (notebook));
4121 gtk_notebook_switch_focus_tab (notebook,
4122 g_list_find (priv->children,
4132 focus_tabs_move (GtkNotebook *notebook,
4133 GtkDirectionType direction,
4134 gint search_direction)
4136 GtkNotebookPrivate *priv = notebook->priv;
4139 new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
4140 search_direction, TRUE);
4143 gboolean wrap_around;
4145 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
4146 "gtk-keynav-wrap-around", &wrap_around,
4150 new_page = gtk_notebook_search_page (notebook, NULL,
4151 search_direction, TRUE);
4155 gtk_notebook_switch_focus_tab (notebook, new_page);
4157 gtk_widget_error_bell (GTK_WIDGET (notebook));
4163 focus_child_in (GtkNotebook *notebook,
4164 GtkDirectionType direction)
4166 GtkNotebookPrivate *priv = notebook->priv;
4169 return gtk_widget_child_focus (priv->cur_page->child, direction);
4175 focus_action_in (GtkNotebook *notebook,
4177 GtkDirectionType direction)
4179 GtkNotebookPrivate *priv = notebook->priv;
4181 if (priv->action_widget[action] &&
4182 gtk_widget_get_visible (priv->action_widget[action]))
4183 return gtk_widget_child_focus (priv->action_widget[action], direction);
4188 /* Focus in the notebook can either be on the pages, or on
4189 * the tabs or on the action_widgets.
4192 gtk_notebook_focus (GtkWidget *widget,
4193 GtkDirectionType direction)
4195 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
4196 GtkNotebookPrivate *priv = notebook->priv;
4197 GtkWidget *old_focus_child;
4198 GtkDirectionType effective_direction;
4202 gboolean widget_is_focus;
4203 GtkContainer *container;
4205 container = GTK_CONTAINER (widget);
4207 if (priv->tab_pos == GTK_POS_TOP ||
4208 priv->tab_pos == GTK_POS_LEFT)
4210 first_action = ACTION_WIDGET_START;
4211 last_action = ACTION_WIDGET_END;
4215 first_action = ACTION_WIDGET_END;
4216 last_action = ACTION_WIDGET_START;
4219 if (priv->focus_out)
4221 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
4225 widget_is_focus = gtk_widget_is_focus (widget);
4226 old_focus_child = gtk_container_get_focus_child (container);
4228 effective_direction = get_effective_direction (notebook, direction);
4230 if (old_focus_child) /* Focus on page child or action widget */
4232 if (gtk_widget_child_focus (old_focus_child, direction))
4235 if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
4237 switch (effective_direction)
4240 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4242 return focus_tabs_in (notebook);
4250 case GTK_DIR_TAB_FORWARD:
4251 if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
4252 focus_child_in (notebook, direction))
4254 return focus_tabs_in (notebook);
4255 case GTK_DIR_TAB_BACKWARD:
4258 g_assert_not_reached ();
4262 else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
4264 switch (effective_direction)
4267 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4271 return focus_tabs_in (notebook);
4277 case GTK_DIR_TAB_FORWARD:
4279 case GTK_DIR_TAB_BACKWARD:
4280 if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
4281 focus_child_in (notebook, direction))
4283 return focus_tabs_in (notebook);
4285 g_assert_not_reached ();
4291 switch (effective_direction)
4293 case GTK_DIR_TAB_BACKWARD:
4295 /* Focus onto the tabs */
4296 return focus_tabs_in (notebook);
4301 case GTK_DIR_TAB_FORWARD:
4302 return focus_action_in (notebook, last_action, direction);
4306 else if (widget_is_focus) /* Focus was on tabs */
4308 switch (effective_direction)
4310 case GTK_DIR_TAB_BACKWARD:
4311 return focus_action_in (notebook, first_action, direction);
4314 case GTK_DIR_TAB_FORWARD:
4315 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4317 return focus_action_in (notebook, last_action, direction);
4319 /* We use TAB_FORWARD rather than direction so that we focus a more
4320 * predictable widget for the user; users may be using arrow focusing
4321 * in this situation even if they don't usually use arrow focusing.
4323 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4325 return focus_tabs_move (notebook, direction, STEP_PREV);
4327 return focus_tabs_move (notebook, direction, STEP_NEXT);
4330 else /* Focus was not on widget */
4332 switch (effective_direction)
4334 case GTK_DIR_TAB_FORWARD:
4336 if (focus_action_in (notebook, first_action, direction))
4338 if (focus_tabs_in (notebook))
4340 if (focus_action_in (notebook, last_action, direction))
4342 if (focus_child_in (notebook, direction))
4345 case GTK_DIR_TAB_BACKWARD:
4346 if (focus_action_in (notebook, last_action, direction))
4348 if (focus_child_in (notebook, direction))
4350 if (focus_tabs_in (notebook))
4352 if (focus_action_in (notebook, first_action, direction))
4357 return focus_child_in (notebook, direction);
4361 g_assert_not_reached ();
4366 gtk_notebook_set_focus_child (GtkContainer *container,
4369 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4370 GtkNotebookPrivate *priv = notebook->priv;
4371 GtkWidget *page_child;
4372 GtkWidget *toplevel;
4374 /* If the old focus widget was within a page of the notebook,
4375 * (child may either be NULL or not in this case), record it
4376 * for future use if we switch to the page with a mnemonic.
4379 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4380 if (toplevel && gtk_widget_is_toplevel (toplevel))
4382 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4385 if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4387 GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4390 GtkNotebookPage *page = list->data;
4392 if (page->last_focus_child)
4393 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4395 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4396 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4402 page_child = gtk_widget_get_parent (page_child);
4408 g_return_if_fail (GTK_IS_WIDGET (child));
4410 priv->child_has_focus = TRUE;
4411 if (!priv->focus_tab)
4414 GtkNotebookPage *page;
4416 children = priv->children;
4419 page = children->data;
4420 if (page->child == child || page->tab_label == child)
4421 gtk_notebook_switch_focus_tab (notebook, children);
4422 children = children->next;
4427 priv->child_has_focus = FALSE;
4429 GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4433 gtk_notebook_forall (GtkContainer *container,
4434 gboolean include_internals,
4435 GtkCallback callback,
4436 gpointer callback_data)
4438 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4439 GtkNotebookPrivate *priv = notebook->priv;
4443 children = priv->children;
4446 GtkNotebookPage *page;
4448 page = children->data;
4449 children = children->next;
4450 (* callback) (page->child, callback_data);
4452 if (include_internals)
4454 if (page->tab_label)
4455 (* callback) (page->tab_label, callback_data);
4459 if (include_internals) {
4460 for (i = 0; i < N_ACTION_WIDGETS; i++)
4462 if (priv->action_widget[i])
4463 (* callback) (priv->action_widget[i], callback_data);
4468 static GtkWidgetPath *
4469 gtk_notebook_get_path_for_child (GtkContainer *container,
4472 GtkNotebookPrivate *priv;
4473 GtkNotebook *notebook;
4474 GtkNotebookPage *page;
4475 GtkWidgetPath *path;
4478 path = GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->get_path_for_child (container, widget);
4480 notebook = GTK_NOTEBOOK (container);
4481 priv = notebook->priv;
4483 for (c = priv->children; c; c = c->next)
4487 if (page->tab_label == widget)
4491 /* Widget is not a tab label */
4495 gtk_widget_path_iter_add_region (path,
4496 gtk_widget_path_length (path) - 2,
4497 GTK_STYLE_REGION_TAB,
4498 _gtk_notebook_get_tab_flags (notebook, page));
4504 gtk_notebook_child_type (GtkContainer *container)
4506 return GTK_TYPE_WIDGET;
4509 /* Private GtkNotebook Methods:
4511 * gtk_notebook_real_insert_page
4514 page_visible_cb (GtkWidget *page,
4518 GtkNotebook *notebook = GTK_NOTEBOOK (data);
4519 GtkNotebookPrivate *priv = notebook->priv;
4523 if (priv->cur_page &&
4524 priv->cur_page->child == page &&
4525 !gtk_widget_get_visible (page))
4527 list = g_list_find (priv->children, priv->cur_page);
4530 next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4532 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4536 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4541 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4543 GtkWidget *tab_label,
4544 GtkWidget *menu_label,
4547 GtkNotebookPrivate *priv = notebook->priv;
4548 GtkNotebookPage *page;
4551 gtk_widget_freeze_child_notify (child);
4553 page = g_slice_new0 (GtkNotebookPage);
4554 page->child = child;
4556 nchildren = g_list_length (priv->children);
4557 if ((position < 0) || (position > nchildren))
4558 position = nchildren;
4560 priv->children = g_list_insert (priv->children, page, position);
4564 page->default_tab = TRUE;
4566 page->tab_label = tab_label;
4567 page->menu_label = menu_label;
4568 page->expand = FALSE;
4572 page->default_menu = TRUE;
4574 g_object_ref_sink (page->menu_label);
4577 gtk_notebook_menu_item_create (notebook,
4578 g_list_find (priv->children, page));
4580 /* child visible will be turned on by switch_page below */
4581 if (priv->cur_page != page)
4582 gtk_widget_set_child_visible (child, FALSE);
4584 gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4586 gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4588 gtk_notebook_update_labels (notebook);
4590 if (!priv->first_tab)
4591 priv->first_tab = priv->children;
4595 if (priv->show_tabs && gtk_widget_get_visible (child))
4596 gtk_widget_show (tab_label);
4598 gtk_widget_hide (tab_label);
4600 page->mnemonic_activate_signal =
4601 g_signal_connect (tab_label,
4602 "mnemonic-activate",
4603 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4607 page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4608 G_CALLBACK (page_visible_cb), notebook);
4610 g_signal_emit (notebook,
4611 notebook_signals[PAGE_ADDED],
4616 if (!priv->cur_page)
4618 gtk_notebook_switch_page (notebook, page);
4619 /* focus_tab is set in the switch_page method */
4620 gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4623 gtk_notebook_update_tab_states (notebook);
4625 if (priv->scrollable)
4626 gtk_notebook_redraw_arrows (notebook);
4628 gtk_widget_child_notify (child, "tab-expand");
4629 gtk_widget_child_notify (child, "tab-fill");
4630 gtk_widget_child_notify (child, "tab-label");
4631 gtk_widget_child_notify (child, "menu-label");
4632 gtk_widget_child_notify (child, "position");
4633 gtk_widget_thaw_child_notify (child);
4635 /* The page-added handler might have reordered the pages, re-get the position */
4636 return gtk_notebook_page_num (notebook, child);
4639 /* Private GtkNotebook Functions:
4641 * gtk_notebook_redraw_tabs
4642 * gtk_notebook_real_remove
4643 * gtk_notebook_update_labels
4644 * gtk_notebook_timer
4645 * gtk_notebook_set_scroll_timer
4646 * gtk_notebook_page_compare
4647 * gtk_notebook_search_page
4650 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4652 GtkNotebookPrivate *priv = notebook->priv;
4653 GtkAllocation allocation;
4655 GtkNotebookPage *page;
4656 GtkStyleContext *context;
4657 GdkRectangle redraw_rect;
4659 gint tab_pos = get_effective_tab_pos (notebook);
4662 widget = GTK_WIDGET (notebook);
4663 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4665 if (!gtk_widget_get_mapped (widget) || !priv->first_tab)
4668 page = priv->first_tab->data;
4670 redraw_rect.x = border;
4671 redraw_rect.y = border;
4673 gtk_widget_get_allocation (widget, &allocation);
4675 context = gtk_widget_get_style_context (widget);
4676 gtk_style_context_get_padding (context, 0, &padding);
4680 case GTK_POS_BOTTOM:
4681 redraw_rect.y = allocation.height - border -
4682 page->allocation.height - padding.bottom;
4684 if (page != priv->cur_page)
4685 redraw_rect.y -= padding.bottom;
4688 redraw_rect.width = allocation.width - 2 * border;
4689 redraw_rect.height = page->allocation.height + padding.top;
4691 if (page != priv->cur_page)
4692 redraw_rect.height += padding.top;
4695 redraw_rect.x = allocation.width - border -
4696 page->allocation.width - padding.right;
4698 if (page != priv->cur_page)
4699 redraw_rect.x -= padding.right;
4702 redraw_rect.width = page->allocation.width + padding.left;
4703 redraw_rect.height = allocation.height - 2 * border;
4705 if (page != priv->cur_page)
4706 redraw_rect.width += padding.left;
4710 redraw_rect.x += allocation.x;
4711 redraw_rect.y += allocation.y;
4713 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4714 &redraw_rect, TRUE);
4718 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4720 GtkNotebookPrivate *priv = notebook->priv;
4722 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4723 gtk_notebook_show_arrows (notebook))
4727 GtkNotebookArrow arrow[4];
4729 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4730 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4731 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4732 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4734 for (i = 0; i < 4; i++)
4736 if (arrow[i] == ARROW_NONE)
4739 gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4740 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4747 gtk_notebook_timer (GtkNotebook *notebook)
4749 GtkNotebookPrivate *priv = notebook->priv;
4750 gboolean retval = FALSE;
4754 gtk_notebook_do_arrow (notebook, priv->click_child);
4756 if (priv->need_timer)
4758 GtkSettings *settings;
4761 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4762 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4764 priv->need_timer = FALSE;
4765 priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4766 (GSourceFunc) gtk_notebook_timer,
4767 (gpointer) notebook);
4777 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4779 GtkNotebookPrivate *priv = notebook->priv;
4780 GtkWidget *widget = GTK_WIDGET (notebook);
4784 GtkSettings *settings = gtk_widget_get_settings (widget);
4787 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4789 priv->timer = gdk_threads_add_timeout (timeout,
4790 (GSourceFunc) gtk_notebook_timer,
4791 (gpointer) notebook);
4792 priv->need_timer = TRUE;
4797 gtk_notebook_page_compare (gconstpointer a,
4800 return (((GtkNotebookPage *) a)->child != b);
4804 gtk_notebook_find_child (GtkNotebook *notebook,
4806 const gchar *function)
4808 GtkNotebookPrivate *priv = notebook->priv;
4809 GList *list = g_list_find_custom (priv->children, child,
4810 gtk_notebook_page_compare);
4812 #ifndef G_DISABLE_CHECKS
4813 if (!list && function)
4814 g_warning ("%s: unable to find child %p in notebook %p",
4815 function, child, notebook);
4822 gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4823 GtkNotebookPage *page)
4825 if (page->tab_label)
4827 if (page->mnemonic_activate_signal)
4828 g_signal_handler_disconnect (page->tab_label,
4829 page->mnemonic_activate_signal);
4830 page->mnemonic_activate_signal = 0;
4832 gtk_widget_set_state_flags (page->tab_label, 0, TRUE);
4833 gtk_widget_unparent (page->tab_label);
4834 page->tab_label = NULL;
4839 gtk_notebook_real_remove (GtkNotebook *notebook,
4842 GtkNotebookPrivate *priv = notebook->priv;
4843 GtkNotebookPage *page;
4845 gint need_resize = FALSE;
4846 GtkWidget *tab_label;
4847 gboolean destroying;
4849 destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4851 next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4853 next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4855 priv->children = g_list_remove_link (priv->children, list);
4857 if (priv->cur_page == list->data)
4859 priv->cur_page = NULL;
4860 if (next_list && !destroying)
4861 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4864 if (priv->detached_tab == list->data)
4865 priv->detached_tab = NULL;
4867 if (list == priv->first_tab)
4868 priv->first_tab = next_list;
4869 if (list == priv->focus_tab && !destroying)
4870 gtk_notebook_switch_focus_tab (notebook, next_list);
4874 g_signal_handler_disconnect (page->child, page->notify_visible_handler);
4876 if (gtk_widget_get_visible (page->child) &&
4877 gtk_widget_get_visible (GTK_WIDGET (notebook)))
4880 gtk_widget_unparent (page->child);
4882 tab_label = page->tab_label;
4885 g_object_ref (tab_label);
4886 gtk_notebook_remove_tab_label (notebook, page);
4888 gtk_widget_destroy (tab_label);
4889 g_object_unref (tab_label);
4894 GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
4896 gtk_notebook_menu_label_unparent (parent, NULL);
4897 gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
4899 gtk_widget_queue_resize (priv->menu);
4901 if (!page->default_menu)
4902 g_object_unref (page->menu_label);
4906 if (page->last_focus_child)
4908 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4909 page->last_focus_child = NULL;
4912 g_slice_free (GtkNotebookPage, page);
4914 gtk_notebook_update_labels (notebook);
4916 gtk_widget_queue_resize (GTK_WIDGET (notebook));
4920 gtk_notebook_update_labels (GtkNotebook *notebook)
4922 GtkNotebookPrivate *priv = notebook->priv;
4923 GtkNotebookPage *page;
4928 if (!priv->show_tabs && !priv->menu)
4931 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
4933 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
4936 g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
4937 if (priv->show_tabs)
4939 if (page->default_tab)
4941 if (!page->tab_label)
4943 page->tab_label = gtk_label_new (string);
4944 gtk_widget_set_parent (page->tab_label,
4945 GTK_WIDGET (notebook));
4948 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
4951 if (gtk_widget_get_visible (page->child) &&
4952 !gtk_widget_get_visible (page->tab_label))
4953 gtk_widget_show (page->tab_label);
4954 else if (!gtk_widget_get_visible (page->child) &&
4955 gtk_widget_get_visible (page->tab_label))
4956 gtk_widget_hide (page->tab_label);
4958 if (priv->menu && page->default_menu)
4960 if (GTK_IS_LABEL (page->tab_label))
4961 gtk_label_set_text (GTK_LABEL (page->menu_label),
4962 gtk_label_get_label (GTK_LABEL (page->tab_label)));
4964 gtk_label_set_text (GTK_LABEL (page->menu_label), string);
4970 gtk_notebook_search_page (GtkNotebook *notebook,
4973 gboolean find_visible)
4975 GtkNotebookPrivate *priv = notebook->priv;
4976 GtkNotebookPage *page = NULL;
4977 GList *old_list = NULL;
4982 if (!page || direction == STEP_NEXT)
4990 list = priv->children;
4995 if (direction == STEP_NEXT &&
4997 (gtk_widget_get_visible (page->child) &&
4998 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
5013 if (direction == STEP_PREV &&
5015 (gtk_widget_get_visible (page->child) &&
5016 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
5024 /* Private GtkNotebook Drawing Functions:
5026 * gtk_notebook_paint
5027 * gtk_notebook_draw_tab
5028 * gtk_notebook_draw_arrow
5031 gtk_notebook_paint (GtkWidget *widget,
5034 GtkNotebook *notebook;
5035 GtkNotebookPrivate *priv;
5036 GtkNotebookPage *page;
5037 GtkAllocation allocation;
5042 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5043 gint gap_x = 0, gap_width = 0, step = STEP_PREV;
5046 GtkStyleContext *context;
5048 notebook = GTK_NOTEBOOK (widget);
5049 priv = notebook->priv;
5050 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5051 tab_pos = get_effective_tab_pos (notebook);
5052 context = gtk_widget_get_style_context (widget);
5055 if ((!priv->show_tabs && !priv->show_border) ||
5056 !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
5059 gtk_widget_get_allocation (widget, &allocation);
5061 x = allocation.x + border_width;
5062 y = allocation.y + border_width;
5063 width = allocation.width - border_width * 2;
5064 height = allocation.height - border_width * 2;
5066 if (priv->show_border && (!priv->show_tabs || !priv->children))
5068 gtk_render_background (context, cr,
5069 x, y, width, height);
5070 gtk_render_frame (context, cr,
5071 x, y, width, height);
5075 if (!priv->first_tab)
5076 priv->first_tab = priv->children;
5078 if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
5079 page = GTK_NOTEBOOK_PAGE (priv->first_tab);
5081 page = priv->cur_page;
5086 y += page->allocation.height;
5088 case GTK_POS_BOTTOM:
5089 height -= page->allocation.height;
5092 x += page->allocation.width;
5095 width -= page->allocation.width;
5099 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
5100 !gtk_widget_get_mapped (priv->cur_page->tab_label))
5110 case GTK_POS_BOTTOM:
5111 if (priv->operation == DRAG_OPERATION_REORDER)
5112 gap_x = priv->drag_window_x - allocation.x - border_width;
5114 gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
5116 gap_width = priv->cur_page->allocation.width;
5117 step = is_rtl ? STEP_PREV : STEP_NEXT;
5121 if (priv->operation == DRAG_OPERATION_REORDER)
5122 gap_x = priv->drag_window_y - border_width - allocation.y;
5124 gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
5126 gap_width = priv->cur_page->allocation.height;
5132 for (children = priv->children; children; children = children->next)
5134 page = children->data;
5136 if (!gtk_widget_get_visible (page->child))
5139 if (!gtk_widget_get_mapped (page->tab_label))
5142 /* No point in keeping searching */
5147 gtk_style_context_save (context);
5149 if (!showarrow || !priv->scrollable)
5151 GtkJunctionSides junction = 0;
5153 /* Apply junction sides, if no arrows are shown,
5154 * then make corners with connecting tabs square.
5159 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_TOPRIGHT : GTK_JUNCTION_CORNER_TOPLEFT;
5162 case GTK_POS_BOTTOM:
5163 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_BOTTOMRIGHT : GTK_JUNCTION_CORNER_BOTTOMLEFT;
5167 junction |= GTK_JUNCTION_CORNER_TOPLEFT;
5171 junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
5176 gtk_style_context_set_junction_sides (context, junction);
5179 gtk_render_background (context, cr,
5180 x, y, width, height);
5181 gtk_render_frame_gap (context, cr,
5182 x, y, width, height,
5183 tab_pos, gap_x, gap_x + gap_width);
5185 gtk_style_context_restore (context);
5187 children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
5191 page = children->data;
5193 if (page == priv->cur_page)
5196 children = gtk_notebook_search_page (notebook, children,
5199 if (!gtk_widget_get_visible (page->child) ||
5200 !gtk_widget_get_mapped (page->tab_label))
5203 gtk_notebook_draw_tab (notebook, page, cr, TRUE);
5206 if (children != NULL)
5208 GList *other_order = NULL;
5212 page = children->data;
5213 children = gtk_notebook_search_page (notebook, children,
5215 if (!gtk_widget_get_visible (page->child) ||
5216 !gtk_widget_get_mapped (page->tab_label))
5219 if (children != NULL)
5220 other_order = g_list_prepend (other_order, children->data);
5223 /* draw them with the opposite order */
5224 for (children = other_order; children; children = children->next)
5226 page = children->data;
5227 gtk_notebook_draw_tab (notebook, page, cr, TRUE);
5230 g_list_free (other_order);
5233 if (showarrow && priv->scrollable)
5235 if (priv->has_before_previous)
5236 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
5237 if (priv->has_before_next)
5238 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
5239 if (priv->has_after_previous)
5240 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
5241 if (priv->has_after_next)
5242 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
5245 if (priv->operation != DRAG_OPERATION_REORDER)
5246 gtk_notebook_draw_tab (notebook, priv->cur_page, cr, TRUE);
5250 gtk_notebook_draw_tab (GtkNotebook *notebook,
5251 GtkNotebookPage *page,
5255 GtkNotebookPrivate *priv;
5257 GtkStyleContext *context;
5259 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5260 !gtk_widget_get_mapped (page->tab_label) ||
5261 (page->allocation.width == 0) || (page->allocation.height == 0))
5264 widget = GTK_WIDGET (notebook);
5265 priv = notebook->priv;
5267 context = gtk_widget_get_style_context (widget);
5268 gtk_style_context_save (context);
5269 notebook_tab_prepare_style_context (notebook, page, context, use_flags);
5271 gtk_render_extension (context, cr,
5274 page->allocation.width,
5275 page->allocation.height,
5276 get_tab_gap_pos (notebook));
5278 if (gtk_widget_has_visible_focus (widget) &&
5279 priv->cur_page == page)
5281 gint focus_width, focus_pad;
5282 GtkAllocation allocation;
5284 gtk_widget_get_allocation (page->tab_label, &allocation);
5285 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
5286 gtk_widget_style_get (widget, "focus-padding", &focus_pad, NULL);
5288 gtk_render_focus (context, cr,
5289 allocation.x - focus_width - focus_pad,
5290 allocation.y - focus_width - focus_pad,
5291 allocation.width + 2 * (focus_width + focus_pad),
5292 allocation.height + 2 * (focus_width + focus_pad));
5295 gtk_style_context_restore (context);
5299 gtk_notebook_draw_arrow (GtkNotebook *notebook,
5301 GtkNotebookArrow nbarrow)
5303 GtkNotebookPrivate *priv = notebook->priv;
5304 GtkStyleContext *context;
5305 GtkStateFlags state = 0;
5307 GdkRectangle arrow_rect;
5308 gboolean is_rtl, left;
5309 gint scroll_arrow_hlength;
5310 gint scroll_arrow_vlength;
5314 widget = GTK_WIDGET (notebook);
5315 context = gtk_widget_get_style_context (widget);
5317 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
5319 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5320 left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
5321 (!ARROW_IS_LEFT (nbarrow) && is_rtl);
5323 gtk_widget_style_get (widget,
5324 "scroll-arrow-hlength", &scroll_arrow_hlength,
5325 "scroll-arrow-vlength", &scroll_arrow_vlength,
5328 if (priv->in_child == nbarrow)
5330 state |= GTK_STATE_FLAG_PRELIGHT;
5332 if (priv->click_child == nbarrow)
5333 state |= GTK_STATE_FLAG_ACTIVE;
5336 state = gtk_widget_get_state_flags (widget);
5338 if (priv->focus_tab &&
5339 !gtk_notebook_search_page (notebook, priv->focus_tab,
5340 left ? STEP_PREV : STEP_NEXT, TRUE))
5341 state = GTK_STATE_FLAG_INSENSITIVE;
5343 if (priv->tab_pos == GTK_POS_LEFT ||
5344 priv->tab_pos == GTK_POS_RIGHT)
5346 angle = (ARROW_IS_LEFT (nbarrow)) ? 0 : G_PI;
5347 arrow_size = scroll_arrow_vlength;
5351 angle = (ARROW_IS_LEFT (nbarrow)) ? 3 * (G_PI / 2) : G_PI / 2;
5352 arrow_size = scroll_arrow_hlength;
5355 gtk_style_context_save (context);
5356 gtk_style_context_set_state (context, state);
5358 gtk_render_arrow (context, cr, angle,
5359 arrow_rect.x, arrow_rect.y,
5362 gtk_style_context_restore (context);
5365 /* Private GtkNotebook Size Allocate Functions:
5367 * gtk_notebook_tab_space
5368 * gtk_notebook_calculate_shown_tabs
5369 * gtk_notebook_calculate_tabs_allocation
5370 * gtk_notebook_pages_allocate
5371 * gtk_notebook_page_allocate
5372 * gtk_notebook_calc_tabs
5375 gtk_notebook_tab_space (GtkNotebook *notebook,
5376 gboolean *show_arrows,
5381 GtkNotebookPrivate *priv = notebook->priv;
5382 GtkAllocation allocation, action_allocation;
5384 GtkStyleContext *context;
5386 gint tab_pos = get_effective_tab_pos (notebook);
5389 gint scroll_arrow_hlength;
5390 gint scroll_arrow_vlength;
5397 widget = GTK_WIDGET (notebook);
5398 children = priv->children;
5399 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5401 context = gtk_widget_get_style_context (widget);
5403 gtk_widget_style_get (GTK_WIDGET (notebook),
5404 "arrow-spacing", &arrow_spacing,
5405 "scroll-arrow-hlength", &scroll_arrow_hlength,
5406 "scroll-arrow-vlength", &scroll_arrow_vlength,
5407 "initial-gap", &initial_gap,
5410 border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5411 gtk_style_context_get_padding (context, 0, &padding);
5413 gtk_widget_get_allocation (widget, &allocation);
5415 allocation.x += initial_gap;
5416 allocation.width -= 2 * initial_gap;
5421 case GTK_POS_BOTTOM:
5422 *min = allocation.x + border_width;
5423 *max = allocation.x + allocation.width - border_width;
5425 for (i = 0; i < N_ACTION_WIDGETS; i++)
5427 if (priv->action_widget[i])
5429 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5431 if ((i == ACTION_WIDGET_START && !is_rtl) ||
5432 (i == ACTION_WIDGET_END && is_rtl))
5433 *min += action_allocation.width + padding.left;
5435 *max -= action_allocation.width + padding.right;
5441 GtkNotebookPage *page;
5443 page = children->data;
5444 children = children->next;
5446 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5447 gtk_widget_get_visible (page->child))
5448 *tab_space += page->requisition.width;
5453 *min = allocation.y + border_width;
5454 *max = allocation.y + allocation.height - border_width;
5456 for (i = 0; i < N_ACTION_WIDGETS; i++)
5458 if (priv->action_widget[i])
5460 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5462 if (i == ACTION_WIDGET_START)
5463 *min += action_allocation.height + padding.top;
5465 *max -= action_allocation.height + padding.bottom;
5471 GtkNotebookPage *page;
5473 page = children->data;
5474 children = children->next;
5476 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5477 gtk_widget_get_visible (page->child))
5478 *tab_space += page->requisition.height;
5483 if (!priv->scrollable)
5484 *show_arrows = FALSE;
5487 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5492 case GTK_POS_BOTTOM:
5493 if (*tab_space > *max - *min - tab_overlap)
5495 *show_arrows = TRUE;
5497 /* take arrows into account */
5498 *tab_space = *max - *min - tab_overlap;
5500 if (priv->has_after_previous)
5502 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5503 *max -= arrow_spacing + scroll_arrow_hlength;
5506 if (priv->has_after_next)
5508 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5509 *max -= arrow_spacing + scroll_arrow_hlength;
5512 if (priv->has_before_previous)
5514 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5515 *min += arrow_spacing + scroll_arrow_hlength;
5518 if (priv->has_before_next)
5520 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5521 *min += arrow_spacing + scroll_arrow_hlength;
5527 if (*tab_space > *max - *min - tab_overlap)
5529 *show_arrows = TRUE;
5531 /* take arrows into account */
5532 *tab_space = *max - *min - tab_overlap;
5534 if (priv->has_after_previous || priv->has_after_next)
5536 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5537 *max -= arrow_spacing + scroll_arrow_vlength;
5540 if (priv->has_before_previous || priv->has_before_next)
5542 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5543 *min += arrow_spacing + scroll_arrow_vlength;
5552 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5553 gboolean show_arrows,
5559 gint *remaining_space)
5561 GtkNotebookPrivate *priv = notebook->priv;
5564 GtkNotebookPage *page;
5567 widget = GTK_WIDGET (notebook);
5568 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5570 if (show_arrows) /* first_tab <- focus_tab */
5572 *remaining_space = tab_space;
5574 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5575 gtk_widget_get_visible (priv->cur_page->child))
5577 gtk_notebook_calc_tabs (notebook,
5580 remaining_space, STEP_NEXT);
5583 if (tab_space <= 0 || *remaining_space <= 0)
5586 priv->first_tab = priv->focus_tab;
5587 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5589 page = priv->first_tab->data;
5590 *remaining_space = tab_space - page->requisition.width;
5597 if (priv->first_tab && priv->first_tab != priv->focus_tab)
5599 /* Is first_tab really predecessor of focus_tab? */
5600 page = priv->first_tab->data;
5601 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5602 gtk_widget_get_visible (page->child))
5603 for (children = priv->focus_tab;
5604 children && children != priv->first_tab;
5605 children = gtk_notebook_search_page (notebook,
5613 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5614 priv->first_tab = priv->focus_tab;
5616 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5620 /* calculate shown tabs counting backwards from the focus tab */
5621 gtk_notebook_calc_tabs (notebook,
5622 gtk_notebook_search_page (notebook,
5630 if (*remaining_space < 0)
5633 gtk_notebook_search_page (notebook, priv->first_tab,
5635 if (!priv->first_tab)
5636 priv->first_tab = priv->focus_tab;
5638 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5641 else /* focus_tab -> end */
5643 if (!priv->first_tab)
5644 priv->first_tab = gtk_notebook_search_page (notebook,
5649 gtk_notebook_calc_tabs (notebook,
5650 gtk_notebook_search_page (notebook,
5658 if (*remaining_space <= 0)
5659 *last_child = children;
5660 else /* start <- first_tab */
5665 gtk_notebook_calc_tabs (notebook,
5666 gtk_notebook_search_page (notebook,
5674 if (*remaining_space == 0)
5675 priv->first_tab = children;
5677 priv->first_tab = gtk_notebook_search_page(notebook,
5684 if (*remaining_space < 0)
5686 /* calculate number of tabs */
5687 *remaining_space = - (*remaining_space);
5690 for (children = priv->first_tab;
5691 children && children != *last_child;
5692 children = gtk_notebook_search_page (notebook, children,
5697 *remaining_space = 0;
5700 /* unmap all non-visible tabs */
5701 for (children = gtk_notebook_search_page (notebook, NULL,
5703 children && children != priv->first_tab;
5704 children = gtk_notebook_search_page (notebook, children,
5707 page = children->data;
5709 if (page->tab_label &&
5710 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5711 gtk_widget_set_child_visible (page->tab_label, FALSE);
5714 for (children = *last_child; children;
5715 children = gtk_notebook_search_page (notebook, children,
5718 page = children->data;
5720 if (page->tab_label &&
5721 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5722 gtk_widget_set_child_visible (page->tab_label, FALSE);
5725 else /* !show_arrows */
5727 GtkOrientation tab_expand_orientation;
5731 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5732 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5734 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5735 *remaining_space = max - min - tab_overlap - tab_space;
5736 children = priv->children;
5737 priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5741 page = children->data;
5742 children = children->next;
5744 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5745 !gtk_widget_get_visible (page->child))
5751 (gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5758 get_allocate_at_bottom (GtkWidget *widget,
5759 gint search_direction)
5761 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5762 gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5767 case GTK_POS_BOTTOM:
5769 return (search_direction == STEP_PREV);
5771 return (search_direction == STEP_NEXT);
5776 return (search_direction == STEP_PREV);
5784 gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5789 gint *remaining_space,
5790 gint *expanded_tabs,
5794 GtkNotebookPrivate *priv = notebook->priv;
5795 GtkAllocation allocation;
5797 GtkContainer *container;
5798 GtkNotebookPage *page;
5799 GtkStyleContext *context;
5800 gboolean allocate_at_bottom;
5801 gint tab_overlap, tab_pos, tab_extra_space;
5802 gint left_x, right_x, top_y, bottom_y, anchor;
5804 gboolean gap_left, packing_changed;
5805 GtkAllocation child_allocation = { 0, };
5806 GtkOrientation tab_expand_orientation;
5808 widget = GTK_WIDGET (notebook);
5809 container = GTK_CONTAINER (notebook);
5810 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5811 tab_pos = get_effective_tab_pos (notebook);
5812 allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5815 gtk_widget_get_allocation (widget, &allocation);
5817 border_width = gtk_container_get_border_width (container);
5818 child_allocation.x = allocation.x + border_width;
5819 child_allocation.y = allocation.y + border_width;
5821 context = gtk_widget_get_style_context (widget);
5825 case GTK_POS_BOTTOM:
5826 child_allocation.y = allocation.y + allocation.height -
5827 priv->cur_page->requisition.height - border_width;
5830 child_allocation.x = (allocate_at_bottom) ? max : min;
5831 child_allocation.height = priv->cur_page->requisition.height;
5832 anchor = child_allocation.x;
5836 child_allocation.x = allocation.x + allocation.width -
5837 priv->cur_page->requisition.width - border_width;
5840 child_allocation.y = (allocate_at_bottom) ? max : min;
5841 child_allocation.width = priv->cur_page->requisition.width;
5842 anchor = child_allocation.y;
5846 left_x = CLAMP (priv->mouse_x - priv->drag_offset_x,
5847 min, max - priv->cur_page->allocation.width);
5848 top_y = CLAMP (priv->mouse_y - priv->drag_offset_y,
5849 min, max - priv->cur_page->allocation.height);
5850 right_x = left_x + priv->cur_page->allocation.width;
5851 bottom_y = top_y + priv->cur_page->allocation.height;
5852 gap_left = packing_changed = FALSE;
5854 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5855 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5857 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5859 while (*children && *children != last_child)
5861 page = (*children)->data;
5863 if (direction == STEP_NEXT)
5864 *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5867 *children = (*children)->next;
5871 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5874 tab_extra_space = 0;
5875 if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5877 tab_extra_space = *remaining_space / *expanded_tabs;
5878 *remaining_space -= tab_extra_space;
5885 case GTK_POS_BOTTOM:
5886 child_allocation.width = MAX (1, page->requisition.width + tab_overlap + tab_extra_space);
5888 /* make sure that the reordered tab doesn't go past the last position */
5889 if (priv->operation == DRAG_OPERATION_REORDER &&
5890 !gap_left && packing_changed)
5892 if (!allocate_at_bottom)
5894 if (left_x >= anchor)
5896 left_x = priv->drag_window_x = anchor;
5897 anchor += priv->cur_page->allocation.width - tab_overlap;
5902 if (right_x <= anchor)
5904 anchor -= priv->cur_page->allocation.width;
5905 left_x = priv->drag_window_x = anchor;
5906 anchor += tab_overlap;
5913 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5915 priv->drag_window_x = left_x;
5916 priv->drag_window_y = child_allocation.y;
5920 if (allocate_at_bottom)
5921 anchor -= child_allocation.width;
5923 if (priv->operation == DRAG_OPERATION_REORDER)
5925 if (!allocate_at_bottom &&
5927 left_x <= anchor + child_allocation.width / 2)
5928 anchor += priv->cur_page->allocation.width - tab_overlap;
5929 else if (allocate_at_bottom &&
5930 right_x >= anchor + child_allocation.width / 2 &&
5931 right_x <= anchor + child_allocation.width)
5932 anchor -= priv->cur_page->allocation.width - tab_overlap;
5935 child_allocation.x = anchor;
5941 child_allocation.height = MAX (1, page->requisition.height + tab_overlap + tab_extra_space);
5943 /* make sure that the reordered tab doesn't go past the last position */
5944 if (priv->operation == DRAG_OPERATION_REORDER &&
5945 !gap_left && packing_changed)
5947 if (!allocate_at_bottom && top_y >= anchor)
5949 top_y = priv->drag_window_y = anchor;
5950 anchor += priv->cur_page->allocation.height - tab_overlap;
5956 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5958 priv->drag_window_x = child_allocation.x;
5959 priv->drag_window_y = top_y;
5963 if (allocate_at_bottom)
5964 anchor -= child_allocation.height;
5966 if (priv->operation == DRAG_OPERATION_REORDER)
5968 if (!allocate_at_bottom &&
5970 top_y <= anchor + child_allocation.height / 2)
5971 anchor += priv->cur_page->allocation.height - tab_overlap;
5972 else if (allocate_at_bottom &&
5973 bottom_y >= anchor + child_allocation.height / 2 &&
5974 bottom_y <= anchor + child_allocation.height)
5975 anchor -= priv->cur_page->allocation.height - tab_overlap;
5978 child_allocation.y = anchor;
5984 page->allocation = child_allocation;
5986 if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
5987 (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
5989 /* needs to be allocated at 0,0
5990 * to be shown in the drag window */
5991 page->allocation.x = 0;
5992 page->allocation.y = 0;
5995 if (page != priv->cur_page)
5997 GtkBorder active_padding, normal_padding, padding;
5999 /* The active tab is by definition at least the same height as the inactive one.
6000 * The padding we're building is the offset between the two tab states,
6001 * so in case the style specifies normal_padding > active_padding we
6002 * remove the offset and draw them with the same height.
6003 * Note that the padding will still be applied to the tab content though,
6004 * see gtk_notebook_page_allocate().
6006 gtk_style_context_save (context);
6007 notebook_tab_prepare_style_context (notebook, page, context, TRUE);
6009 gtk_style_context_get_padding (context, GTK_STATE_FLAG_ACTIVE, &active_padding);
6010 gtk_style_context_get_padding (context, GTK_STATE_FLAG_NORMAL, &normal_padding);
6012 gtk_style_context_restore (context);
6014 padding.top = MAX (0, active_padding.top - normal_padding.top);
6015 padding.right = MAX (0, active_padding.right - normal_padding.right);
6016 padding.bottom = MAX (0, active_padding.bottom - normal_padding.bottom);
6017 padding.left = MAX (0, active_padding.left - normal_padding.left);
6022 page->allocation.y += padding.top + padding.bottom;
6023 page->allocation.height = MAX (1, page->allocation.height - padding.top - padding.bottom);
6025 case GTK_POS_BOTTOM:
6026 page->allocation.height = MAX (1, page->allocation.height - padding.top - padding.bottom);
6029 page->allocation.x += padding.left + padding.right;
6030 page->allocation.width = MAX (1, page->allocation.width - padding.left - padding.right);
6033 page->allocation.width = MAX (1, page->allocation.width - padding.left - padding.right);
6038 /* calculate whether to leave a gap based on reorder operation or not */
6042 case GTK_POS_BOTTOM:
6043 if (priv->operation != DRAG_OPERATION_REORDER ||
6044 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6046 if (priv->operation == DRAG_OPERATION_REORDER)
6048 if (!allocate_at_bottom &&
6049 left_x > anchor + child_allocation.width / 2 &&
6050 left_x <= anchor + child_allocation.width)
6051 anchor += priv->cur_page->allocation.width - tab_overlap;
6052 else if (allocate_at_bottom &&
6053 right_x >= anchor &&
6054 right_x <= anchor + child_allocation.width / 2)
6055 anchor -= priv->cur_page->allocation.width - tab_overlap;
6058 if (!allocate_at_bottom)
6059 anchor += child_allocation.width - tab_overlap;
6061 anchor += tab_overlap;
6067 if (priv->operation != DRAG_OPERATION_REORDER ||
6068 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6070 if (priv->operation == DRAG_OPERATION_REORDER)
6072 if (!allocate_at_bottom &&
6073 top_y >= anchor + child_allocation.height / 2 &&
6074 top_y <= anchor + child_allocation.height)
6075 anchor += priv->cur_page->allocation.height - tab_overlap;
6076 else if (allocate_at_bottom &&
6077 bottom_y >= anchor &&
6078 bottom_y <= anchor + child_allocation.height / 2)
6079 anchor -= priv->cur_page->allocation.height - tab_overlap;
6082 if (!allocate_at_bottom)
6083 anchor += child_allocation.height - tab_overlap;
6085 anchor += tab_overlap;
6091 /* set child visible */
6092 if (page->tab_label)
6093 gtk_widget_set_child_visible (page->tab_label, TRUE);
6096 /* Don't move the current tab past the last position during tabs reordering */
6098 priv->operation == DRAG_OPERATION_REORDER &&
6099 direction == STEP_NEXT)
6104 case GTK_POS_BOTTOM:
6105 if (allocate_at_bottom)
6106 anchor -= priv->cur_page->allocation.width;
6108 if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
6109 (allocate_at_bottom && priv->drag_window_x < anchor))
6110 priv->drag_window_x = anchor;
6114 if (allocate_at_bottom)
6115 anchor -= priv->cur_page->allocation.height;
6117 if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
6118 (allocate_at_bottom && priv->drag_window_y < anchor))
6119 priv->drag_window_y = anchor;
6126 gtk_notebook_pages_allocate (GtkNotebook *notebook)
6128 GtkNotebookPrivate *priv = notebook->priv;
6129 GList *children = NULL;
6130 GList *last_child = NULL;
6131 gboolean showarrow = FALSE;
6132 gint tab_space, min, max, remaining_space;
6134 gboolean tab_allocations_changed = FALSE;
6136 if (!priv->show_tabs || !priv->children || !priv->cur_page)
6139 min = max = tab_space = remaining_space = 0;
6142 gtk_notebook_tab_space (notebook, &showarrow,
6143 &min, &max, &tab_space);
6145 gtk_notebook_calculate_shown_tabs (notebook, showarrow,
6146 min, max, tab_space, &last_child,
6147 &expanded_tabs, &remaining_space);
6149 children = priv->first_tab;
6150 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6151 showarrow, STEP_NEXT,
6152 &remaining_space, &expanded_tabs, min, max);
6153 if (children && children != last_child)
6155 children = priv->children;
6156 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6157 showarrow, STEP_PREV,
6158 &remaining_space, &expanded_tabs, min, max);
6161 children = priv->children;
6165 if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
6166 tab_allocations_changed = TRUE;
6167 children = children->next;
6170 if (!priv->first_tab)
6171 priv->first_tab = priv->children;
6173 if (tab_allocations_changed)
6174 gtk_notebook_redraw_tabs (notebook);
6178 gtk_notebook_page_allocate (GtkNotebook *notebook,
6179 GtkNotebookPage *page)
6181 GtkWidget *widget = GTK_WIDGET (notebook);
6182 GtkNotebookPrivate *priv = notebook->priv;
6183 GtkAllocation child_allocation, label_allocation;
6184 GtkRequisition tab_requisition;
6185 GtkStyleContext *context;
6187 gint focus_width, focus_padding;
6188 gint tab_curvature, tab_overlap;
6189 gint tab_pos = get_effective_tab_pos (notebook);
6190 gboolean tab_allocation_changed;
6191 gboolean was_visible = page->tab_allocated_visible;
6192 GtkBorder tab_padding;
6193 GtkStateFlags state;
6195 if (!page->tab_label ||
6196 !gtk_widget_get_visible (page->tab_label) ||
6197 !gtk_widget_get_child_visible (page->tab_label))
6199 page->tab_allocated_visible = FALSE;
6203 context = gtk_widget_get_style_context (widget);
6205 gtk_style_context_save (context);
6206 state = notebook_tab_prepare_style_context (notebook, page, context, TRUE);
6208 gtk_style_context_get_padding (context, state, &tab_padding);
6210 gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
6211 gtk_widget_style_get (widget,
6212 "focus-line-width", &focus_width,
6213 "focus-padding", &focus_padding,
6214 "tab-curvature", &tab_curvature,
6215 "tab-overlap", &tab_overlap,
6220 case GTK_POS_BOTTOM:
6221 padding = tab_curvature + focus_width + focus_padding;
6224 child_allocation.x = tab_padding.left + padding;
6225 child_allocation.width = MAX (1, (page->allocation.width -
6226 tab_padding.left - tab_padding.right -
6228 child_allocation.x += page->allocation.x;
6230 /* if we're drawing an inactive page, trim the allocation width
6231 * for the children by the difference between tab-curvature
6233 * if we're after the active tab, we need to trim the x
6234 * coordinate of the allocation too, to position it after
6235 * the end of the overlap.
6237 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.left, tab_padding.right))
6239 if (gtk_notebook_page_num (notebook, page->child) >
6240 gtk_notebook_page_num (notebook, priv->cur_page->child))
6242 child_allocation.x += tab_overlap - tab_curvature - tab_padding.left;
6243 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.left;
6247 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.right;
6253 child_allocation.x = page->allocation.x +
6254 (page->allocation.width - tab_requisition.width) / 2;
6256 child_allocation.width = tab_requisition.width;
6259 child_allocation.y =
6260 page->allocation.y + tab_padding.top + focus_width + focus_padding;
6262 child_allocation.height = MAX (1, (page->allocation.height -
6263 tab_padding.top - tab_padding.bottom -
6264 2 * (focus_width + focus_padding)));
6268 padding = tab_curvature + focus_width + focus_padding;
6271 child_allocation.y = tab_padding.top + padding;
6272 child_allocation.height = MAX (1, (page->allocation.height -
6273 tab_padding.bottom - tab_padding.top -
6275 child_allocation.y += page->allocation.y;
6277 /* if we're drawing an inactive page, trim the allocation height
6278 * for the children by the difference between tab-curvature
6280 * if we're after the active tab, we need to trim the y
6281 * coordinate of the allocation too, to position it after
6282 * the end of the overlap.
6284 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.top, tab_padding.bottom))
6286 if (gtk_notebook_page_num (notebook, page->child) >
6287 gtk_notebook_page_num (notebook, priv->cur_page->child))
6289 child_allocation.y += tab_overlap - tab_curvature - tab_padding.top;
6290 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.top;
6294 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.bottom;
6300 child_allocation.y = page->allocation.y +
6301 (page->allocation.height - tab_requisition.height) / 2;
6303 child_allocation.height = tab_requisition.height;
6306 child_allocation.x =
6307 page->allocation.x + tab_padding.left + focus_width + focus_padding;
6309 child_allocation.width = MAX (1, (page->allocation.width -
6310 tab_padding.left - tab_padding.right -
6311 2 * (focus_width + focus_padding)));
6315 gtk_widget_get_allocation (page->tab_label, &label_allocation);
6316 tab_allocation_changed = (child_allocation.x != label_allocation.x ||
6317 child_allocation.y != label_allocation.y ||
6318 child_allocation.width != label_allocation.width ||
6319 child_allocation.height != label_allocation.height);
6321 gtk_widget_size_allocate (page->tab_label, &child_allocation);
6325 page->tab_allocated_visible = TRUE;
6326 tab_allocation_changed = TRUE;
6329 gtk_style_context_restore (context);
6331 return tab_allocation_changed;
6335 gtk_notebook_calc_tabs (GtkNotebook *notebook,
6341 GtkNotebookPage *page = NULL;
6343 GList *last_calculated_child = NULL;
6344 gint tab_pos = get_effective_tab_pos (notebook);
6354 case GTK_POS_BOTTOM:
6357 page = children->data;
6358 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6359 gtk_widget_get_visible (page->child))
6361 *tab_space -= page->requisition.width;
6362 if (*tab_space < 0 || children == *end)
6366 *tab_space = - (*tab_space +
6367 page->requisition.width);
6369 if (*tab_space == 0 && direction == STEP_PREV)
6370 children = last_calculated_child;
6377 last_calculated_child = children;
6379 if (direction == STEP_NEXT)
6380 children = children->next;
6382 children = children->prev;
6389 page = children->data;
6390 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6391 gtk_widget_get_visible (page->child))
6393 *tab_space -= page->requisition.height;
6394 if (*tab_space < 0 || children == *end)
6398 *tab_space = - (*tab_space + page->requisition.height);
6400 if (*tab_space == 0 && direction == STEP_PREV)
6401 children = last_calculated_child;
6408 last_calculated_child = children;
6410 if (direction == STEP_NEXT)
6411 children = children->next;
6413 children = children->prev;
6420 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6422 GtkNotebookPrivate *priv = notebook->priv;
6426 pos = gtk_widget_path_length (gtk_widget_get_path (GTK_WIDGET (notebook))) - 1;
6428 for (list = priv->children; list != NULL; list = list->next)
6430 GtkNotebookPage *page = list->data;
6432 if (page->tab_label)
6434 GtkRegionFlags current_flags;
6436 if (page == priv->cur_page)
6437 gtk_widget_set_state_flags (page->tab_label, GTK_STATE_FLAG_ACTIVE, FALSE);
6439 gtk_widget_unset_state_flags (page->tab_label, GTK_STATE_FLAG_ACTIVE);
6441 /* FIXME: We should store these flags somewhere instead of poking
6442 * the widget's path */
6443 if (!gtk_widget_path_iter_has_region (gtk_widget_get_path (page->tab_label),
6445 GTK_STYLE_REGION_TAB,
6447 || current_flags != _gtk_notebook_get_tab_flags (notebook, page))
6448 gtk_widget_reset_style (page->tab_label);
6453 /* Private GtkNotebook Page Switch Methods:
6455 * gtk_notebook_real_switch_page
6458 gtk_notebook_real_switch_page (GtkNotebook *notebook,
6462 GtkNotebookPrivate *priv = notebook->priv;
6463 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6464 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6465 gboolean child_has_focus;
6467 if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6470 /* save the value here, changing visibility changes focus */
6471 child_has_focus = priv->child_has_focus;
6474 gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6476 priv->cur_page = page;
6478 if (!priv->focus_tab ||
6479 priv->focus_tab->data != (gpointer) priv->cur_page)
6481 g_list_find (priv->children, priv->cur_page);
6483 gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6485 /* If the focus was on the previous page, move it to the first
6486 * element on the new page, if possible, or if not, to the
6489 if (child_has_focus)
6491 if (priv->cur_page->last_focus_child &&
6492 gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6493 gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6495 if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6496 gtk_widget_grab_focus (GTK_WIDGET (notebook));
6499 gtk_notebook_update_tab_states (notebook);
6500 gtk_notebook_pages_allocate (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 gtk_notebook_switch_page (notebook, page);
6594 gtk_notebook_menu_switch_page (GtkWidget *widget,
6595 GtkNotebookPage *page)
6597 GtkNotebookPrivate *priv;
6598 GtkNotebook *notebook;
6603 parent = gtk_widget_get_parent (widget);
6604 notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6605 priv = notebook->priv;
6607 if (priv->cur_page == page)
6611 children = priv->children;
6612 while (children && children->data != page)
6614 children = children->next;
6618 g_signal_emit (notebook,
6619 notebook_signals[SWITCH_PAGE],
6625 /* Private GtkNotebook Menu Functions:
6627 * gtk_notebook_menu_item_create
6628 * gtk_notebook_menu_label_unparent
6629 * gtk_notebook_menu_detacher
6632 gtk_notebook_menu_item_create (GtkNotebook *notebook,
6635 GtkNotebookPrivate *priv = notebook->priv;
6636 GtkNotebookPage *page;
6637 GtkWidget *menu_item;
6640 if (page->default_menu)
6642 if (GTK_IS_LABEL (page->tab_label))
6643 page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6645 page->menu_label = gtk_label_new ("");
6646 gtk_widget_set_halign (page->menu_label, GTK_ALIGN_START);
6647 gtk_widget_set_valign (page->menu_label, GTK_ALIGN_CENTER);
6650 gtk_widget_show (page->menu_label);
6651 menu_item = gtk_menu_item_new ();
6652 gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6653 gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6654 g_list_position (priv->children, list));
6655 g_signal_connect (menu_item, "activate",
6656 G_CALLBACK (gtk_notebook_menu_switch_page), page);
6657 if (gtk_widget_get_visible (page->child))
6658 gtk_widget_show (menu_item);
6662 gtk_notebook_menu_label_unparent (GtkWidget *widget,
6665 gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6666 _gtk_bin_set_child (GTK_BIN (widget), NULL);
6670 gtk_notebook_menu_detacher (GtkWidget *widget,
6673 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6674 GtkNotebookPrivate *priv = notebook->priv;
6676 g_return_if_fail (priv->menu == (GtkWidget*) menu);
6681 /* Public GtkNotebook Page Insert/Remove Methods :
6683 * gtk_notebook_append_page
6684 * gtk_notebook_append_page_menu
6685 * gtk_notebook_prepend_page
6686 * gtk_notebook_prepend_page_menu
6687 * gtk_notebook_insert_page
6688 * gtk_notebook_insert_page_menu
6689 * gtk_notebook_remove_page
6692 * gtk_notebook_append_page:
6693 * @notebook: a #GtkNotebook
6694 * @child: the #GtkWidget to use as the contents of the page
6695 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6696 * for the page, or %NULL to use the default label, 'page N'
6698 * Appends a page to @notebook.
6700 * Return value: the index (starting from 0) of the appended
6701 * page in the notebook, or -1 if function fails
6704 gtk_notebook_append_page (GtkNotebook *notebook,
6706 GtkWidget *tab_label)
6708 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6709 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6710 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6712 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6716 * gtk_notebook_append_page_menu:
6717 * @notebook: a #GtkNotebook
6718 * @child: the #GtkWidget to use as the contents of the page
6719 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6720 * for the page, or %NULL to use the default label, 'page N'
6721 * @menu_label: (allow-none): the widget to use as a label for the
6722 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6723 * is a #GtkLabel or %NULL, then the menu label will be a newly
6724 * created label with the same text as @tab_label; if @tab_label
6725 * is not a #GtkLabel, @menu_label must be specified if the
6726 * page-switch menu is to be used.
6728 * Appends a page to @notebook, specifying the widget to use as the
6729 * label in the popup menu.
6731 * Return value: the index (starting from 0) of the appended
6732 * page in the notebook, or -1 if function fails
6735 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6737 GtkWidget *tab_label,
6738 GtkWidget *menu_label)
6740 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6741 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6742 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6743 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6745 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6749 * gtk_notebook_prepend_page:
6750 * @notebook: a #GtkNotebook
6751 * @child: the #GtkWidget to use as the contents of the page
6752 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6753 * for the page, or %NULL to use the default label, 'page N'
6755 * Prepends a page to @notebook.
6757 * Return value: the index (starting from 0) of the prepended
6758 * page in the notebook, or -1 if function fails
6761 gtk_notebook_prepend_page (GtkNotebook *notebook,
6763 GtkWidget *tab_label)
6765 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6766 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6767 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6769 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6773 * gtk_notebook_prepend_page_menu:
6774 * @notebook: a #GtkNotebook
6775 * @child: the #GtkWidget to use as the contents of the page
6776 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6777 * for the page, or %NULL to use the default label, 'page N'
6778 * @menu_label: (allow-none): the widget to use as a label for the
6779 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6780 * is a #GtkLabel or %NULL, then the menu label will be a newly
6781 * created label with the same text as @tab_label; if @tab_label
6782 * is not a #GtkLabel, @menu_label must be specified if the
6783 * page-switch menu is to be used.
6785 * Prepends a page to @notebook, specifying the widget to use as the
6786 * label in the popup menu.
6788 * Return value: the index (starting from 0) of the prepended
6789 * page in the notebook, or -1 if function fails
6792 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6794 GtkWidget *tab_label,
6795 GtkWidget *menu_label)
6797 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6798 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6799 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6800 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6802 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6806 * gtk_notebook_insert_page:
6807 * @notebook: a #GtkNotebook
6808 * @child: the #GtkWidget to use as the contents of the page
6809 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6810 * for the page, or %NULL to use the default label, 'page N'
6811 * @position: the index (starting at 0) at which to insert the page,
6812 * or -1 to append the page after all other pages
6814 * Insert a page into @notebook at the given position.
6816 * Return value: the index (starting from 0) of the inserted
6817 * page in the notebook, or -1 if function fails
6820 gtk_notebook_insert_page (GtkNotebook *notebook,
6822 GtkWidget *tab_label,
6825 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6826 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6827 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6829 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6834 gtk_notebook_page_compare_tab (gconstpointer a,
6837 return (((GtkNotebookPage *) a)->tab_label != b);
6841 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6845 GtkNotebook *notebook = GTK_NOTEBOOK (data);
6846 GtkNotebookPrivate *priv = notebook->priv;
6849 list = g_list_find_custom (priv->children, child,
6850 gtk_notebook_page_compare_tab);
6853 GtkNotebookPage *page = list->data;
6855 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
6856 gtk_notebook_switch_page (notebook, page);
6857 focus_tabs_in (notebook);
6864 * gtk_notebook_insert_page_menu:
6865 * @notebook: a #GtkNotebook
6866 * @child: the #GtkWidget to use as the contents of the page
6867 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6868 * for the page, or %NULL to use the default label, 'page N'
6869 * @menu_label: (allow-none): the widget to use as a label for the
6870 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6871 * is a #GtkLabel or %NULL, then the menu label will be a newly
6872 * created label with the same text as @tab_label; if @tab_label
6873 * is not a #GtkLabel, @menu_label must be specified if the
6874 * page-switch menu is to be used.
6875 * @position: the index (starting at 0) at which to insert the page,
6876 * or -1 to append the page after all other pages.
6878 * Insert a page into @notebook at the given position, specifying
6879 * the widget to use as the label in the popup menu.
6881 * Return value: the index (starting from 0) of the inserted
6882 * page in the notebook
6885 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
6887 GtkWidget *tab_label,
6888 GtkWidget *menu_label,
6891 GtkNotebookClass *class;
6893 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6894 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6895 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6896 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6898 class = GTK_NOTEBOOK_GET_CLASS (notebook);
6900 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
6904 * gtk_notebook_remove_page:
6905 * @notebook: a #GtkNotebook
6906 * @page_num: the index of a notebook page, starting
6907 * from 0. If -1, the last page will be removed.
6909 * Removes a page from the notebook given its index
6913 gtk_notebook_remove_page (GtkNotebook *notebook,
6916 GtkNotebookPrivate *priv;
6919 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6921 priv = notebook->priv;
6924 list = g_list_nth (priv->children, page_num);
6926 list = g_list_last (priv->children);
6929 gtk_container_remove (GTK_CONTAINER (notebook),
6930 ((GtkNotebookPage *) list->data)->child);
6933 /* Public GtkNotebook Page Switch Methods :
6934 * gtk_notebook_get_current_page
6935 * gtk_notebook_page_num
6936 * gtk_notebook_set_current_page
6937 * gtk_notebook_next_page
6938 * gtk_notebook_prev_page
6941 * gtk_notebook_get_current_page:
6942 * @notebook: a #GtkNotebook
6944 * Returns the page number of the current page.
6946 * Return value: the index (starting from 0) of the current
6947 * page in the notebook. If the notebook has no pages,
6948 * then -1 will be returned.
6951 gtk_notebook_get_current_page (GtkNotebook *notebook)
6953 GtkNotebookPrivate *priv;
6955 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6957 priv = notebook->priv;
6959 if (!priv->cur_page)
6962 return g_list_index (priv->children, priv->cur_page);
6966 * gtk_notebook_get_nth_page:
6967 * @notebook: a #GtkNotebook
6968 * @page_num: the index of a page in the notebook, or -1
6969 * to get the last page
6971 * Returns the child widget contained in page number @page_num.
6973 * Return value: (transfer none): the child widget, or %NULL
6974 * if @page_num is out of bounds
6977 gtk_notebook_get_nth_page (GtkNotebook *notebook,
6980 GtkNotebookPrivate *priv;
6981 GtkNotebookPage *page;
6984 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6986 priv = notebook->priv;
6989 list = g_list_nth (priv->children, page_num);
6991 list = g_list_last (priv->children);
7003 * gtk_notebook_get_n_pages:
7004 * @notebook: a #GtkNotebook
7006 * Gets the number of pages in a notebook.
7008 * Return value: the number of pages in the notebook
7013 gtk_notebook_get_n_pages (GtkNotebook *notebook)
7015 GtkNotebookPrivate *priv;
7017 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
7019 priv = notebook->priv;
7021 return g_list_length (priv->children);
7025 * gtk_notebook_page_num:
7026 * @notebook: a #GtkNotebook
7027 * @child: a #GtkWidget
7029 * Finds the index of the page which contains the given child
7032 * Return value: the index of the page containing @child, or
7033 * -1 if @child is not in the notebook
7036 gtk_notebook_page_num (GtkNotebook *notebook,
7039 GtkNotebookPrivate *priv;
7043 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7045 priv = notebook->priv;
7048 children = priv->children;
7051 GtkNotebookPage *page = children->data;
7053 if (page->child == child)
7056 children = children->next;
7064 * gtk_notebook_set_current_page:
7065 * @notebook: a #GtkNotebook
7066 * @page_num: index of the page to switch to, starting from 0.
7067 * If negative, the last page will be used. If greater
7068 * than the number of pages in the notebook, nothing
7071 * Switches to the page number @page_num.
7073 * Note that due to historical reasons, GtkNotebook refuses
7074 * to switch to a page unless the child widget is visible.
7075 * Therefore, it is recommended to show child widgets before
7076 * adding them to a notebook.
7079 gtk_notebook_set_current_page (GtkNotebook *notebook,
7082 GtkNotebookPrivate *priv;
7085 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7087 priv = notebook->priv;
7090 page_num = g_list_length (priv->children) - 1;
7092 list = g_list_nth (priv->children, page_num);
7094 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7098 * gtk_notebook_next_page:
7099 * @notebook: a #GtkNotebook
7101 * Switches to the next page. Nothing happens if the current page is
7105 gtk_notebook_next_page (GtkNotebook *notebook)
7107 GtkNotebookPrivate *priv;
7110 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7112 priv = notebook->priv;
7114 list = g_list_find (priv->children, priv->cur_page);
7118 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
7122 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7126 * gtk_notebook_prev_page:
7127 * @notebook: a #GtkNotebook
7129 * Switches to the previous page. Nothing happens if the current page
7130 * is the first page.
7133 gtk_notebook_prev_page (GtkNotebook *notebook)
7135 GtkNotebookPrivate *priv;
7138 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7140 priv = notebook->priv;
7142 list = g_list_find (priv->children, priv->cur_page);
7146 list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
7150 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7153 /* Public GtkNotebook/Tab Style Functions
7155 * gtk_notebook_set_show_border
7156 * gtk_notebook_get_show_border
7157 * gtk_notebook_set_show_tabs
7158 * gtk_notebook_get_show_tabs
7159 * gtk_notebook_set_tab_pos
7160 * gtk_notebook_get_tab_pos
7161 * gtk_notebook_set_scrollable
7162 * gtk_notebook_get_scrollable
7163 * gtk_notebook_get_tab_hborder
7164 * gtk_notebook_get_tab_vborder
7167 * gtk_notebook_set_show_border:
7168 * @notebook: a #GtkNotebook
7169 * @show_border: %TRUE if a bevel should be drawn around the notebook
7171 * Sets whether a bevel will be drawn around the notebook pages.
7172 * This only has a visual effect when the tabs are not shown.
7173 * See gtk_notebook_set_show_tabs().
7176 gtk_notebook_set_show_border (GtkNotebook *notebook,
7177 gboolean show_border)
7179 GtkNotebookPrivate *priv;
7181 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7183 priv = notebook->priv;
7185 if (priv->show_border != show_border)
7187 priv->show_border = show_border;
7189 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7190 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7192 g_object_notify (G_OBJECT (notebook), "show-border");
7197 * gtk_notebook_get_show_border:
7198 * @notebook: a #GtkNotebook
7200 * Returns whether a bevel will be drawn around the notebook pages.
7201 * See gtk_notebook_set_show_border().
7203 * Return value: %TRUE if the bevel is drawn
7206 gtk_notebook_get_show_border (GtkNotebook *notebook)
7208 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7210 return notebook->priv->show_border;
7214 * gtk_notebook_set_show_tabs:
7215 * @notebook: a #GtkNotebook
7216 * @show_tabs: %TRUE if the tabs should be shown
7218 * Sets whether to show the tabs for the notebook or not.
7221 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
7224 GtkNotebookPrivate *priv;
7225 GtkNotebookPage *page;
7226 GtkStyleContext *context;
7230 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7232 priv = notebook->priv;
7234 show_tabs = show_tabs != FALSE;
7236 if (priv->show_tabs == show_tabs)
7239 priv->show_tabs = show_tabs;
7240 children = priv->children;
7241 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
7245 gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
7249 page = children->data;
7250 children = children->next;
7251 if (page->default_tab)
7253 gtk_widget_destroy (page->tab_label);
7254 page->tab_label = NULL;
7257 gtk_widget_hide (page->tab_label);
7260 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7264 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
7265 gtk_notebook_update_labels (notebook);
7266 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7269 for (i = 0; i < N_ACTION_WIDGETS; i++)
7271 if (priv->action_widget[i])
7272 gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
7275 gtk_widget_reset_style (GTK_WIDGET (notebook));
7276 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7278 g_object_notify (G_OBJECT (notebook), "show-tabs");
7282 * gtk_notebook_get_show_tabs:
7283 * @notebook: a #GtkNotebook
7285 * Returns whether the tabs of the notebook are shown.
7286 * See gtk_notebook_set_show_tabs().
7288 * Return value: %TRUE if the tabs are shown
7291 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
7293 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7295 return notebook->priv->show_tabs;
7299 * gtk_notebook_set_tab_pos:
7300 * @notebook: a #GtkNotebook.
7301 * @pos: the edge to draw the tabs at
7303 * Sets the edge at which the tabs for switching pages in the
7304 * notebook are drawn.
7307 gtk_notebook_set_tab_pos (GtkNotebook *notebook,
7308 GtkPositionType pos)
7310 GtkNotebookPrivate *priv;
7312 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7314 priv = notebook->priv;
7316 if (priv->tab_pos != pos)
7318 priv->tab_pos = pos;
7319 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7320 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7323 g_object_notify (G_OBJECT (notebook), "tab-pos");
7327 * gtk_notebook_get_tab_pos:
7328 * @notebook: a #GtkNotebook
7330 * Gets the edge at which the tabs for switching pages in the
7331 * notebook are drawn.
7333 * Return value: the edge at which the tabs are drawn
7336 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
7338 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
7340 return notebook->priv->tab_pos;
7344 * gtk_notebook_set_scrollable:
7345 * @notebook: a #GtkNotebook
7346 * @scrollable: %TRUE if scroll arrows should be added
7348 * Sets whether the tab label area will have arrows for
7349 * scrolling if there are too many tabs to fit in the area.
7352 gtk_notebook_set_scrollable (GtkNotebook *notebook,
7353 gboolean scrollable)
7355 GtkNotebookPrivate *priv;
7357 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7359 priv = notebook->priv;
7361 scrollable = (scrollable != FALSE);
7363 if (scrollable != priv->scrollable)
7365 priv->scrollable = scrollable;
7367 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7368 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7370 g_object_notify (G_OBJECT (notebook), "scrollable");
7375 * gtk_notebook_get_scrollable:
7376 * @notebook: a #GtkNotebook
7378 * Returns whether the tab label area has arrows for scrolling.
7379 * See gtk_notebook_set_scrollable().
7381 * Return value: %TRUE if arrows for scrolling are present
7384 gtk_notebook_get_scrollable (GtkNotebook *notebook)
7386 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7388 return notebook->priv->scrollable;
7392 * gtk_notebook_get_tab_hborder:
7393 * @notebook: a #GtkNotebook
7395 * Returns the horizontal width of a tab border.
7397 * Return value: horizontal width of a tab border
7401 * Deprecated: 3.4: this function returns zero
7404 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
7406 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7412 * gtk_notebook_get_tab_vborder:
7413 * @notebook: a #GtkNotebook
7415 * Returns the vertical width of a tab border.
7417 * Return value: vertical width of a tab border
7421 * Deprecated: 3.4: this function returns zero
7424 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7426 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7432 /* Public GtkNotebook Popup Menu Methods:
7434 * gtk_notebook_popup_enable
7435 * gtk_notebook_popup_disable
7440 * gtk_notebook_popup_enable:
7441 * @notebook: a #GtkNotebook
7443 * Enables the popup menu: if the user clicks with the right
7444 * mouse button on the tab labels, a menu with all the pages
7445 * will be popped up.
7448 gtk_notebook_popup_enable (GtkNotebook *notebook)
7450 GtkNotebookPrivate *priv;
7453 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7455 priv = notebook->priv;
7460 priv->menu = gtk_menu_new ();
7461 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7463 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7464 gtk_notebook_menu_item_create (notebook, list);
7466 gtk_notebook_update_labels (notebook);
7467 gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7468 GTK_WIDGET (notebook),
7469 gtk_notebook_menu_detacher);
7471 g_object_notify (G_OBJECT (notebook), "enable-popup");
7475 * gtk_notebook_popup_disable:
7476 * @notebook: a #GtkNotebook
7478 * Disables the popup menu.
7481 gtk_notebook_popup_disable (GtkNotebook *notebook)
7483 GtkNotebookPrivate *priv;
7485 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7487 priv = notebook->priv;
7492 gtk_container_foreach (GTK_CONTAINER (priv->menu),
7493 (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7494 gtk_widget_destroy (priv->menu);
7496 g_object_notify (G_OBJECT (notebook), "enable-popup");
7499 /* Public GtkNotebook Page Properties Functions:
7501 * gtk_notebook_get_tab_label
7502 * gtk_notebook_set_tab_label
7503 * gtk_notebook_set_tab_label_text
7504 * gtk_notebook_get_menu_label
7505 * gtk_notebook_set_menu_label
7506 * gtk_notebook_set_menu_label_text
7507 * gtk_notebook_get_tab_reorderable
7508 * gtk_notebook_set_tab_reorderable
7509 * gtk_notebook_get_tab_detachable
7510 * gtk_notebook_set_tab_detachable
7514 * gtk_notebook_get_tab_label:
7515 * @notebook: a #GtkNotebook
7518 * Returns the tab label widget for the page @child.
7519 * %NULL is returned if @child is not in @notebook or
7520 * if no tab label has specifically been set for @child.
7522 * Return value: (transfer none): the tab label
7525 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7530 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7531 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7533 list = CHECK_FIND_CHILD (notebook, child);
7537 if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7540 return GTK_NOTEBOOK_PAGE (list)->tab_label;
7544 * gtk_notebook_set_tab_label:
7545 * @notebook: a #GtkNotebook
7547 * @tab_label: (allow-none): the tab label widget to use, or %NULL
7548 * for default tab label
7550 * Changes the tab label for @child.
7551 * If %NULL is specified for @tab_label, then the page will
7552 * have the label 'page N'.
7555 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7557 GtkWidget *tab_label)
7559 GtkNotebookPrivate *priv;
7560 GtkNotebookPage *page;
7563 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7564 g_return_if_fail (GTK_IS_WIDGET (child));
7566 priv = notebook->priv;
7568 list = CHECK_FIND_CHILD (notebook, child);
7572 /* a NULL pointer indicates a default_tab setting, otherwise
7573 * we need to set the associated label
7577 if (page->tab_label == tab_label)
7581 gtk_notebook_remove_tab_label (notebook, page);
7585 page->default_tab = FALSE;
7586 page->tab_label = tab_label;
7587 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7591 page->default_tab = TRUE;
7592 page->tab_label = NULL;
7594 if (priv->show_tabs)
7598 g_snprintf (string, sizeof(string), _("Page %u"),
7599 g_list_position (priv->children, list));
7600 page->tab_label = gtk_label_new (string);
7601 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7605 if (page->tab_label)
7606 page->mnemonic_activate_signal =
7607 g_signal_connect (page->tab_label,
7608 "mnemonic-activate",
7609 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7612 if (priv->show_tabs && gtk_widget_get_visible (child))
7614 gtk_widget_show (page->tab_label);
7615 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7618 gtk_notebook_update_tab_states (notebook);
7619 gtk_widget_child_notify (child, "tab-label");
7623 * gtk_notebook_set_tab_label_text:
7624 * @notebook: a #GtkNotebook
7626 * @tab_text: the label text
7628 * Creates a new label and sets it as the tab label for the page
7629 * containing @child.
7632 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7634 const gchar *tab_text)
7636 GtkWidget *tab_label = NULL;
7638 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7641 tab_label = gtk_label_new (tab_text);
7642 gtk_notebook_set_tab_label (notebook, child, tab_label);
7643 gtk_widget_child_notify (child, "tab-label");
7647 * gtk_notebook_get_tab_label_text:
7648 * @notebook: a #GtkNotebook
7649 * @child: a widget contained in a page of @notebook
7651 * Retrieves the text of the tab label for the page containing
7654 * Return value: the text of the tab label, or %NULL if the
7655 * tab label widget is not a #GtkLabel. The string is owned
7656 * by the widget and must not be freed.
7659 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7662 GtkWidget *tab_label;
7664 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7665 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7667 tab_label = gtk_notebook_get_tab_label (notebook, child);
7669 if (GTK_IS_LABEL (tab_label))
7670 return gtk_label_get_text (GTK_LABEL (tab_label));
7676 * gtk_notebook_get_menu_label:
7677 * @notebook: a #GtkNotebook
7678 * @child: a widget contained in a page of @notebook
7680 * Retrieves the menu label widget of the page containing @child.
7682 * Return value: (transfer none): the menu label, or %NULL if the
7683 * notebook page does not have a menu label other than the
7684 * default (the tab label).
7687 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7692 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7693 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7695 list = CHECK_FIND_CHILD (notebook, child);
7699 if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7702 return GTK_NOTEBOOK_PAGE (list)->menu_label;
7706 * gtk_notebook_set_menu_label:
7707 * @notebook: a #GtkNotebook
7708 * @child: the child widget
7709 * @menu_label: (allow-none): the menu label, or %NULL for default
7711 * Changes the menu label for the page containing @child.
7714 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7716 GtkWidget *menu_label)
7718 GtkNotebookPrivate *priv;
7719 GtkNotebookPage *page;
7722 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7723 g_return_if_fail (GTK_IS_WIDGET (child));
7725 priv = notebook->priv;
7727 list = CHECK_FIND_CHILD (notebook, child);
7732 if (page->menu_label)
7735 gtk_container_remove (GTK_CONTAINER (priv->menu),
7736 gtk_widget_get_parent (page->menu_label));
7738 if (!page->default_menu)
7739 g_object_unref (page->menu_label);
7744 page->menu_label = menu_label;
7745 g_object_ref_sink (page->menu_label);
7746 page->default_menu = FALSE;
7749 page->default_menu = TRUE;
7752 gtk_notebook_menu_item_create (notebook, list);
7753 gtk_widget_child_notify (child, "menu-label");
7757 * gtk_notebook_set_menu_label_text:
7758 * @notebook: a #GtkNotebook
7759 * @child: the child widget
7760 * @menu_text: the label text
7762 * Creates a new label and sets it as the menu label of @child.
7765 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7767 const gchar *menu_text)
7769 GtkWidget *menu_label = NULL;
7771 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7775 menu_label = gtk_label_new (menu_text);
7776 gtk_widget_set_halign (menu_label, GTK_ALIGN_START);
7777 gtk_widget_set_valign (menu_label, GTK_ALIGN_CENTER);
7779 gtk_notebook_set_menu_label (notebook, child, menu_label);
7780 gtk_widget_child_notify (child, "menu-label");
7784 * gtk_notebook_get_menu_label_text:
7785 * @notebook: a #GtkNotebook
7786 * @child: the child widget of a page of the notebook.
7788 * Retrieves the text of the menu label for the page containing
7791 * Return value: the text of the tab label, or %NULL if the
7792 * widget does not have a menu label other than the default
7793 * menu label, or the menu label widget is not a #GtkLabel.
7794 * The string is owned by the widget and must not be freed.
7797 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7800 GtkWidget *menu_label;
7802 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7803 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7805 menu_label = gtk_notebook_get_menu_label (notebook, child);
7807 if (GTK_IS_LABEL (menu_label))
7808 return gtk_label_get_text (GTK_LABEL (menu_label));
7813 /* Helper function called when pages are reordered
7816 gtk_notebook_child_reordered (GtkNotebook *notebook,
7817 GtkNotebookPage *page)
7819 GtkNotebookPrivate *priv = notebook->priv;
7823 GtkWidget *menu_item;
7825 menu_item = gtk_widget_get_parent (page->menu_label);
7826 gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7827 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7828 gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7831 gtk_notebook_update_tab_states (notebook);
7832 gtk_notebook_update_labels (notebook);
7836 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7841 GtkNotebookPrivate *priv;
7842 GtkNotebookPage *page;
7845 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7846 g_return_if_fail (GTK_IS_WIDGET (child));
7848 priv = notebook->priv;
7850 list = CHECK_FIND_CHILD (notebook, child);
7855 expand = expand != FALSE;
7856 fill = fill != FALSE;
7857 if (page->expand == expand && page->fill == fill)
7860 gtk_widget_freeze_child_notify (child);
7861 page->expand = expand;
7862 gtk_widget_child_notify (child, "tab-expand");
7864 gtk_widget_child_notify (child, "tab-fill");
7865 gtk_widget_child_notify (child, "position");
7866 if (priv->show_tabs)
7867 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7868 gtk_widget_thaw_child_notify (child);
7872 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
7879 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7880 g_return_if_fail (GTK_IS_WIDGET (child));
7882 list = CHECK_FIND_CHILD (notebook, child);
7887 *expand = GTK_NOTEBOOK_PAGE (list)->expand;
7889 *fill = GTK_NOTEBOOK_PAGE (list)->fill;
7893 * gtk_notebook_reorder_child:
7894 * @notebook: a #GtkNotebook
7895 * @child: the child to move
7896 * @position: the new position, or -1 to move to the end
7898 * Reorders the page containing @child, so that it appears in position
7899 * @position. If @position is greater than or equal to the number of
7900 * children in the list or negative, @child will be moved to the end
7904 gtk_notebook_reorder_child (GtkNotebook *notebook,
7908 GtkNotebookPrivate *priv;
7909 GList *list, *new_list;
7910 GtkNotebookPage *page;
7914 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7915 g_return_if_fail (GTK_IS_WIDGET (child));
7917 priv = notebook->priv;
7919 list = CHECK_FIND_CHILD (notebook, child);
7923 max_pos = g_list_length (priv->children) - 1;
7924 if (position < 0 || position > max_pos)
7927 old_pos = g_list_position (priv->children, list);
7929 if (old_pos == position)
7933 priv->children = g_list_delete_link (priv->children, list);
7935 priv->children = g_list_insert (priv->children, page, position);
7936 new_list = g_list_nth (priv->children, position);
7938 /* Fix up GList references in GtkNotebook structure */
7939 if (priv->first_tab == list)
7940 priv->first_tab = new_list;
7941 if (priv->focus_tab == list)
7942 priv->focus_tab = new_list;
7944 gtk_widget_freeze_child_notify (child);
7946 /* Move around the menu items if necessary */
7947 gtk_notebook_child_reordered (notebook, page);
7948 gtk_widget_child_notify (child, "position");
7950 if (priv->show_tabs)
7951 gtk_notebook_pages_allocate (notebook);
7953 gtk_widget_thaw_child_notify (child);
7955 g_signal_emit (notebook,
7956 notebook_signals[PAGE_REORDERED],
7963 * gtk_notebook_set_group_name:
7964 * @notebook: a #GtkNotebook
7965 * @group_name: (allow-none): the name of the notebook group,
7966 * or %NULL to unset it
7968 * Sets a group name for @notebook.
7970 * Notebooks with the same name will be able to exchange tabs
7971 * via drag and drop. A notebook with a %NULL group name will
7972 * not be able to exchange tabs with any other notebook.
7977 gtk_notebook_set_group_name (GtkNotebook *notebook,
7978 const gchar *group_name)
7980 GtkNotebookPrivate *priv;
7983 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7985 priv = notebook->priv;
7987 group = g_quark_from_string (group_name);
7989 if (priv->group != group)
7991 priv->group = group;
7992 g_object_notify (G_OBJECT (notebook), "group-name");
7997 * gtk_notebook_get_group_name:
7998 * @notebook: a #GtkNotebook
8000 * Gets the current group name for @notebook.
8002 * Return Value: (transfer none): the group name,
8003 * or %NULL if none is set.
8008 gtk_notebook_get_group_name (GtkNotebook *notebook)
8010 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8012 return g_quark_to_string (notebook->priv->group);
8016 * gtk_notebook_get_tab_reorderable:
8017 * @notebook: a #GtkNotebook
8018 * @child: a child #GtkWidget
8020 * Gets whether the tab can be reordered via drag and drop or not.
8022 * Return Value: %TRUE if the tab is reorderable.
8027 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
8032 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8033 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8035 list = CHECK_FIND_CHILD (notebook, child);
8039 return GTK_NOTEBOOK_PAGE (list)->reorderable;
8043 * gtk_notebook_set_tab_reorderable:
8044 * @notebook: a #GtkNotebook
8045 * @child: a child #GtkWidget
8046 * @reorderable: whether the tab is reorderable or not
8048 * Sets whether the notebook tab can be reordered
8049 * via drag and drop or not.
8054 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
8056 gboolean reorderable)
8060 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8061 g_return_if_fail (GTK_IS_WIDGET (child));
8063 list = CHECK_FIND_CHILD (notebook, child);
8067 if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
8069 GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
8070 gtk_widget_child_notify (child, "reorderable");
8075 * gtk_notebook_get_tab_detachable:
8076 * @notebook: a #GtkNotebook
8077 * @child: a child #GtkWidget
8079 * Returns whether the tab contents can be detached from @notebook.
8081 * Return Value: %TRUE if the tab is detachable.
8086 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
8091 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8092 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8094 list = CHECK_FIND_CHILD (notebook, child);
8098 return GTK_NOTEBOOK_PAGE (list)->detachable;
8102 * gtk_notebook_set_tab_detachable:
8103 * @notebook: a #GtkNotebook
8104 * @child: a child #GtkWidget
8105 * @detachable: whether the tab is detachable or not
8107 * Sets whether the tab can be detached from @notebook to another
8108 * notebook or widget.
8110 * Note that 2 notebooks must share a common group identificator
8111 * (see gtk_notebook_set_group_name()) to allow automatic tabs
8112 * interchange between them.
8114 * If you want a widget to interact with a notebook through DnD
8115 * (i.e.: accept dragged tabs from it) it must be set as a drop
8116 * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
8117 * will fill the selection with a GtkWidget** pointing to the child
8118 * widget that corresponds to the dropped tab.
8121 * on_drop_zone_drag_data_received (GtkWidget *widget,
8122 * GdkDragContext *context,
8125 * GtkSelectionData *selection_data,
8128 * gpointer user_data)
8130 * GtkWidget *notebook;
8131 * GtkWidget **child;
8133 * notebook = gtk_drag_get_source_widget (context);
8134 * child = (void*) gtk_selection_data_get_data (selection_data);
8136 * process_widget (*child);
8137 * gtk_container_remove (GTK_CONTAINER (notebook), *child);
8141 * If you want a notebook to accept drags from other widgets,
8142 * you will have to set your own DnD code to do it.
8147 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
8149 gboolean detachable)
8153 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8154 g_return_if_fail (GTK_IS_WIDGET (child));
8156 list = CHECK_FIND_CHILD (notebook, child);
8160 if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
8162 GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
8163 gtk_widget_child_notify (child, "detachable");
8168 * gtk_notebook_get_action_widget:
8169 * @notebook: a #GtkNotebook
8170 * @pack_type: pack type of the action widget to receive
8172 * Gets one of the action widgets. See gtk_notebook_set_action_widget().
8174 * Returns: (transfer none): The action widget with the given @pack_type
8175 * or %NULL when this action widget has not been set
8180 gtk_notebook_get_action_widget (GtkNotebook *notebook,
8181 GtkPackType pack_type)
8183 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8185 return notebook->priv->action_widget[pack_type];
8189 * gtk_notebook_set_action_widget:
8190 * @notebook: a #GtkNotebook
8191 * @widget: a #GtkWidget
8192 * @pack_type: pack type of the action widget
8194 * Sets @widget as one of the action widgets. Depending on the pack type
8195 * the widget will be placed before or after the tabs. You can use
8196 * a #GtkBox if you need to pack more than one widget on the same side.
8198 * Note that action widgets are "internal" children of the notebook and thus
8199 * not included in the list returned from gtk_container_foreach().
8204 gtk_notebook_set_action_widget (GtkNotebook *notebook,
8206 GtkPackType pack_type)
8208 GtkNotebookPrivate *priv;
8210 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8211 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
8212 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
8214 priv = notebook->priv;
8216 if (priv->action_widget[pack_type])
8217 gtk_widget_unparent (priv->action_widget[pack_type]);
8219 priv->action_widget[pack_type] = widget;
8223 gtk_widget_set_child_visible (widget, priv->show_tabs);
8224 gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
8227 gtk_widget_queue_resize (GTK_WIDGET (notebook));