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/.
30 #include "gtknotebook.h"
35 #include <gdk/gdkkeysyms.h>
39 #include "gtkmenuitem.h"
42 #include "gtkmarshalers.h"
43 #include "gtkbindings.h"
44 #include "gtkprivate.h"
46 #include "gtkbuildable.h"
48 #define SCROLL_DELAY_FACTOR 5
49 #define SCROLL_THRESHOLD 12
50 #define DND_THRESHOLD_MULTIPLIER 4
51 #define FRAMES_PER_SECOND 45
52 #define MSECS_BETWEEN_UPDATES (1000 / FRAMES_PER_SECOND)
54 typedef struct _GtkNotebookPage GtkNotebookPage;
59 DRAG_OPERATION_REORDER,
61 } GtkNotebookDragOperation;
69 struct _GtkNotebookPrivate
71 GtkNotebookDragOperation operation;
72 GtkNotebookPage *cur_page;
73 GtkNotebookPage *detached_tab;
74 GtkTargetList *source_targets;
75 GtkWidget *action_widget[N_ACTION_WIDGETS];
76 GtkWidget *dnd_window;
79 GdkWindow *drag_window;
80 GdkWindow *event_window;
83 GList *first_tab; /* The first tab visible (for scrolling notebooks) */
99 guint switch_tab_timer;
108 guint child_has_focus : 1;
109 guint click_child : 3;
110 guint during_detach : 1;
111 guint during_reorder : 1;
112 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
113 guint has_scrolled : 1;
114 guint have_visible_child : 1;
115 guint homogeneous : 1;
117 guint need_timer : 1;
118 guint show_border : 1;
120 guint scrollable : 1;
123 guint has_before_previous : 1;
124 guint has_before_next : 1;
125 guint has_after_previous : 1;
126 guint has_after_next : 1;
162 } GtkNotebookPointerPosition;
164 #define ARROW_IS_LEFT(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
165 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
180 CHILD_PROP_TAB_LABEL,
181 CHILD_PROP_MENU_LABEL,
183 CHILD_PROP_TAB_EXPAND,
186 CHILD_PROP_REORDERABLE,
187 CHILD_PROP_DETACHABLE
190 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
192 /* some useful defines for calculating coords */
193 #define PAGE_LEFT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x)
194 #define PAGE_RIGHT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
195 #define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
196 #define PAGE_TOP_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y)
197 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
198 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
199 #define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent (((GtkNotebookPage *) (_page_))->tab_label) == ((GtkWidget *) (_notebook_)))
201 struct _GtkNotebookPage
204 GtkWidget *tab_label;
205 GtkWidget *menu_label;
206 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
208 guint default_menu : 1; /* If true, we create the menu label ourself */
209 guint default_tab : 1; /* If true, we create the tab label ourself */
213 guint reorderable : 1;
214 guint detachable : 1;
216 /* if true, the tab label was visible on last allocation; we track this so
217 * that we know to redraw the tab area if a tab label was hidden then shown
218 * without changing position */
219 guint tab_allocated_visible : 1;
221 GtkRequisition requisition;
222 GtkAllocation allocation;
224 gulong mnemonic_activate_signal;
225 gulong notify_visible_handler;
228 static const GtkTargetEntry notebook_targets [] = {
229 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
232 #ifdef G_DISABLE_CHECKS
233 #define CHECK_FIND_CHILD(notebook, child) \
234 gtk_notebook_find_child (notebook, child, G_STRLOC)
236 #define CHECK_FIND_CHILD(notebook, child) \
237 gtk_notebook_find_child (notebook, child, NULL)
240 /*** GtkNotebook Methods ***/
241 static gboolean gtk_notebook_select_page (GtkNotebook *notebook,
242 gboolean move_focus);
243 static gboolean gtk_notebook_focus_tab (GtkNotebook *notebook,
244 GtkNotebookTab type);
245 static gboolean gtk_notebook_change_current_page (GtkNotebook *notebook,
247 static void gtk_notebook_move_focus_out (GtkNotebook *notebook,
248 GtkDirectionType direction_type);
249 static gboolean gtk_notebook_reorder_tab (GtkNotebook *notebook,
250 GtkDirectionType direction_type,
251 gboolean move_to_last);
252 static void gtk_notebook_remove_tab_label (GtkNotebook *notebook,
253 GtkNotebookPage *page);
254 static void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
258 GtkPackType pack_type);
259 static void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
263 GtkPackType *pack_type);
265 /*** GObject Methods ***/
266 static void gtk_notebook_set_property (GObject *object,
270 static void gtk_notebook_get_property (GObject *object,
275 /*** GtkWidget Methods ***/
276 static void gtk_notebook_destroy (GtkWidget *widget);
277 static void gtk_notebook_map (GtkWidget *widget);
278 static void gtk_notebook_unmap (GtkWidget *widget);
279 static void gtk_notebook_realize (GtkWidget *widget);
280 static void gtk_notebook_unrealize (GtkWidget *widget);
281 static void gtk_notebook_size_request (GtkWidget *widget,
282 GtkRequisition *requisition);
283 static void gtk_notebook_size_allocate (GtkWidget *widget,
284 GtkAllocation *allocation);
285 static gint gtk_notebook_draw (GtkWidget *widget,
287 static gint gtk_notebook_button_press (GtkWidget *widget,
288 GdkEventButton *event);
289 static gint gtk_notebook_button_release (GtkWidget *widget,
290 GdkEventButton *event);
291 static gboolean gtk_notebook_popup_menu (GtkWidget *widget);
292 static gint gtk_notebook_leave_notify (GtkWidget *widget,
293 GdkEventCrossing *event);
294 static gint gtk_notebook_motion_notify (GtkWidget *widget,
295 GdkEventMotion *event);
296 static gint gtk_notebook_focus_in (GtkWidget *widget,
297 GdkEventFocus *event);
298 static gint gtk_notebook_focus_out (GtkWidget *widget,
299 GdkEventFocus *event);
300 static void gtk_notebook_grab_notify (GtkWidget *widget,
301 gboolean was_grabbed);
302 static void gtk_notebook_state_changed (GtkWidget *widget,
303 GtkStateType previous_state);
304 static gint gtk_notebook_focus (GtkWidget *widget,
305 GtkDirectionType direction);
306 static void gtk_notebook_style_set (GtkWidget *widget,
309 /*** Drag and drop Methods ***/
310 static void gtk_notebook_drag_begin (GtkWidget *widget,
311 GdkDragContext *context);
312 static void gtk_notebook_drag_end (GtkWidget *widget,
313 GdkDragContext *context);
314 static gboolean gtk_notebook_drag_failed (GtkWidget *widget,
315 GdkDragContext *context,
316 GtkDragResult result,
318 static gboolean gtk_notebook_drag_motion (GtkWidget *widget,
319 GdkDragContext *context,
323 static void gtk_notebook_drag_leave (GtkWidget *widget,
324 GdkDragContext *context,
326 static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
327 GdkDragContext *context,
331 static void gtk_notebook_drag_data_get (GtkWidget *widget,
332 GdkDragContext *context,
333 GtkSelectionData *data,
336 static void gtk_notebook_drag_data_received (GtkWidget *widget,
337 GdkDragContext *context,
340 GtkSelectionData *data,
344 /*** GtkContainer Methods ***/
345 static void gtk_notebook_set_child_property (GtkContainer *container,
350 static void gtk_notebook_get_child_property (GtkContainer *container,
355 static void gtk_notebook_add (GtkContainer *container,
357 static void gtk_notebook_remove (GtkContainer *container,
359 static void gtk_notebook_set_focus_child (GtkContainer *container,
361 static GType gtk_notebook_child_type (GtkContainer *container);
362 static void gtk_notebook_forall (GtkContainer *container,
363 gboolean include_internals,
364 GtkCallback callback,
365 gpointer callback_data);
367 /*** GtkNotebook Methods ***/
368 static gint gtk_notebook_real_insert_page (GtkNotebook *notebook,
370 GtkWidget *tab_label,
371 GtkWidget *menu_label,
374 static GtkNotebook *gtk_notebook_create_window (GtkNotebook *notebook,
379 /*** GtkNotebook Private Functions ***/
380 static void gtk_notebook_redraw_tabs (GtkNotebook *notebook);
381 static void gtk_notebook_redraw_arrows (GtkNotebook *notebook);
382 static void gtk_notebook_real_remove (GtkNotebook *notebook,
384 static void gtk_notebook_update_labels (GtkNotebook *notebook);
385 static gint gtk_notebook_timer (GtkNotebook *notebook);
386 static void gtk_notebook_set_scroll_timer (GtkNotebook *notebook);
387 static gint gtk_notebook_page_compare (gconstpointer a,
389 static GList* gtk_notebook_find_child (GtkNotebook *notebook,
391 const gchar *function);
392 static gint gtk_notebook_real_page_position (GtkNotebook *notebook,
394 static GList * gtk_notebook_search_page (GtkNotebook *notebook,
397 gboolean find_visible);
398 static void gtk_notebook_child_reordered (GtkNotebook *notebook,
399 GtkNotebookPage *page);
401 /*** GtkNotebook Drawing Functions ***/
402 static void gtk_notebook_paint (GtkWidget *widget,
404 static void gtk_notebook_draw_tab (GtkNotebook *notebook,
405 GtkNotebookPage *page,
407 static void gtk_notebook_draw_arrow (GtkNotebook *notebook,
409 GtkNotebookArrow arrow);
411 /*** GtkNotebook Size Allocate Functions ***/
412 static void gtk_notebook_pages_allocate (GtkNotebook *notebook);
413 static gboolean gtk_notebook_page_allocate (GtkNotebook *notebook,
414 GtkNotebookPage *page);
415 static void gtk_notebook_calc_tabs (GtkNotebook *notebook,
421 /*** GtkNotebook Page Switch Methods ***/
422 static void gtk_notebook_real_switch_page (GtkNotebook *notebook,
426 /*** GtkNotebook Page Switch Functions ***/
427 static void gtk_notebook_switch_page (GtkNotebook *notebook,
428 GtkNotebookPage *page);
429 static gint gtk_notebook_page_select (GtkNotebook *notebook,
430 gboolean move_focus);
431 static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
433 static void gtk_notebook_menu_switch_page (GtkWidget *widget,
434 GtkNotebookPage *page);
436 /*** GtkNotebook Menu Functions ***/
437 static void gtk_notebook_menu_item_create (GtkNotebook *notebook,
439 static void gtk_notebook_menu_label_unparent (GtkWidget *widget,
441 static void gtk_notebook_menu_detacher (GtkWidget *widget,
444 /*** GtkNotebook Private Setters ***/
445 static void gtk_notebook_update_tab_states (GtkNotebook *notebook);
446 static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
450 static gboolean focus_tabs_in (GtkNotebook *notebook);
451 static gboolean focus_child_in (GtkNotebook *notebook,
452 GtkDirectionType direction);
454 static void stop_scrolling (GtkNotebook *notebook);
455 static void do_detach_tab (GtkNotebook *from,
462 static void gtk_notebook_buildable_init (GtkBuildableIface *iface);
463 static void gtk_notebook_buildable_add_child (GtkBuildable *buildable,
468 static guint notebook_signals[LAST_SIGNAL] = { 0 };
470 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
471 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
472 gtk_notebook_buildable_init))
475 add_tab_bindings (GtkBindingSet *binding_set,
476 GdkModifierType modifiers,
477 GtkDirectionType direction)
479 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
481 GTK_TYPE_DIRECTION_TYPE, direction);
482 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
484 GTK_TYPE_DIRECTION_TYPE, direction);
488 add_arrow_bindings (GtkBindingSet *binding_set,
490 GtkDirectionType direction)
492 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
494 gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
496 GTK_TYPE_DIRECTION_TYPE, direction);
497 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
499 GTK_TYPE_DIRECTION_TYPE, direction);
503 add_reorder_bindings (GtkBindingSet *binding_set,
505 GtkDirectionType direction,
506 gboolean move_to_last)
508 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
510 gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
512 GTK_TYPE_DIRECTION_TYPE, direction,
513 G_TYPE_BOOLEAN, move_to_last);
514 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
516 GTK_TYPE_DIRECTION_TYPE, direction,
517 G_TYPE_BOOLEAN, move_to_last);
521 gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
523 const GValue *handler_return,
526 gboolean continue_emission;
529 object = g_value_get_object (handler_return);
530 g_value_set_object (return_accu, object);
531 continue_emission = !object;
533 return continue_emission;
537 gtk_notebook_class_init (GtkNotebookClass *class)
539 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
540 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
541 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
542 GtkBindingSet *binding_set;
544 gobject_class->set_property = gtk_notebook_set_property;
545 gobject_class->get_property = gtk_notebook_get_property;
547 widget_class->destroy = gtk_notebook_destroy;
548 widget_class->map = gtk_notebook_map;
549 widget_class->unmap = gtk_notebook_unmap;
550 widget_class->realize = gtk_notebook_realize;
551 widget_class->unrealize = gtk_notebook_unrealize;
552 widget_class->size_request = gtk_notebook_size_request;
553 widget_class->size_allocate = gtk_notebook_size_allocate;
554 widget_class->draw = gtk_notebook_draw;
555 widget_class->button_press_event = gtk_notebook_button_press;
556 widget_class->button_release_event = gtk_notebook_button_release;
557 widget_class->popup_menu = gtk_notebook_popup_menu;
558 widget_class->leave_notify_event = gtk_notebook_leave_notify;
559 widget_class->motion_notify_event = gtk_notebook_motion_notify;
560 widget_class->grab_notify = gtk_notebook_grab_notify;
561 widget_class->state_changed = gtk_notebook_state_changed;
562 widget_class->focus_in_event = gtk_notebook_focus_in;
563 widget_class->focus_out_event = gtk_notebook_focus_out;
564 widget_class->focus = gtk_notebook_focus;
565 widget_class->style_set = gtk_notebook_style_set;
566 widget_class->drag_begin = gtk_notebook_drag_begin;
567 widget_class->drag_end = gtk_notebook_drag_end;
568 widget_class->drag_motion = gtk_notebook_drag_motion;
569 widget_class->drag_leave = gtk_notebook_drag_leave;
570 widget_class->drag_drop = gtk_notebook_drag_drop;
571 widget_class->drag_data_get = gtk_notebook_drag_data_get;
572 widget_class->drag_data_received = gtk_notebook_drag_data_received;
574 container_class->add = gtk_notebook_add;
575 container_class->remove = gtk_notebook_remove;
576 container_class->forall = gtk_notebook_forall;
577 container_class->set_focus_child = gtk_notebook_set_focus_child;
578 container_class->get_child_property = gtk_notebook_get_child_property;
579 container_class->set_child_property = gtk_notebook_set_child_property;
580 container_class->child_type = gtk_notebook_child_type;
582 class->switch_page = gtk_notebook_real_switch_page;
583 class->insert_page = gtk_notebook_real_insert_page;
585 class->focus_tab = gtk_notebook_focus_tab;
586 class->select_page = gtk_notebook_select_page;
587 class->change_current_page = gtk_notebook_change_current_page;
588 class->move_focus_out = gtk_notebook_move_focus_out;
589 class->reorder_tab = gtk_notebook_reorder_tab;
590 class->create_window = gtk_notebook_create_window;
592 g_object_class_install_property (gobject_class,
594 g_param_spec_int ("page",
596 P_("The index of the current page"),
600 GTK_PARAM_READWRITE));
601 g_object_class_install_property (gobject_class,
603 g_param_spec_enum ("tab-pos",
605 P_("Which side of the notebook holds the tabs"),
606 GTK_TYPE_POSITION_TYPE,
608 GTK_PARAM_READWRITE));
609 g_object_class_install_property (gobject_class,
611 g_param_spec_boolean ("show-tabs",
613 P_("Whether tabs should be shown"),
615 GTK_PARAM_READWRITE));
616 g_object_class_install_property (gobject_class,
618 g_param_spec_boolean ("show-border",
620 P_("Whether the border should be shown"),
622 GTK_PARAM_READWRITE));
623 g_object_class_install_property (gobject_class,
625 g_param_spec_boolean ("scrollable",
627 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
629 GTK_PARAM_READWRITE));
630 g_object_class_install_property (gobject_class,
632 g_param_spec_boolean ("enable-popup",
634 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
636 GTK_PARAM_READWRITE));
639 * GtkNotebook:group-name:
641 * Group name for tab drag and drop.
645 g_object_class_install_property (gobject_class,
647 g_param_spec_string ("group-name",
649 P_("Group name for tab drag and drop"),
651 GTK_PARAM_READWRITE));
653 gtk_container_class_install_child_property (container_class,
654 CHILD_PROP_TAB_LABEL,
655 g_param_spec_string ("tab-label",
657 P_("The string displayed on the child's tab label"),
659 GTK_PARAM_READWRITE));
660 gtk_container_class_install_child_property (container_class,
661 CHILD_PROP_MENU_LABEL,
662 g_param_spec_string ("menu-label",
664 P_("The string displayed in the child's menu entry"),
666 GTK_PARAM_READWRITE));
667 gtk_container_class_install_child_property (container_class,
669 g_param_spec_int ("position",
671 P_("The index of the child in the parent"),
673 GTK_PARAM_READWRITE));
674 gtk_container_class_install_child_property (container_class,
675 CHILD_PROP_TAB_EXPAND,
676 g_param_spec_boolean ("tab-expand",
678 P_("Whether to expand the child's tab"),
680 GTK_PARAM_READWRITE));
681 gtk_container_class_install_child_property (container_class,
683 g_param_spec_boolean ("tab-fill",
685 P_("Whether the child's tab should fill the allocated area"),
687 GTK_PARAM_READWRITE));
688 gtk_container_class_install_child_property (container_class,
690 g_param_spec_enum ("tab-pack",
692 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
693 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
694 GTK_PARAM_READWRITE));
695 gtk_container_class_install_child_property (container_class,
696 CHILD_PROP_REORDERABLE,
697 g_param_spec_boolean ("reorderable",
698 P_("Tab reorderable"),
699 P_("Whether the tab is reorderable by user action"),
701 GTK_PARAM_READWRITE));
702 gtk_container_class_install_child_property (container_class,
703 CHILD_PROP_DETACHABLE,
704 g_param_spec_boolean ("detachable",
705 P_("Tab detachable"),
706 P_("Whether the tab is detachable"),
708 GTK_PARAM_READWRITE));
711 * GtkNotebook:has-secondary-backward-stepper:
713 * The "has-secondary-backward-stepper" property determines whether
714 * a second backward arrow button is displayed on the opposite end
719 gtk_widget_class_install_style_property (widget_class,
720 g_param_spec_boolean ("has-secondary-backward-stepper",
721 P_("Secondary backward stepper"),
722 P_("Display a second backward arrow button on the opposite end of the tab area"),
724 GTK_PARAM_READABLE));
727 * GtkNotebook:has-secondary-forward-stepper:
729 * The "has-secondary-forward-stepper" property determines whether
730 * a second forward arrow button is displayed on the opposite end
735 gtk_widget_class_install_style_property (widget_class,
736 g_param_spec_boolean ("has-secondary-forward-stepper",
737 P_("Secondary forward stepper"),
738 P_("Display a second forward arrow button on the opposite end of the tab area"),
740 GTK_PARAM_READABLE));
743 * GtkNotebook:has-backward-stepper:
745 * The "has-backward-stepper" property determines whether
746 * the standard backward arrow button is displayed.
750 gtk_widget_class_install_style_property (widget_class,
751 g_param_spec_boolean ("has-backward-stepper",
752 P_("Backward stepper"),
753 P_("Display the standard backward arrow button"),
755 GTK_PARAM_READABLE));
758 * GtkNotebook:has-forward-stepper:
760 * The "has-forward-stepper" property determines whether
761 * the standard forward arrow button is displayed.
765 gtk_widget_class_install_style_property (widget_class,
766 g_param_spec_boolean ("has-forward-stepper",
767 P_("Forward stepper"),
768 P_("Display the standard forward arrow button"),
770 GTK_PARAM_READABLE));
773 * GtkNotebook:tab-overlap:
775 * The "tab-overlap" property defines size of tab overlap
780 gtk_widget_class_install_style_property (widget_class,
781 g_param_spec_int ("tab-overlap",
783 P_("Size of tab overlap area"),
787 GTK_PARAM_READABLE));
790 * GtkNotebook:tab-curvature:
792 * The "tab-curvature" property defines size of tab curvature.
796 gtk_widget_class_install_style_property (widget_class,
797 g_param_spec_int ("tab-curvature",
799 P_("Size of tab curvature"),
803 GTK_PARAM_READABLE));
806 * GtkNotebook:arrow-spacing:
808 * The "arrow-spacing" property defines the spacing between the scroll
809 * arrows and the tabs.
813 gtk_widget_class_install_style_property (widget_class,
814 g_param_spec_int ("arrow-spacing",
816 P_("Scroll arrow spacing"),
820 GTK_PARAM_READABLE));
822 notebook_signals[SWITCH_PAGE] =
823 g_signal_new (I_("switch-page"),
824 G_TYPE_FROM_CLASS (gobject_class),
826 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
828 _gtk_marshal_VOID__OBJECT_UINT,
832 notebook_signals[FOCUS_TAB] =
833 g_signal_new (I_("focus-tab"),
834 G_TYPE_FROM_CLASS (gobject_class),
835 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
836 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
838 _gtk_marshal_BOOLEAN__ENUM,
840 GTK_TYPE_NOTEBOOK_TAB);
841 notebook_signals[SELECT_PAGE] =
842 g_signal_new (I_("select-page"),
843 G_TYPE_FROM_CLASS (gobject_class),
844 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
845 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
847 _gtk_marshal_BOOLEAN__BOOLEAN,
850 notebook_signals[CHANGE_CURRENT_PAGE] =
851 g_signal_new (I_("change-current-page"),
852 G_TYPE_FROM_CLASS (gobject_class),
853 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
854 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
856 _gtk_marshal_BOOLEAN__INT,
859 notebook_signals[MOVE_FOCUS_OUT] =
860 g_signal_new (I_("move-focus-out"),
861 G_TYPE_FROM_CLASS (gobject_class),
862 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
863 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
865 _gtk_marshal_VOID__ENUM,
867 GTK_TYPE_DIRECTION_TYPE);
868 notebook_signals[REORDER_TAB] =
869 g_signal_new (I_("reorder-tab"),
870 G_TYPE_FROM_CLASS (gobject_class),
871 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
872 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
874 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
876 GTK_TYPE_DIRECTION_TYPE,
879 * GtkNotebook::page-reordered:
880 * @notebook: the #GtkNotebook
881 * @child: the child #GtkWidget affected
882 * @page_num: the new page number for @child
884 * the ::page-reordered signal is emitted in the notebook
885 * right after a page has been reordered.
889 notebook_signals[PAGE_REORDERED] =
890 g_signal_new (I_("page-reordered"),
891 G_TYPE_FROM_CLASS (gobject_class),
894 _gtk_marshal_VOID__OBJECT_UINT,
899 * GtkNotebook::page-removed:
900 * @notebook: the #GtkNotebook
901 * @child: the child #GtkWidget affected
902 * @page_num: the @child page number
904 * the ::page-removed signal is emitted in the notebook
905 * right after a page is removed from the notebook.
909 notebook_signals[PAGE_REMOVED] =
910 g_signal_new (I_("page-removed"),
911 G_TYPE_FROM_CLASS (gobject_class),
914 _gtk_marshal_VOID__OBJECT_UINT,
919 * GtkNotebook::page-added:
920 * @notebook: the #GtkNotebook
921 * @child: the child #GtkWidget affected
922 * @page_num: the new page number for @child
924 * the ::page-added signal is emitted in the notebook
925 * right after a page is added to the notebook.
929 notebook_signals[PAGE_ADDED] =
930 g_signal_new (I_("page-added"),
931 G_TYPE_FROM_CLASS (gobject_class),
934 _gtk_marshal_VOID__OBJECT_UINT,
940 * GtkNotebook::create-window:
941 * @notebook: the #GtkNotebook emitting the signal
942 * @page: the tab of @notebook that is being detached
943 * @x: the X coordinate where the drop happens
944 * @y: the Y coordinate where the drop happens
946 * The ::create-window signal is emitted when a detachable
947 * tab is dropped on the root window.
949 * A handler for this signal can create a window containing
950 * a notebook where the tab will be attached. It is also
951 * responsible for moving/resizing the window and adding the
952 * necessary properties to the notebook (e.g. the
953 * #GtkNotebook:group ).
955 * Returns: a #GtkNotebook that @page should be added to, or %NULL.
959 notebook_signals[CREATE_WINDOW] =
960 g_signal_new (I_("create-window"),
961 G_TYPE_FROM_CLASS (gobject_class),
963 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
964 gtk_object_handled_accumulator, NULL,
965 _gtk_marshal_OBJECT__OBJECT_INT_INT,
966 GTK_TYPE_NOTEBOOK, 3,
967 GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
969 binding_set = gtk_binding_set_by_class (class);
970 gtk_binding_entry_add_signal (binding_set,
973 G_TYPE_BOOLEAN, FALSE);
974 gtk_binding_entry_add_signal (binding_set,
977 G_TYPE_BOOLEAN, FALSE);
979 gtk_binding_entry_add_signal (binding_set,
982 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
983 gtk_binding_entry_add_signal (binding_set,
986 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
987 gtk_binding_entry_add_signal (binding_set,
990 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
991 gtk_binding_entry_add_signal (binding_set,
994 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
996 gtk_binding_entry_add_signal (binding_set,
997 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
998 "change-current-page", 1,
1000 gtk_binding_entry_add_signal (binding_set,
1001 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1002 "change-current-page", 1,
1005 gtk_binding_entry_add_signal (binding_set,
1006 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1007 "change-current-page", 1,
1009 gtk_binding_entry_add_signal (binding_set,
1010 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1011 "change-current-page", 1,
1014 add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1015 add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1016 add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1017 add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1019 add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1020 add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1021 add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1022 add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1023 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1024 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1025 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1026 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1028 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1029 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1031 g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1035 gtk_notebook_init (GtkNotebook *notebook)
1037 GtkNotebookPrivate *priv;
1039 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1040 gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1042 notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1044 GtkNotebookPrivate);
1045 priv = notebook->priv;
1047 priv->cur_page = NULL;
1048 priv->children = NULL;
1049 priv->first_tab = NULL;
1050 priv->focus_tab = NULL;
1051 priv->event_window = NULL;
1054 priv->tab_hborder = 2;
1055 priv->tab_vborder = 2;
1057 priv->show_tabs = TRUE;
1058 priv->show_border = TRUE;
1059 priv->tab_pos = GTK_POS_TOP;
1060 priv->scrollable = FALSE;
1062 priv->click_child = 0;
1064 priv->need_timer = 0;
1065 priv->child_has_focus = FALSE;
1066 priv->have_visible_child = FALSE;
1067 priv->focus_out = FALSE;
1069 priv->has_before_previous = 1;
1070 priv->has_before_next = 0;
1071 priv->has_after_previous = 0;
1072 priv->has_after_next = 1;
1075 priv->pressed_button = -1;
1076 priv->dnd_timer = 0;
1077 priv->switch_tab_timer = 0;
1078 priv->source_targets = gtk_target_list_new (notebook_targets,
1079 G_N_ELEMENTS (notebook_targets));
1080 priv->operation = DRAG_OPERATION_NONE;
1081 priv->detached_tab = NULL;
1082 priv->during_detach = FALSE;
1083 priv->has_scrolled = FALSE;
1085 gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1086 notebook_targets, G_N_ELEMENTS (notebook_targets),
1089 g_signal_connect (G_OBJECT (notebook), "drag-failed",
1090 G_CALLBACK (gtk_notebook_drag_failed), NULL);
1092 gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1096 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1098 iface->add_child = gtk_notebook_buildable_add_child;
1102 gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1103 GtkBuilder *builder,
1107 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1109 if (type && strcmp (type, "tab") == 0)
1113 page = gtk_notebook_get_nth_page (notebook, -1);
1114 /* To set the tab label widget, we must have already a child
1115 * inside the tab container. */
1116 g_assert (page != NULL);
1117 gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1119 else if (type && strcmp (type, "action-start") == 0)
1121 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1123 else if (type && strcmp (type, "action-end") == 0)
1125 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1128 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1130 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1134 gtk_notebook_select_page (GtkNotebook *notebook,
1135 gboolean move_focus)
1137 GtkNotebookPrivate *priv = notebook->priv;
1139 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1141 gtk_notebook_page_select (notebook, move_focus);
1149 gtk_notebook_focus_tab (GtkNotebook *notebook,
1150 GtkNotebookTab type)
1152 GtkNotebookPrivate *priv = notebook->priv;
1155 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1159 case GTK_NOTEBOOK_TAB_FIRST:
1160 list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1162 gtk_notebook_switch_focus_tab (notebook, list);
1164 case GTK_NOTEBOOK_TAB_LAST:
1165 list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1167 gtk_notebook_switch_focus_tab (notebook, list);
1178 gtk_notebook_change_current_page (GtkNotebook *notebook,
1181 GtkNotebookPrivate *priv = notebook->priv;
1182 GList *current = NULL;
1184 if (!priv->show_tabs)
1188 current = g_list_find (priv->children, priv->cur_page);
1192 current = gtk_notebook_search_page (notebook, current,
1193 offset < 0 ? STEP_PREV : STEP_NEXT,
1198 gboolean wrap_around;
1200 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1201 "gtk-keynav-wrap-around", &wrap_around,
1205 current = gtk_notebook_search_page (notebook, NULL,
1206 offset < 0 ? STEP_PREV : STEP_NEXT,
1212 offset += offset < 0 ? 1 : -1;
1216 gtk_notebook_switch_page (notebook, current->data);
1218 gtk_widget_error_bell (GTK_WIDGET (notebook));
1223 static GtkDirectionType
1224 get_effective_direction (GtkNotebook *notebook,
1225 GtkDirectionType direction)
1227 GtkNotebookPrivate *priv = notebook->priv;
1229 /* Remap the directions into the effective direction it would be for a
1230 * GTK_POS_TOP notebook
1233 #define D(rest) GTK_DIR_##rest
1235 static const GtkDirectionType translate_direction[2][4][6] = {
1236 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1237 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1238 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1239 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1240 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1241 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1242 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1243 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1248 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1250 return translate_direction[text_dir][priv->tab_pos][direction];
1254 get_effective_tab_pos (GtkNotebook *notebook)
1256 GtkNotebookPrivate *priv = notebook->priv;
1258 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1260 switch (priv->tab_pos)
1263 return GTK_POS_RIGHT;
1265 return GTK_POS_LEFT;
1270 return priv->tab_pos;
1274 get_tab_gap_pos (GtkNotebook *notebook)
1276 gint tab_pos = get_effective_tab_pos (notebook);
1277 gint gap_side = GTK_POS_BOTTOM;
1282 gap_side = GTK_POS_BOTTOM;
1284 case GTK_POS_BOTTOM:
1285 gap_side = GTK_POS_TOP;
1288 gap_side = GTK_POS_RIGHT;
1291 gap_side = GTK_POS_LEFT;
1299 gtk_notebook_move_focus_out (GtkNotebook *notebook,
1300 GtkDirectionType direction_type)
1302 GtkNotebookPrivate *priv = notebook->priv;
1303 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1304 GtkWidget *toplevel;
1306 if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1307 if (focus_tabs_in (notebook))
1309 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1310 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1313 /* At this point, we know we should be focusing out of the notebook entirely. We
1314 * do this by setting a flag, then propagating the focus motion to the notebook.
1316 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1317 if (!gtk_widget_is_toplevel (toplevel))
1320 g_object_ref (notebook);
1322 priv->focus_out = TRUE;
1323 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1324 priv->focus_out = FALSE;
1326 g_object_unref (notebook);
1330 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1332 GtkNotebookPrivate *priv = notebook->priv;
1335 if (position == tab)
1336 return g_list_position (priv->children, tab);
1338 /* check that we aren't inserting the tab in the
1339 * same relative position, taking packing into account */
1340 elem = (position) ? position->prev : g_list_last (priv->children);
1342 while (elem && elem != tab && GTK_NOTEBOOK_PAGE (elem)->pack != GTK_NOTEBOOK_PAGE (tab)->pack)
1346 return g_list_position (priv->children, tab);
1348 /* now actually reorder the tab */
1349 if (priv->first_tab == tab)
1350 priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1353 priv->children = g_list_remove_link (priv->children, tab);
1356 elem = g_list_last (priv->children);
1359 elem = position->prev;
1360 position->prev = tab;
1366 priv->children = tab;
1369 tab->next = position;
1371 return g_list_position (priv->children, tab);
1375 gtk_notebook_reorder_tab (GtkNotebook *notebook,
1376 GtkDirectionType direction_type,
1377 gboolean move_to_last)
1379 GtkNotebookPrivate *priv = notebook->priv;
1380 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1381 GtkNotebookPage *page;
1382 GList *last, *child;
1385 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1388 if (!priv->cur_page ||
1389 !priv->cur_page->reorderable)
1392 if (effective_direction != GTK_DIR_LEFT &&
1393 effective_direction != GTK_DIR_RIGHT)
1398 child = priv->focus_tab;
1403 child = gtk_notebook_search_page (notebook, last,
1404 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1407 while (child && GTK_NOTEBOOK_PAGE (last)->pack == GTK_NOTEBOOK_PAGE (child)->pack);
1412 child = gtk_notebook_search_page (notebook, priv->focus_tab,
1413 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1416 if (!child || child->data == priv->cur_page)
1421 if (page->pack == priv->cur_page->pack)
1423 if (effective_direction == GTK_DIR_RIGHT)
1424 page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child->next : child, priv->focus_tab);
1426 page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child : child->next, priv->focus_tab);
1428 gtk_notebook_pages_allocate (notebook);
1430 g_signal_emit (notebook,
1431 notebook_signals[PAGE_REORDERED],
1433 ((GtkNotebookPage *) priv->focus_tab->data)->child,
1445 * Creates a new #GtkNotebook widget with no pages.
1447 * Return value: the newly created #GtkNotebook
1450 gtk_notebook_new (void)
1452 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1455 /* Private GObject Methods :
1457 * gtk_notebook_set_property
1458 * gtk_notebook_get_property
1461 gtk_notebook_set_property (GObject *object,
1463 const GValue *value,
1466 GtkNotebook *notebook;
1468 notebook = GTK_NOTEBOOK (object);
1472 case PROP_SHOW_TABS:
1473 gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1475 case PROP_SHOW_BORDER:
1476 gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1478 case PROP_SCROLLABLE:
1479 gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1481 case PROP_ENABLE_POPUP:
1482 if (g_value_get_boolean (value))
1483 gtk_notebook_popup_enable (notebook);
1485 gtk_notebook_popup_disable (notebook);
1488 gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1491 gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1493 case PROP_GROUP_NAME:
1494 gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1497 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1503 gtk_notebook_get_property (GObject *object,
1508 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1509 GtkNotebookPrivate *priv = notebook->priv;
1513 case PROP_SHOW_TABS:
1514 g_value_set_boolean (value, priv->show_tabs);
1516 case PROP_SHOW_BORDER:
1517 g_value_set_boolean (value, priv->show_border);
1519 case PROP_SCROLLABLE:
1520 g_value_set_boolean (value, priv->scrollable);
1522 case PROP_ENABLE_POPUP:
1523 g_value_set_boolean (value, priv->menu != NULL);
1526 g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1529 g_value_set_enum (value, priv->tab_pos);
1531 case PROP_GROUP_NAME:
1532 g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1535 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1540 /* Private GtkWidget Methods :
1542 * gtk_notebook_destroy
1544 * gtk_notebook_unmap
1545 * gtk_notebook_realize
1546 * gtk_notebook_size_request
1547 * gtk_notebook_size_allocate
1549 * gtk_notebook_scroll
1550 * gtk_notebook_button_press
1551 * gtk_notebook_button_release
1552 * gtk_notebook_popup_menu
1553 * gtk_notebook_leave_notify
1554 * gtk_notebook_motion_notify
1555 * gtk_notebook_focus_in
1556 * gtk_notebook_focus_out
1557 * gtk_notebook_style_set
1558 * gtk_notebook_drag_begin
1559 * gtk_notebook_drag_end
1560 * gtk_notebook_drag_failed
1561 * gtk_notebook_drag_motion
1562 * gtk_notebook_drag_drop
1563 * gtk_notebook_drag_data_get
1564 * gtk_notebook_drag_data_received
1567 gtk_notebook_destroy (GtkWidget *widget)
1569 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1570 GtkNotebookPrivate *priv = notebook->priv;
1573 gtk_notebook_popup_disable (notebook);
1575 if (priv->source_targets)
1577 gtk_target_list_unref (priv->source_targets);
1578 priv->source_targets = NULL;
1581 if (priv->switch_tab_timer)
1583 g_source_remove (priv->switch_tab_timer);
1584 priv->switch_tab_timer = 0;
1587 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->destroy (widget);
1591 gtk_notebook_get_event_window_position (GtkNotebook *notebook,
1592 GdkRectangle *rectangle)
1594 GtkNotebookPrivate *priv = notebook->priv;
1595 GtkAllocation allocation, action_allocation;
1596 GtkWidget *widget = GTK_WIDGET (notebook);
1597 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1598 GtkNotebookPage *visible_page = NULL;
1600 gint tab_pos = get_effective_tab_pos (notebook);
1604 for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1606 GtkNotebookPage *page = tmp_list->data;
1607 if (gtk_widget_get_visible (page->child))
1609 visible_page = page;
1614 if (priv->show_tabs && visible_page)
1618 gtk_widget_get_allocation (widget, &allocation);
1620 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1621 rectangle->x = allocation.x + border_width;
1622 rectangle->y = allocation.y + border_width;
1627 case GTK_POS_BOTTOM:
1628 rectangle->width = allocation.width - 2 * border_width;
1629 rectangle->height = visible_page->requisition.height;
1630 if (tab_pos == GTK_POS_BOTTOM)
1631 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1633 for (i = 0; i < N_ACTION_WIDGETS; i++)
1635 if (priv->action_widget[i] &&
1636 gtk_widget_get_visible (priv->action_widget[i]))
1638 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1640 rectangle->width -= action_allocation.width;
1641 if ((!is_rtl && i == ACTION_WIDGET_START) ||
1642 (is_rtl && i == ACTION_WIDGET_END))
1643 rectangle->x += action_allocation.width;
1649 rectangle->width = visible_page->requisition.width;
1650 rectangle->height = allocation.height - 2 * border_width;
1651 if (tab_pos == GTK_POS_RIGHT)
1652 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1654 for (i = 0; i < N_ACTION_WIDGETS; i++)
1656 if (priv->action_widget[i] &&
1657 gtk_widget_get_visible (priv->action_widget[i]))
1659 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1661 rectangle->height -= action_allocation.height;
1663 if (i == ACTION_WIDGET_START)
1664 rectangle->y += action_allocation.height;
1677 rectangle->x = rectangle->y = 0;
1678 rectangle->width = rectangle->height = 10;
1686 gtk_notebook_map (GtkWidget *widget)
1688 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1689 GtkNotebookPrivate *priv = notebook->priv;
1690 GtkNotebookPage *page;
1694 gtk_widget_set_mapped (widget, TRUE);
1696 if (priv->cur_page &&
1697 gtk_widget_get_visible (priv->cur_page->child) &&
1698 !gtk_widget_get_mapped (priv->cur_page->child))
1699 gtk_widget_map (priv->cur_page->child);
1701 for (i = 0; i < N_ACTION_WIDGETS; i++)
1703 if (priv->action_widget[i] &&
1704 gtk_widget_get_visible (priv->action_widget[i]) &&
1705 gtk_widget_get_child_visible (priv->action_widget[i]) &&
1706 !gtk_widget_get_mapped (priv->action_widget[i]))
1707 gtk_widget_map (priv->action_widget[i]);
1710 if (priv->scrollable)
1711 gtk_notebook_pages_allocate (notebook);
1714 children = priv->children;
1718 page = children->data;
1719 children = children->next;
1721 if (page->tab_label &&
1722 gtk_widget_get_visible (page->tab_label) &&
1723 !gtk_widget_get_mapped (page->tab_label))
1724 gtk_widget_map (page->tab_label);
1728 if (gtk_notebook_get_event_window_position (notebook, NULL))
1729 gdk_window_show_unraised (priv->event_window);
1733 gtk_notebook_unmap (GtkWidget *widget)
1735 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1736 GtkNotebookPrivate *priv = notebook->priv;
1738 stop_scrolling (notebook);
1740 gtk_widget_set_mapped (widget, FALSE);
1742 gdk_window_hide (priv->event_window);
1744 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1748 gtk_notebook_realize (GtkWidget *widget)
1750 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1751 GtkNotebookPrivate *priv = notebook->priv;
1753 GdkWindowAttr attributes;
1754 gint attributes_mask;
1755 GdkRectangle event_window_pos;
1757 gtk_widget_set_realized (widget, TRUE);
1759 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1761 window = gtk_widget_get_parent_window (widget);
1762 gtk_widget_set_window (widget, window);
1763 g_object_ref (window);
1765 attributes.window_type = GDK_WINDOW_CHILD;
1766 attributes.x = event_window_pos.x;
1767 attributes.y = event_window_pos.y;
1768 attributes.width = event_window_pos.width;
1769 attributes.height = event_window_pos.height;
1770 attributes.wclass = GDK_INPUT_ONLY;
1771 attributes.event_mask = gtk_widget_get_events (widget);
1772 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1773 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1774 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1775 attributes_mask = GDK_WA_X | GDK_WA_Y;
1777 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1778 &attributes, attributes_mask);
1779 gdk_window_set_user_data (priv->event_window, notebook);
1781 gtk_widget_style_attach (widget);
1785 gtk_notebook_unrealize (GtkWidget *widget)
1787 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1788 GtkNotebookPrivate *priv = notebook->priv;
1790 gdk_window_set_user_data (priv->event_window, NULL);
1791 gdk_window_destroy (priv->event_window);
1792 priv->event_window = NULL;
1794 if (priv->drag_window)
1796 gdk_window_set_user_data (priv->drag_window, NULL);
1797 gdk_window_destroy (priv->drag_window);
1798 priv->drag_window = NULL;
1801 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1805 gtk_notebook_size_request (GtkWidget *widget,
1806 GtkRequisition *requisition)
1808 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1809 GtkNotebookPrivate *priv = notebook->priv;
1810 GtkNotebookPage *page;
1812 GtkRequisition child_requisition;
1813 GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
1814 gboolean switch_page = FALSE;
1820 gint scroll_arrow_hlength;
1821 gint scroll_arrow_vlength;
1824 gtk_widget_style_get (widget,
1825 "focus-line-width", &focus_width,
1826 "tab-overlap", &tab_overlap,
1827 "tab-curvature", &tab_curvature,
1828 "arrow-spacing", &arrow_spacing,
1829 "scroll-arrow-hlength", &scroll_arrow_hlength,
1830 "scroll-arrow-vlength", &scroll_arrow_vlength,
1833 requisition->width = 0;
1834 requisition->height = 0;
1836 for (children = priv->children, vis_pages = 0; children;
1837 children = children->next)
1840 page = children->data;
1842 if (gtk_widget_get_visible (page->child))
1845 gtk_widget_get_preferred_size (page->child,
1846 &child_requisition, NULL);
1848 requisition->width = MAX (requisition->width,
1849 child_requisition.width);
1850 requisition->height = MAX (requisition->height,
1851 child_requisition.height);
1853 if (priv->menu && page->menu_label)
1855 parent = gtk_widget_get_parent (page->menu_label);
1856 if (parent && !gtk_widget_get_visible (parent))
1857 gtk_widget_show (parent);
1862 if (page == priv->cur_page)
1865 if (priv->menu && page->menu_label)
1867 parent = gtk_widget_get_parent (page->menu_label);
1868 if (parent && gtk_widget_get_visible (parent))
1869 gtk_widget_hide (parent);
1874 if (priv->show_border || priv->show_tabs)
1878 style = gtk_widget_get_style (widget);
1880 requisition->width += style->xthickness * 2;
1881 requisition->height += style->ythickness * 2;
1883 if (priv->show_tabs)
1886 gint tab_height = 0;
1890 gint action_width = 0;
1891 gint action_height = 0;
1893 for (children = priv->children; children;
1894 children = children->next)
1896 page = children->data;
1898 if (gtk_widget_get_visible (page->child))
1900 if (!gtk_widget_get_visible (page->tab_label))
1901 gtk_widget_show (page->tab_label);
1903 gtk_widget_get_preferred_size (page->tab_label,
1904 &child_requisition, NULL);
1906 page->requisition.width = child_requisition.width + 2 * style->xthickness;
1907 page->requisition.height = child_requisition.height + 2 * style->ythickness;
1909 switch (priv->tab_pos)
1912 case GTK_POS_BOTTOM:
1913 page->requisition.height += 2 * (priv->tab_vborder +
1915 tab_height = MAX (tab_height, page->requisition.height);
1916 tab_max = MAX (tab_max, page->requisition.width);
1920 page->requisition.width += 2 * (priv->tab_hborder +
1922 tab_width = MAX (tab_width, page->requisition.width);
1923 tab_max = MAX (tab_max, page->requisition.height);
1927 else if (gtk_widget_get_visible (page->tab_label))
1928 gtk_widget_hide (page->tab_label);
1931 children = priv->children;
1935 for (i = 0; i < N_ACTION_WIDGETS; i++)
1937 if (priv->action_widget[i])
1939 gtk_widget_get_preferred_size (priv->action_widget[i],
1940 &action_widget_requisition[i], NULL);
1941 action_widget_requisition[i].width += style->xthickness;
1942 action_widget_requisition[i].height += style->ythickness;
1946 switch (priv->tab_pos)
1949 case GTK_POS_BOTTOM:
1950 if (tab_height == 0)
1953 if (priv->scrollable && vis_pages > 1 &&
1954 requisition->width < tab_width)
1955 tab_height = MAX (tab_height, scroll_arrow_hlength);
1957 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
1958 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
1960 padding = 2 * (tab_curvature + focus_width +
1961 priv->tab_hborder) - tab_overlap;
1965 page = children->data;
1966 children = children->next;
1968 if (!gtk_widget_get_visible (page->child))
1971 if (priv->homogeneous)
1972 page->requisition.width = tab_max;
1974 page->requisition.width += padding;
1976 tab_width += page->requisition.width;
1977 page->requisition.height = tab_height;
1980 if (priv->scrollable && vis_pages > 1 &&
1981 requisition->width < tab_width)
1982 tab_width = tab_max + 2 * (scroll_arrow_hlength + arrow_spacing);
1984 action_width += action_widget_requisition[ACTION_WIDGET_START].width;
1985 action_width += action_widget_requisition[ACTION_WIDGET_END].width;
1986 if (priv->homogeneous && !priv->scrollable)
1987 requisition->width = MAX (requisition->width,
1988 vis_pages * tab_max +
1989 tab_overlap + action_width);
1991 requisition->width = MAX (requisition->width,
1992 tab_width + tab_overlap + action_width);
1994 requisition->height += tab_height;
2001 if (priv->scrollable && vis_pages > 1 &&
2002 requisition->height < tab_height)
2003 tab_width = MAX (tab_width,
2004 arrow_spacing + 2 * scroll_arrow_vlength);
2006 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2007 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2009 padding = 2 * (tab_curvature + focus_width +
2010 priv->tab_vborder) - tab_overlap;
2015 page = children->data;
2016 children = children->next;
2018 if (!gtk_widget_get_visible (page->child))
2021 page->requisition.width = tab_width;
2023 if (priv->homogeneous)
2024 page->requisition.height = tab_max;
2026 page->requisition.height += padding;
2028 tab_height += page->requisition.height;
2031 if (priv->scrollable && vis_pages > 1 &&
2032 requisition->height < tab_height)
2033 tab_height = tab_max + (2 * scroll_arrow_vlength + arrow_spacing);
2034 action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2035 action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2037 if (priv->homogeneous && !priv->scrollable)
2038 requisition->height =
2039 MAX (requisition->height,
2040 vis_pages * tab_max + tab_overlap + action_height);
2042 requisition->height =
2043 MAX (requisition->height,
2044 tab_height + tab_overlap + action_height);
2046 if (!priv->homogeneous || priv->scrollable)
2048 requisition->height = MAX (requisition->height,
2049 vis_pages * tab_max +
2052 requisition->width += tab_width;
2059 for (children = priv->children; children;
2060 children = children->next)
2062 page = children->data;
2064 if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2065 gtk_widget_hide (page->tab_label);
2070 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2072 requisition->width += border_width * 2;
2073 requisition->height += border_width * 2;
2079 for (children = priv->children; children;
2080 children = children->next)
2082 page = children->data;
2083 if (gtk_widget_get_visible (page->child))
2085 gtk_notebook_switch_page (notebook, page);
2090 else if (gtk_widget_get_visible (widget))
2092 requisition->width = border_width * 2;
2093 requisition->height = border_width * 2;
2096 if (vis_pages && !priv->cur_page)
2098 children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2101 priv->first_tab = children;
2102 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2108 gtk_notebook_size_allocate (GtkWidget *widget,
2109 GtkAllocation *allocation)
2111 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2112 GtkNotebookPrivate *priv = notebook->priv;
2114 gint tab_pos = get_effective_tab_pos (notebook);
2118 style = gtk_widget_get_style (widget);
2120 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2122 gtk_widget_set_allocation (widget, allocation);
2124 if (gtk_widget_get_realized (widget))
2126 GdkRectangle position;
2128 if (gtk_notebook_get_event_window_position (notebook, &position))
2130 gdk_window_move_resize (priv->event_window,
2131 position.x, position.y,
2132 position.width, position.height);
2133 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2134 gdk_window_show_unraised (priv->event_window);
2137 gdk_window_hide (priv->event_window);
2142 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2143 GtkNotebookPage *page;
2144 GtkAllocation child_allocation;
2148 child_allocation.x = allocation->x + border_width;
2149 child_allocation.y = allocation->y + border_width;
2150 child_allocation.width = MAX (1, allocation->width - border_width * 2);
2151 child_allocation.height = MAX (1, allocation->height - border_width * 2);
2153 if (priv->show_tabs || priv->show_border)
2155 child_allocation.x += style->xthickness;
2156 child_allocation.y += style->ythickness;
2157 child_allocation.width = MAX (1, child_allocation.width - style->xthickness * 2);
2158 child_allocation.height = MAX (1, child_allocation.height - style->ythickness * 2);
2160 if (priv->show_tabs && priv->children && priv->cur_page)
2165 child_allocation.y += priv->cur_page->requisition.height;
2166 case GTK_POS_BOTTOM:
2167 child_allocation.height =
2168 MAX (1, child_allocation.height -
2169 priv->cur_page->requisition.height);
2172 child_allocation.x += priv->cur_page->requisition.width;
2174 child_allocation.width =
2175 MAX (1, child_allocation.width -
2176 priv->cur_page->requisition.width);
2180 for (i = 0; i < N_ACTION_WIDGETS; i++)
2182 GtkAllocation widget_allocation;
2183 GtkRequisition requisition;
2185 if (!priv->action_widget[i])
2188 widget_allocation.x = allocation->x + border_width;
2189 widget_allocation.y = allocation->y + border_width;
2190 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2192 gtk_widget_get_preferred_size (priv->action_widget[i],
2193 &requisition, NULL);
2197 case GTK_POS_BOTTOM:
2198 widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2201 widget_allocation.width = requisition.width;
2202 widget_allocation.height = priv->cur_page->requisition.height - style->ythickness;
2204 if ((i == ACTION_WIDGET_START && is_rtl) ||
2205 (i == ACTION_WIDGET_END && !is_rtl))
2206 widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2207 if (tab_pos == GTK_POS_TOP) /* no fall through */
2208 widget_allocation.y += 2 * focus_width;
2211 widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2214 widget_allocation.height = requisition.height;
2215 widget_allocation.width = priv->cur_page->requisition.width - style->xthickness;
2217 if (i == ACTION_WIDGET_END)
2218 widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2219 if (tab_pos == GTK_POS_LEFT) /* no fall through */
2220 widget_allocation.x += 2 * focus_width;
2224 gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2229 children = priv->children;
2232 page = children->data;
2233 children = children->next;
2235 if (gtk_widget_get_visible (page->child))
2236 gtk_widget_size_allocate (page->child, &child_allocation);
2239 gtk_notebook_pages_allocate (notebook);
2244 gtk_notebook_draw (GtkWidget *widget,
2247 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2248 GtkNotebookPrivate *priv = notebook->priv;
2249 GtkAllocation allocation;
2253 gtk_widget_get_allocation (widget, &allocation);
2255 window = gtk_widget_get_window (widget);
2256 if (gtk_cairo_should_draw_window (cr, window))
2260 cairo_translate (cr, -allocation.x, -allocation.y);
2261 gtk_notebook_paint (widget, cr);
2265 if (priv->show_tabs)
2267 GtkNotebookPage *page;
2270 for (pages = priv->children; pages; pages = pages->next)
2272 page = GTK_NOTEBOOK_PAGE (pages);
2274 if (gtk_widget_get_parent (page->tab_label) == widget)
2275 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2276 page->tab_label, cr);
2280 if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2281 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2282 priv->cur_page->child,
2284 if (priv->show_tabs)
2286 for (i = 0; i < N_ACTION_WIDGETS; i++)
2288 if (priv->action_widget[i])
2289 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2290 priv->action_widget[i], cr);
2295 if (priv->operation == DRAG_OPERATION_REORDER &&
2296 gtk_cairo_should_draw_window (cr, priv->drag_window))
2299 gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2301 /* FIXME: This is a workaround to make tabs reordering work better
2302 * with engines with rounded tabs. If the drag window background
2303 * isn't set, the rounded corners would be black.
2305 * Ideally, these corners should be made transparent, Either by using
2306 * ARGB visuals or shape windows.
2308 gdk_cairo_set_source_color (cr, >k_widget_get_style (widget)->bg [GTK_STATE_NORMAL]);
2311 gtk_notebook_draw_tab (notebook,
2317 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2318 priv->cur_page->tab_label, cr);
2325 gtk_notebook_show_arrows (GtkNotebook *notebook)
2327 GtkNotebookPrivate *priv = notebook->priv;
2328 gboolean show_arrow = FALSE;
2331 if (!priv->scrollable)
2334 children = priv->children;
2337 GtkNotebookPage *page = children->data;
2339 if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2342 children = children->next;
2349 gtk_notebook_get_arrow_rect (GtkNotebook *notebook,
2350 GdkRectangle *rectangle,
2351 GtkNotebookArrow arrow)
2353 GtkNotebookPrivate *priv = notebook->priv;
2354 GdkRectangle event_window_pos;
2355 gboolean before = ARROW_IS_BEFORE (arrow);
2356 gboolean left = ARROW_IS_LEFT (arrow);
2358 if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2360 gint scroll_arrow_hlength;
2361 gint scroll_arrow_vlength;
2363 gtk_widget_style_get (GTK_WIDGET (notebook),
2364 "scroll-arrow-hlength", &scroll_arrow_hlength,
2365 "scroll-arrow-vlength", &scroll_arrow_vlength,
2368 switch (priv->tab_pos)
2372 rectangle->width = scroll_arrow_vlength;
2373 rectangle->height = scroll_arrow_vlength;
2375 if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2376 (!before && (priv->has_after_previous != priv->has_after_next)))
2377 rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2379 rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2381 rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2382 rectangle->y = event_window_pos.y;
2384 rectangle->y += event_window_pos.height - rectangle->height;
2388 case GTK_POS_BOTTOM:
2389 rectangle->width = scroll_arrow_hlength;
2390 rectangle->height = scroll_arrow_hlength;
2394 if (left || !priv->has_before_previous)
2395 rectangle->x = event_window_pos.x;
2397 rectangle->x = event_window_pos.x + rectangle->width;
2401 if (!left || !priv->has_after_next)
2402 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2404 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2406 rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2412 static GtkNotebookArrow
2413 gtk_notebook_get_arrow (GtkNotebook *notebook,
2417 GtkNotebookPrivate *priv = notebook->priv;
2418 GdkRectangle arrow_rect;
2419 GdkRectangle event_window_pos;
2422 GtkNotebookArrow arrow[4];
2424 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2425 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2426 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2427 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2429 if (gtk_notebook_show_arrows (notebook))
2431 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2432 for (i = 0; i < 4; i++)
2434 if (arrow[i] == ARROW_NONE)
2437 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2439 x0 = x - arrow_rect.x;
2440 y0 = y - arrow_rect.y;
2442 if (y0 >= 0 && y0 < arrow_rect.height &&
2443 x0 >= 0 && x0 < arrow_rect.width)
2452 gtk_notebook_do_arrow (GtkNotebook *notebook,
2453 GtkNotebookArrow arrow)
2455 GtkNotebookPrivate *priv = notebook->priv;
2456 GtkWidget *widget = GTK_WIDGET (notebook);
2457 gboolean is_rtl, left;
2459 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2460 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2461 (!ARROW_IS_LEFT (arrow) && is_rtl);
2463 if (!priv->focus_tab ||
2464 gtk_notebook_search_page (notebook, priv->focus_tab,
2465 left ? STEP_PREV : STEP_NEXT,
2468 gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2469 gtk_widget_grab_focus (widget);
2474 gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2475 GtkNotebookArrow arrow,
2478 GtkNotebookPrivate *priv = notebook->priv;
2479 GtkWidget *widget = GTK_WIDGET (notebook);
2480 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2481 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2482 (!ARROW_IS_LEFT (arrow) && is_rtl);
2484 if (!gtk_widget_has_focus (widget))
2485 gtk_widget_grab_focus (widget);
2487 priv->button = button;
2488 priv->click_child = arrow;
2492 gtk_notebook_do_arrow (notebook, arrow);
2493 gtk_notebook_set_scroll_timer (notebook);
2495 else if (button == 2)
2496 gtk_notebook_page_select (notebook, TRUE);
2497 else if (button == 3)
2498 gtk_notebook_switch_focus_tab (notebook,
2499 gtk_notebook_search_page (notebook,
2501 left ? STEP_NEXT : STEP_PREV,
2503 gtk_notebook_redraw_arrows (notebook);
2509 get_widget_coordinates (GtkWidget *widget,
2514 GdkWindow *window = ((GdkEventAny *)event)->window;
2517 if (!gdk_event_get_coords (event, &tx, &ty))
2520 while (window && window != gtk_widget_get_window (widget))
2522 gint window_x, window_y;
2524 gdk_window_get_position (window, &window_x, &window_y);
2528 window = gdk_window_get_parent (window);
2543 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2545 GtkNotebookPrivate *priv = notebook->priv;
2546 GtkNotebookPage *page;
2549 children = priv->children;
2552 page = children->data;
2554 if (gtk_widget_get_visible (page->child) &&
2555 page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2556 (x >= page->allocation.x) &&
2557 (y >= page->allocation.y) &&
2558 (x <= (page->allocation.x + page->allocation.width)) &&
2559 (y <= (page->allocation.y + page->allocation.height)))
2562 children = children->next;
2569 gtk_notebook_button_press (GtkWidget *widget,
2570 GdkEventButton *event)
2572 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2573 GtkNotebookPrivate *priv = notebook->priv;
2574 GtkNotebookPage *page;
2576 GtkNotebookArrow arrow;
2579 if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2583 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2586 arrow = gtk_notebook_get_arrow (notebook, x, y);
2588 return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2590 if (event->button == 3 && priv->menu)
2592 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2593 NULL, NULL, 3, event->time);
2597 if (event->button != 1)
2600 priv->button = event->button;
2602 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2604 gboolean page_changed, was_focus;
2607 page_changed = page != priv->cur_page;
2608 was_focus = gtk_widget_is_focus (widget);
2610 gtk_notebook_switch_focus_tab (notebook, tab);
2611 gtk_widget_grab_focus (widget);
2613 if (page_changed && !was_focus)
2614 gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2616 /* save press to possibly begin a drag */
2617 if (page->reorderable || page->detachable)
2619 priv->during_detach = FALSE;
2620 priv->during_reorder = FALSE;
2621 priv->pressed_button = event->button;
2626 priv->drag_begin_x = priv->mouse_x;
2627 priv->drag_begin_y = priv->mouse_y;
2628 priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2629 priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2637 popup_position_func (GtkMenu *menu,
2643 GtkNotebook *notebook = data;
2644 GtkNotebookPrivate *priv = notebook->priv;
2645 GtkAllocation allocation;
2647 GtkRequisition requisition;
2649 if (priv->focus_tab)
2651 GtkNotebookPage *page;
2653 page = priv->focus_tab->data;
2654 w = page->tab_label;
2658 w = GTK_WIDGET (notebook);
2661 gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2663 gtk_widget_get_allocation (w, &allocation);
2664 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2665 &requisition, NULL);
2667 if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
2668 *x += allocation.x + allocation.width - requisition.width;
2672 *y += allocation.y + allocation.height;
2678 gtk_notebook_popup_menu (GtkWidget *widget)
2680 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2681 GtkNotebookPrivate *priv = notebook->priv;
2685 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2686 popup_position_func, notebook,
2687 0, gtk_get_current_event_time ());
2688 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
2696 stop_scrolling (GtkNotebook *notebook)
2698 GtkNotebookPrivate *priv = notebook->priv;
2702 g_source_remove (priv->timer);
2704 priv->need_timer = FALSE;
2706 priv->click_child = 0;
2708 gtk_notebook_redraw_arrows (notebook);
2712 get_drop_position (GtkNotebook *notebook,
2715 GtkNotebookPrivate *priv = notebook->priv;
2716 GList *children, *last_child;
2717 GtkNotebookPage *page;
2724 is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
2725 children = priv->children;
2730 page = children->data;
2732 if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
2733 gtk_widget_get_visible (page->child) &&
2735 gtk_widget_get_mapped (page->tab_label) &&
2738 switch (priv->tab_pos)
2741 case GTK_POS_BOTTOM:
2744 if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_X (page) > x) ||
2745 (page->pack == GTK_PACK_END && PAGE_MIDDLE_X (page) < x))
2750 if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_X (page) < x) ||
2751 (page->pack == GTK_PACK_END && PAGE_MIDDLE_X (page) > x))
2758 if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_Y (page) > y) ||
2759 (page->pack == GTK_PACK_END && PAGE_MIDDLE_Y (page) < y))
2765 last_child = children->next;
2768 children = children->next;
2775 show_drag_window (GtkNotebook *notebook,
2776 GtkNotebookPrivate *priv,
2777 GtkNotebookPage *page,
2780 GtkWidget *widget = GTK_WIDGET (notebook);
2782 if (!priv->drag_window)
2784 GdkWindowAttr attributes;
2785 guint attributes_mask;
2787 attributes.x = page->allocation.x;
2788 attributes.y = page->allocation.y;
2789 attributes.width = page->allocation.width;
2790 attributes.height = page->allocation.height;
2791 attributes.window_type = GDK_WINDOW_CHILD;
2792 attributes.wclass = GDK_INPUT_OUTPUT;
2793 attributes.visual = gtk_widget_get_visual (widget);
2794 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2795 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2797 priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
2800 gdk_window_set_user_data (priv->drag_window, widget);
2803 g_object_ref (page->tab_label);
2804 gtk_widget_unparent (page->tab_label);
2805 gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
2806 gtk_widget_set_parent (page->tab_label, widget);
2807 g_object_unref (page->tab_label);
2809 gdk_window_show (priv->drag_window);
2811 /* the grab will dissapear when the window is hidden */
2812 gdk_device_grab (device, priv->drag_window,
2813 GDK_OWNERSHIP_WINDOW, FALSE,
2814 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2815 NULL, GDK_CURRENT_TIME);
2818 /* This function undoes the reparenting that happens both when drag_window
2819 * is shown for reordering and when the DnD icon is shown for detaching
2822 hide_drag_window (GtkNotebook *notebook,
2823 GtkNotebookPrivate *priv,
2824 GtkNotebookPage *page)
2826 GtkWidget *widget = GTK_WIDGET (notebook);
2827 GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
2829 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
2830 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
2832 g_object_ref (page->tab_label);
2834 if (GTK_IS_WINDOW (parent))
2836 /* parent widget is the drag window */
2837 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
2840 gtk_widget_unparent (page->tab_label);
2842 gtk_widget_set_parent (page->tab_label, widget);
2843 g_object_unref (page->tab_label);
2846 if (priv->drag_window &&
2847 gdk_window_is_visible (priv->drag_window))
2848 gdk_window_hide (priv->drag_window);
2852 gtk_notebook_stop_reorder (GtkNotebook *notebook)
2854 GtkNotebookPrivate *priv = notebook->priv;
2855 GtkNotebookPage *page;
2857 if (priv->operation == DRAG_OPERATION_DETACH)
2858 page = priv->detached_tab;
2860 page = priv->cur_page;
2862 if (!page || !page->tab_label)
2865 priv->pressed_button = -1;
2867 if (page->reorderable || page->detachable)
2869 if (priv->during_reorder)
2871 gint old_page_num, page_num;
2874 element = get_drop_position (notebook, page->pack);
2875 old_page_num = g_list_position (priv->children, priv->focus_tab);
2876 page_num = reorder_tab (notebook, element, priv->focus_tab);
2877 gtk_notebook_child_reordered (notebook, page);
2879 if (priv->has_scrolled || old_page_num != page_num)
2880 g_signal_emit (notebook,
2881 notebook_signals[PAGE_REORDERED], 0,
2882 page->child, page_num);
2884 priv->has_scrolled = FALSE;
2885 priv->during_reorder = FALSE;
2888 hide_drag_window (notebook, priv, page);
2890 priv->operation = DRAG_OPERATION_NONE;
2891 gtk_notebook_pages_allocate (notebook);
2893 if (priv->dnd_timer)
2895 g_source_remove (priv->dnd_timer);
2896 priv->dnd_timer = 0;
2902 gtk_notebook_button_release (GtkWidget *widget,
2903 GdkEventButton *event)
2905 GtkNotebook *notebook;
2906 GtkNotebookPrivate *priv;
2907 GtkNotebookPage *page;
2909 if (event->type != GDK_BUTTON_RELEASE)
2912 notebook = GTK_NOTEBOOK (widget);
2913 priv = notebook->priv;
2915 page = priv->cur_page;
2917 if (!priv->during_detach &&
2918 page->reorderable &&
2919 event->button == priv->pressed_button)
2920 gtk_notebook_stop_reorder (notebook);
2922 if (event->button == priv->button)
2924 stop_scrolling (notebook);
2932 gtk_notebook_leave_notify (GtkWidget *widget,
2933 GdkEventCrossing *event)
2935 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2936 GtkNotebookPrivate *priv = notebook->priv;
2939 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2945 gtk_notebook_redraw_arrows (notebook);
2951 static GtkNotebookPointerPosition
2952 get_pointer_position (GtkNotebook *notebook)
2954 GtkNotebookPrivate *priv = notebook->priv;
2955 GtkWidget *widget = GTK_WIDGET (notebook);
2956 gint wx, wy, width, height;
2959 if (!priv->scrollable)
2960 return POINTER_BETWEEN;
2962 gdk_window_get_position (priv->event_window, &wx, &wy);
2963 width = gdk_window_get_width (priv->event_window);
2964 height = gdk_window_get_height (priv->event_window);
2966 if (priv->tab_pos == GTK_POS_TOP ||
2967 priv->tab_pos == GTK_POS_BOTTOM)
2971 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2972 x = priv->mouse_x - wx;
2974 if (x > width - SCROLL_THRESHOLD)
2975 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
2976 else if (x < SCROLL_THRESHOLD)
2977 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
2979 return POINTER_BETWEEN;
2985 y = priv->mouse_y - wy;
2986 if (y > height - SCROLL_THRESHOLD)
2987 return POINTER_AFTER;
2988 else if (y < SCROLL_THRESHOLD)
2989 return POINTER_BEFORE;
2991 return POINTER_BETWEEN;
2996 scroll_notebook_timer (gpointer data)
2998 GtkNotebook *notebook = GTK_NOTEBOOK (data);
2999 GtkNotebookPrivate *priv = notebook->priv;
3000 GtkNotebookPointerPosition pointer_position;
3001 GList *element, *first_tab;
3003 pointer_position = get_pointer_position (notebook);
3005 element = get_drop_position (notebook, priv->cur_page->pack);
3006 reorder_tab (notebook, element, priv->focus_tab);
3007 first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3008 (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3012 priv->first_tab = first_tab;
3013 gtk_notebook_pages_allocate (notebook);
3015 gdk_window_move_resize (priv->drag_window,
3016 priv->drag_window_x,
3017 priv->drag_window_y,
3018 priv->cur_page->allocation.width,
3019 priv->cur_page->allocation.height);
3020 gdk_window_raise (priv->drag_window);
3027 check_threshold (GtkNotebook *notebook,
3031 GtkNotebookPrivate *priv = notebook->priv;
3034 GdkRectangle rectangle = { 0, }; /* shut up gcc */
3035 GtkSettings *settings;
3037 widget = GTK_WIDGET (notebook);
3038 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3039 g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3041 /* we want a large threshold */
3042 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3044 gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3045 rectangle.width = gdk_window_get_width (priv->event_window);
3046 rectangle.height = gdk_window_get_height (priv->event_window);
3048 rectangle.x -= dnd_threshold;
3049 rectangle.width += 2 * dnd_threshold;
3050 rectangle.y -= dnd_threshold;
3051 rectangle.height += 2 * dnd_threshold;
3053 return (current_x < rectangle.x ||
3054 current_x > rectangle.x + rectangle.width ||
3055 current_y < rectangle.y ||
3056 current_y > rectangle.y + rectangle.height);
3060 gtk_notebook_motion_notify (GtkWidget *widget,
3061 GdkEventMotion *event)
3063 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3064 GtkNotebookPrivate *priv = notebook->priv;
3065 GtkNotebookPage *page;
3066 GtkNotebookArrow arrow;
3067 GtkNotebookPointerPosition pointer_position;
3068 GtkSettings *settings;
3072 page = priv->cur_page;
3077 if (!(event->state & GDK_BUTTON1_MASK) &&
3078 priv->pressed_button != -1)
3080 gtk_notebook_stop_reorder (notebook);
3081 stop_scrolling (notebook);
3084 if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3087 priv->timestamp = event->time;
3089 /* While animating the move, event->x is relative to the flying tab
3090 * (priv->drag_window has a pointer grab), but we need coordinates relative to
3091 * the notebook widget.
3093 gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3094 priv->mouse_x = event->x_root - x_win;
3095 priv->mouse_y = event->y_root - y_win;
3097 arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3098 if (arrow != priv->in_child)
3100 priv->in_child = arrow;
3101 gtk_notebook_redraw_arrows (notebook);
3104 if (priv->pressed_button == -1)
3107 if (page->detachable &&
3108 check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3110 priv->detached_tab = priv->cur_page;
3111 priv->during_detach = TRUE;
3113 gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3114 priv->pressed_button, (GdkEvent*) event);
3118 if (page->reorderable &&
3119 (priv->during_reorder ||
3120 gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3122 priv->during_reorder = TRUE;
3123 pointer_position = get_pointer_position (notebook);
3125 if (event->window == priv->drag_window &&
3126 pointer_position != POINTER_BETWEEN &&
3127 gtk_notebook_show_arrows (notebook))
3130 if (!priv->dnd_timer)
3132 priv->has_scrolled = TRUE;
3133 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3134 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3136 priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3137 scroll_notebook_timer,
3138 (gpointer) notebook);
3143 if (priv->dnd_timer)
3145 g_source_remove (priv->dnd_timer);
3146 priv->dnd_timer = 0;
3150 if (event->window == priv->drag_window ||
3151 priv->operation != DRAG_OPERATION_REORDER)
3153 /* the drag operation is beginning, create the window */
3154 if (priv->operation != DRAG_OPERATION_REORDER)
3156 priv->operation = DRAG_OPERATION_REORDER;
3157 show_drag_window (notebook, priv, page, event->device);
3160 gtk_notebook_pages_allocate (notebook);
3161 gdk_window_move_resize (priv->drag_window,
3162 priv->drag_window_x,
3163 priv->drag_window_y,
3164 page->allocation.width,
3165 page->allocation.height);
3173 gtk_notebook_grab_notify (GtkWidget *widget,
3174 gboolean was_grabbed)
3176 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3180 gtk_notebook_stop_reorder (notebook);
3181 stop_scrolling (notebook);
3186 gtk_notebook_state_changed (GtkWidget *widget,
3187 GtkStateType previous_state)
3189 if (!gtk_widget_is_sensitive (widget))
3190 stop_scrolling (GTK_NOTEBOOK (widget));
3194 gtk_notebook_focus_in (GtkWidget *widget,
3195 GdkEventFocus *event)
3197 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3203 gtk_notebook_focus_out (GtkWidget *widget,
3204 GdkEventFocus *event)
3206 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3212 gtk_notebook_style_set (GtkWidget *widget,
3215 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3216 GtkNotebookPrivate *priv = notebook->priv;
3218 gboolean has_before_previous;
3219 gboolean has_before_next;
3220 gboolean has_after_previous;
3221 gboolean has_after_next;
3223 gtk_widget_style_get (widget,
3224 "has-backward-stepper", &has_before_previous,
3225 "has-secondary-forward-stepper", &has_before_next,
3226 "has-secondary-backward-stepper", &has_after_previous,
3227 "has-forward-stepper", &has_after_next,
3230 priv->has_before_previous = has_before_previous;
3231 priv->has_before_next = has_before_next;
3232 priv->has_after_previous = has_after_previous;
3233 priv->has_after_next = has_after_next;
3235 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_set (widget, previous);
3239 on_drag_icon_draw (GtkWidget *widget,
3243 GtkWidget *notebook, *child;
3244 GtkRequisition requisition;
3247 notebook = GTK_WIDGET (data);
3248 child = gtk_bin_get_child (GTK_BIN (widget));
3250 gtk_widget_get_preferred_size (widget,
3251 &requisition, NULL);
3252 gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3254 gtk_paint_extension (gtk_widget_get_style (notebook),
3256 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3259 requisition.width, requisition.height,
3262 gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3268 gtk_notebook_drag_begin (GtkWidget *widget,
3269 GdkDragContext *context)
3271 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3272 GtkNotebookPrivate *priv = notebook->priv;
3273 GtkWidget *tab_label;
3275 if (priv->dnd_timer)
3277 g_source_remove (priv->dnd_timer);
3278 priv->dnd_timer = 0;
3281 priv->operation = DRAG_OPERATION_DETACH;
3282 gtk_notebook_pages_allocate (notebook);
3284 tab_label = priv->detached_tab->tab_label;
3286 hide_drag_window (notebook, priv, priv->cur_page);
3287 g_object_ref (tab_label);
3288 gtk_widget_unparent (tab_label);
3290 priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3291 gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3292 gtk_widget_get_screen (widget));
3293 gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3294 gtk_widget_set_size_request (priv->dnd_window,
3295 priv->detached_tab->allocation.width,
3296 priv->detached_tab->allocation.height);
3297 g_object_unref (tab_label);
3299 g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3300 G_CALLBACK (on_drag_icon_draw), notebook);
3302 gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3306 gtk_notebook_drag_end (GtkWidget *widget,
3307 GdkDragContext *context)
3309 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3310 GtkNotebookPrivate *priv = notebook->priv;
3312 gtk_notebook_stop_reorder (notebook);
3314 if (priv->detached_tab)
3315 gtk_notebook_switch_page (notebook, priv->detached_tab);
3317 _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3318 gtk_widget_destroy (priv->dnd_window);
3319 priv->dnd_window = NULL;
3321 priv->operation = DRAG_OPERATION_NONE;
3324 static GtkNotebook *
3325 gtk_notebook_create_window (GtkNotebook *notebook,
3334 gtk_notebook_drag_failed (GtkWidget *widget,
3335 GdkDragContext *context,
3336 GtkDragResult result,
3339 if (result == GTK_DRAG_RESULT_NO_TARGET)
3341 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3342 GtkNotebookPrivate *priv = notebook->priv;
3343 GtkNotebook *dest_notebook = NULL;
3344 GdkDisplay *display;
3347 display = gtk_widget_get_display (widget);
3348 gdk_display_get_pointer (display, NULL, &x, &y, NULL);
3350 g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3351 priv->detached_tab->child, x, y, &dest_notebook);
3354 do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3363 gtk_notebook_switch_tab_timeout (gpointer data)
3365 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3366 GtkNotebookPrivate *priv = notebook->priv;
3370 priv->switch_tab_timer = 0;
3374 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
3376 /* FIXME: hack, we don't want the
3377 * focus to move fom the source widget
3379 priv->child_has_focus = FALSE;
3380 gtk_notebook_switch_focus_tab (notebook, tab);
3387 gtk_notebook_drag_motion (GtkWidget *widget,
3388 GdkDragContext *context,
3393 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3394 GtkNotebookPrivate *priv = notebook->priv;
3395 GtkAllocation allocation;
3396 GdkRectangle position;
3397 GtkSettings *settings;
3398 GtkNotebookArrow arrow;
3400 GdkAtom target, tab_target;
3402 gtk_widget_get_allocation (widget, &allocation);
3404 arrow = gtk_notebook_get_arrow (notebook,
3409 priv->click_child = arrow;
3410 gtk_notebook_set_scroll_timer (notebook);
3411 gdk_drag_status (context, 0, time);
3415 stop_scrolling (notebook);
3416 target = gtk_drag_dest_find_target (widget, context, NULL);
3417 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3419 if (target == tab_target)
3421 GQuark group, source_group;
3422 GtkNotebook *source;
3423 GtkWidget *source_child;
3425 source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3426 source_child = source->priv->cur_page->child;
3428 group = notebook->priv->group;
3429 source_group = source->priv->group;
3431 if (group != 0 && group == source_group &&
3432 !(widget == source_child ||
3433 gtk_widget_is_ancestor (widget, source_child)))
3435 gdk_drag_status (context, GDK_ACTION_MOVE, time);
3440 /* it's a tab, but doesn't share
3441 * ID with this notebook */
3442 gdk_drag_status (context, 0, time);
3449 if (gtk_notebook_get_event_window_position (notebook, &position) &&
3450 x >= position.x && x <= position.x + position.width &&
3451 y >= position.y && y <= position.y + position.height)
3456 if (!priv->switch_tab_timer)
3458 settings = gtk_widget_get_settings (widget);
3460 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3461 priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3462 gtk_notebook_switch_tab_timeout,
3468 if (priv->switch_tab_timer)
3470 g_source_remove (priv->switch_tab_timer);
3471 priv->switch_tab_timer = 0;
3475 return (target == tab_target) ? TRUE : FALSE;
3479 gtk_notebook_drag_leave (GtkWidget *widget,
3480 GdkDragContext *context,
3483 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3484 GtkNotebookPrivate *priv = notebook->priv;
3486 if (priv->switch_tab_timer)
3488 g_source_remove (priv->switch_tab_timer);
3489 priv->switch_tab_timer = 0;
3492 stop_scrolling (GTK_NOTEBOOK (widget));
3496 gtk_notebook_drag_drop (GtkWidget *widget,
3497 GdkDragContext *context,
3502 GdkAtom target, tab_target;
3504 target = gtk_drag_dest_find_target (widget, context, NULL);
3505 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3507 if (target == tab_target)
3509 gtk_drag_get_data (widget, context, target, time);
3517 do_detach_tab (GtkNotebook *from,
3523 GtkNotebookPrivate *to_priv = to->priv;
3524 GtkAllocation to_allocation;
3525 GtkWidget *tab_label, *menu_label;
3526 gboolean tab_expand, tab_fill, reorderable, detachable;
3531 menu_label = gtk_notebook_get_menu_label (from, child);
3534 g_object_ref (menu_label);
3536 tab_label = gtk_notebook_get_tab_label (from, child);
3539 g_object_ref (tab_label);
3541 g_object_ref (child);
3543 gtk_container_child_get (GTK_CONTAINER (from),
3545 "tab-expand", &tab_expand,
3546 "tab-fill", &tab_fill,
3547 "tab-pack", &tab_pack,
3548 "reorderable", &reorderable,
3549 "detachable", &detachable,
3552 gtk_container_remove (GTK_CONTAINER (from), child);
3554 gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3555 to_priv->mouse_x = x + to_allocation.x;
3556 to_priv->mouse_y = y + to_allocation.y;
3558 element = get_drop_position (to, tab_pack);
3559 page_num = g_list_position (to_priv->children, element);
3560 gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3562 gtk_container_child_set (GTK_CONTAINER (to), child,
3563 "tab-pack", tab_pack,
3564 "tab-expand", tab_expand,
3565 "tab-fill", tab_fill,
3566 "reorderable", reorderable,
3567 "detachable", detachable,
3570 g_object_unref (child);
3573 g_object_unref (tab_label);
3576 g_object_unref (menu_label);
3578 gtk_notebook_set_current_page (to, page_num);
3582 gtk_notebook_drag_data_get (GtkWidget *widget,
3583 GdkDragContext *context,
3584 GtkSelectionData *data,
3588 if (data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3590 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3591 GtkNotebookPrivate *priv = notebook->priv;
3593 gtk_selection_data_set (data,
3596 (void*) &priv->detached_tab->child,
3602 gtk_notebook_drag_data_received (GtkWidget *widget,
3603 GdkDragContext *context,
3606 GtkSelectionData *data,
3610 GtkNotebook *notebook;
3611 GtkWidget *source_widget;
3614 notebook = GTK_NOTEBOOK (widget);
3615 source_widget = gtk_drag_get_source_widget (context);
3617 if (source_widget &&
3618 data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3620 child = (void*) data->data;
3622 do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3623 gtk_drag_finish (context, TRUE, FALSE, time);
3626 gtk_drag_finish (context, FALSE, FALSE, time);
3629 /* Private GtkContainer Methods :
3631 * gtk_notebook_set_child_arg
3632 * gtk_notebook_get_child_arg
3634 * gtk_notebook_remove
3635 * gtk_notebook_focus
3636 * gtk_notebook_set_focus_child
3637 * gtk_notebook_child_type
3638 * gtk_notebook_forall
3641 gtk_notebook_set_child_property (GtkContainer *container,
3644 const GValue *value,
3649 GtkPackType pack_type;
3651 /* not finding child's page is valid for menus or labels */
3652 if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3655 switch (property_id)
3657 case CHILD_PROP_TAB_LABEL:
3658 /* a NULL pointer indicates a default_tab setting, otherwise
3659 * we need to set the associated label
3661 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
3662 g_value_get_string (value));
3664 case CHILD_PROP_MENU_LABEL:
3665 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
3666 g_value_get_string (value));
3668 case CHILD_PROP_POSITION:
3669 gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
3670 g_value_get_int (value));
3672 case CHILD_PROP_TAB_EXPAND:
3673 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3674 &expand, &fill, &pack_type);
3675 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3676 g_value_get_boolean (value),
3679 case CHILD_PROP_TAB_FILL:
3680 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3681 &expand, &fill, &pack_type);
3682 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3684 g_value_get_boolean (value),
3687 case CHILD_PROP_TAB_PACK:
3688 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3689 &expand, &fill, &pack_type);
3690 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3692 g_value_get_enum (value));
3694 case CHILD_PROP_REORDERABLE:
3695 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
3696 g_value_get_boolean (value));
3698 case CHILD_PROP_DETACHABLE:
3699 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
3700 g_value_get_boolean (value));
3703 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3709 gtk_notebook_get_child_property (GtkContainer *container,
3715 GtkNotebook *notebook = GTK_NOTEBOOK (container);
3716 GtkNotebookPrivate *priv = notebook->priv;
3721 GtkPackType pack_type;
3723 /* not finding child's page is valid for menus or labels */
3724 list = gtk_notebook_find_child (notebook, child, NULL);
3727 /* nothing to set on labels or menus */
3728 g_param_value_set_default (pspec, value);
3732 switch (property_id)
3734 case CHILD_PROP_TAB_LABEL:
3735 label = gtk_notebook_get_tab_label (notebook, child);
3737 if (GTK_IS_LABEL (label))
3738 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
3740 g_value_set_string (value, NULL);
3742 case CHILD_PROP_MENU_LABEL:
3743 label = gtk_notebook_get_menu_label (notebook, child);
3745 if (GTK_IS_LABEL (label))
3746 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
3748 g_value_set_string (value, NULL);
3750 case CHILD_PROP_POSITION:
3751 g_value_set_int (value, g_list_position (priv->children, list));
3753 case CHILD_PROP_TAB_EXPAND:
3754 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3755 &expand, NULL, NULL);
3756 g_value_set_boolean (value, expand);
3758 case CHILD_PROP_TAB_FILL:
3759 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3761 g_value_set_boolean (value, fill);
3763 case CHILD_PROP_TAB_PACK:
3764 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3765 NULL, NULL, &pack_type);
3766 g_value_set_enum (value, pack_type);
3768 case CHILD_PROP_REORDERABLE:
3769 g_value_set_boolean (value,
3770 gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
3772 case CHILD_PROP_DETACHABLE:
3773 g_value_set_boolean (value,
3774 gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
3777 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3783 gtk_notebook_add (GtkContainer *container,
3786 gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget,
3791 gtk_notebook_remove (GtkContainer *container,
3794 GtkNotebook *notebook = GTK_NOTEBOOK (container);
3795 GtkNotebookPrivate *priv = notebook->priv;
3796 GtkNotebookPage *page;
3800 children = priv->children;
3803 page = children->data;
3805 if (page->child == widget)
3809 children = children->next;
3812 if (children == NULL)
3815 g_object_ref (widget);
3817 gtk_notebook_real_remove (notebook, children);
3819 g_signal_emit (notebook,
3820 notebook_signals[PAGE_REMOVED],
3825 g_object_unref (widget);
3829 focus_tabs_in (GtkNotebook *notebook)
3831 GtkNotebookPrivate *priv = notebook->priv;
3833 if (priv->show_tabs && priv->cur_page)
3835 gtk_widget_grab_focus (GTK_WIDGET (notebook));
3837 gtk_notebook_switch_focus_tab (notebook,
3838 g_list_find (priv->children,
3848 focus_tabs_move (GtkNotebook *notebook,
3849 GtkDirectionType direction,
3850 gint search_direction)
3852 GtkNotebookPrivate *priv = notebook->priv;
3855 new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
3856 search_direction, TRUE);
3859 gboolean wrap_around;
3861 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
3862 "gtk-keynav-wrap-around", &wrap_around,
3866 new_page = gtk_notebook_search_page (notebook, NULL,
3867 search_direction, TRUE);
3871 gtk_notebook_switch_focus_tab (notebook, new_page);
3873 gtk_widget_error_bell (GTK_WIDGET (notebook));
3879 focus_child_in (GtkNotebook *notebook,
3880 GtkDirectionType direction)
3882 GtkNotebookPrivate *priv = notebook->priv;
3885 return gtk_widget_child_focus (priv->cur_page->child, direction);
3891 focus_action_in (GtkNotebook *notebook,
3893 GtkDirectionType direction)
3895 GtkNotebookPrivate *priv = notebook->priv;
3897 if (priv->action_widget[action] &&
3898 gtk_widget_get_visible (priv->action_widget[action]))
3899 return gtk_widget_child_focus (priv->action_widget[action], direction);
3904 /* Focus in the notebook can either be on the pages, or on
3905 * the tabs or on the action_widgets.
3908 gtk_notebook_focus (GtkWidget *widget,
3909 GtkDirectionType direction)
3911 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3912 GtkNotebookPrivate *priv = notebook->priv;
3913 GtkWidget *old_focus_child;
3914 GtkDirectionType effective_direction;
3918 gboolean widget_is_focus;
3919 GtkContainer *container;
3921 container = GTK_CONTAINER (widget);
3923 if (priv->tab_pos == GTK_POS_TOP ||
3924 priv->tab_pos == GTK_POS_LEFT)
3926 first_action = ACTION_WIDGET_START;
3927 last_action = ACTION_WIDGET_END;
3931 first_action = ACTION_WIDGET_END;
3932 last_action = ACTION_WIDGET_START;
3935 if (priv->focus_out)
3937 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
3941 widget_is_focus = gtk_widget_is_focus (widget);
3942 old_focus_child = gtk_container_get_focus_child (container);
3944 effective_direction = get_effective_direction (notebook, direction);
3946 if (old_focus_child) /* Focus on page child or action widget */
3948 if (gtk_widget_child_focus (old_focus_child, direction))
3951 if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
3953 switch (effective_direction)
3956 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3958 return focus_tabs_in (notebook);
3966 case GTK_DIR_TAB_FORWARD:
3967 if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
3968 focus_child_in (notebook, direction))
3970 return focus_tabs_in (notebook);
3971 case GTK_DIR_TAB_BACKWARD:
3974 g_assert_not_reached ();
3978 else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
3980 switch (effective_direction)
3983 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3987 return focus_tabs_in (notebook);
3993 case GTK_DIR_TAB_FORWARD:
3995 case GTK_DIR_TAB_BACKWARD:
3996 if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
3997 focus_child_in (notebook, direction))
3999 return focus_tabs_in (notebook);
4001 g_assert_not_reached ();
4007 switch (effective_direction)
4009 case GTK_DIR_TAB_BACKWARD:
4011 /* Focus onto the tabs */
4012 return focus_tabs_in (notebook);
4017 case GTK_DIR_TAB_FORWARD:
4018 return focus_action_in (notebook, last_action, direction);
4022 else if (widget_is_focus) /* Focus was on tabs */
4024 switch (effective_direction)
4026 case GTK_DIR_TAB_BACKWARD:
4027 return focus_action_in (notebook, first_action, direction);
4030 case GTK_DIR_TAB_FORWARD:
4031 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4033 return focus_action_in (notebook, last_action, direction);
4035 /* We use TAB_FORWARD rather than direction so that we focus a more
4036 * predictable widget for the user; users may be using arrow focusing
4037 * in this situation even if they don't usually use arrow focusing.
4039 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4041 return focus_tabs_move (notebook, direction, STEP_PREV);
4043 return focus_tabs_move (notebook, direction, STEP_NEXT);
4046 else /* Focus was not on widget */
4048 switch (effective_direction)
4050 case GTK_DIR_TAB_FORWARD:
4052 if (focus_action_in (notebook, first_action, direction))
4054 if (focus_tabs_in (notebook))
4056 if (focus_action_in (notebook, last_action, direction))
4058 if (focus_child_in (notebook, direction))
4061 case GTK_DIR_TAB_BACKWARD:
4062 if (focus_action_in (notebook, last_action, direction))
4064 if (focus_child_in (notebook, direction))
4066 if (focus_tabs_in (notebook))
4068 if (focus_action_in (notebook, first_action, direction))
4073 return focus_child_in (notebook, direction);
4077 g_assert_not_reached ();
4082 gtk_notebook_set_focus_child (GtkContainer *container,
4085 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4086 GtkNotebookPrivate *priv = notebook->priv;
4087 GtkWidget *page_child;
4088 GtkWidget *toplevel;
4090 /* If the old focus widget was within a page of the notebook,
4091 * (child may either be NULL or not in this case), record it
4092 * for future use if we switch to the page with a mnemonic.
4095 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4096 if (toplevel && gtk_widget_is_toplevel (toplevel))
4098 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4101 if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4103 GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4106 GtkNotebookPage *page = list->data;
4108 if (page->last_focus_child)
4109 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4111 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4112 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4118 page_child = gtk_widget_get_parent (page_child);
4124 g_return_if_fail (GTK_IS_WIDGET (child));
4126 priv->child_has_focus = TRUE;
4127 if (!priv->focus_tab)
4130 GtkNotebookPage *page;
4132 children = priv->children;
4135 page = children->data;
4136 if (page->child == child || page->tab_label == child)
4137 gtk_notebook_switch_focus_tab (notebook, children);
4138 children = children->next;
4143 priv->child_has_focus = FALSE;
4145 GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4149 gtk_notebook_forall (GtkContainer *container,
4150 gboolean include_internals,
4151 GtkCallback callback,
4152 gpointer callback_data)
4154 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4155 GtkNotebookPrivate *priv = notebook->priv;
4159 children = priv->children;
4162 GtkNotebookPage *page;
4164 page = children->data;
4165 children = children->next;
4166 (* callback) (page->child, callback_data);
4168 if (include_internals)
4170 if (page->tab_label)
4171 (* callback) (page->tab_label, callback_data);
4175 if (include_internals) {
4176 for (i = 0; i < N_ACTION_WIDGETS; i++)
4178 if (priv->action_widget[i])
4179 (* callback) (priv->action_widget[i], callback_data);
4185 gtk_notebook_child_type (GtkContainer *container)
4187 return GTK_TYPE_WIDGET;
4190 /* Private GtkNotebook Methods:
4192 * gtk_notebook_real_insert_page
4195 page_visible_cb (GtkWidget *page,
4199 GtkNotebook *notebook = GTK_NOTEBOOK (data);
4200 GtkNotebookPrivate *priv = notebook->priv;
4204 if (priv->cur_page &&
4205 priv->cur_page->child == page &&
4206 !gtk_widget_get_visible (page))
4208 list = g_list_find (priv->children, priv->cur_page);
4211 next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4213 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4217 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4222 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4224 GtkWidget *tab_label,
4225 GtkWidget *menu_label,
4228 GtkNotebookPrivate *priv = notebook->priv;
4229 GtkNotebookPage *page;
4232 gtk_widget_freeze_child_notify (child);
4234 page = g_slice_new0 (GtkNotebookPage);
4235 page->child = child;
4237 nchildren = g_list_length (priv->children);
4238 if ((position < 0) || (position > nchildren))
4239 position = nchildren;
4241 priv->children = g_list_insert (priv->children, page, position);
4245 page->default_tab = TRUE;
4246 if (priv->show_tabs)
4247 tab_label = gtk_label_new (NULL);
4249 page->tab_label = tab_label;
4250 page->menu_label = menu_label;
4251 page->expand = FALSE;
4253 page->pack = GTK_PACK_START;
4256 page->default_menu = TRUE;
4258 g_object_ref_sink (page->menu_label);
4261 gtk_notebook_menu_item_create (notebook,
4262 g_list_find (priv->children, page));
4264 gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4266 gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4268 gtk_notebook_update_labels (notebook);
4270 if (!priv->first_tab)
4271 priv->first_tab = priv->children;
4273 /* child visible will be turned on by switch_page below */
4274 if (priv->cur_page != page)
4275 gtk_widget_set_child_visible (child, FALSE);
4279 if (priv->show_tabs && gtk_widget_get_visible (child))
4280 gtk_widget_show (tab_label);
4282 gtk_widget_hide (tab_label);
4284 page->mnemonic_activate_signal =
4285 g_signal_connect (tab_label,
4286 "mnemonic-activate",
4287 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4291 page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4292 G_CALLBACK (page_visible_cb), notebook);
4294 g_signal_emit (notebook,
4295 notebook_signals[PAGE_ADDED],
4300 if (!priv->cur_page)
4302 gtk_notebook_switch_page (notebook, page);
4303 /* focus_tab is set in the switch_page method */
4304 gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4307 gtk_notebook_update_tab_states (notebook);
4309 if (priv->scrollable)
4310 gtk_notebook_redraw_arrows (notebook);
4312 gtk_widget_child_notify (child, "tab-expand");
4313 gtk_widget_child_notify (child, "tab-fill");
4314 gtk_widget_child_notify (child, "tab-pack");
4315 gtk_widget_child_notify (child, "tab-label");
4316 gtk_widget_child_notify (child, "menu-label");
4317 gtk_widget_child_notify (child, "position");
4318 gtk_widget_thaw_child_notify (child);
4320 /* The page-added handler might have reordered the pages, re-get the position */
4321 return gtk_notebook_page_num (notebook, child);
4324 /* Private GtkNotebook Functions:
4326 * gtk_notebook_redraw_tabs
4327 * gtk_notebook_real_remove
4328 * gtk_notebook_update_labels
4329 * gtk_notebook_timer
4330 * gtk_notebook_set_scroll_timer
4331 * gtk_notebook_page_compare
4332 * gtk_notebook_real_page_position
4333 * gtk_notebook_search_page
4336 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4338 GtkNotebookPrivate *priv = notebook->priv;
4339 GtkAllocation allocation;
4341 GtkNotebookPage *page;
4343 GdkRectangle redraw_rect;
4345 gint tab_pos = get_effective_tab_pos (notebook);
4347 widget = GTK_WIDGET (notebook);
4348 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4350 if (!gtk_widget_get_mapped (widget) || !priv->first_tab)
4353 page = priv->first_tab->data;
4355 redraw_rect.x = border;
4356 redraw_rect.y = border;
4358 style = gtk_widget_get_style (widget);
4359 gtk_widget_get_allocation (widget, &allocation);
4363 case GTK_POS_BOTTOM:
4364 redraw_rect.y = allocation.height - border -
4365 page->allocation.height - style->ythickness;
4367 if (page != priv->cur_page)
4368 redraw_rect.y -= style->ythickness;
4371 redraw_rect.width = allocation.width - 2 * border;
4372 redraw_rect.height = page->allocation.height + style->ythickness;
4374 if (page != priv->cur_page)
4375 redraw_rect.height += style->ythickness;
4378 redraw_rect.x = allocation.width - border -
4379 page->allocation.width - style->xthickness;
4381 if (page != priv->cur_page)
4382 redraw_rect.x -= style->xthickness;
4385 redraw_rect.width = page->allocation.width + style->xthickness;
4386 redraw_rect.height = allocation.height - 2 * border;
4388 if (page != priv->cur_page)
4389 redraw_rect.width += style->xthickness;
4393 redraw_rect.x += allocation.x;
4394 redraw_rect.y += allocation.y;
4396 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4397 &redraw_rect, TRUE);
4401 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4403 GtkNotebookPrivate *priv = notebook->priv;
4405 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4406 gtk_notebook_show_arrows (notebook))
4410 GtkNotebookArrow arrow[4];
4412 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4413 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4414 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4415 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4417 for (i = 0; i < 4; i++)
4419 if (arrow[i] == ARROW_NONE)
4422 gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4423 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4430 gtk_notebook_timer (GtkNotebook *notebook)
4432 GtkNotebookPrivate *priv = notebook->priv;
4433 gboolean retval = FALSE;
4437 gtk_notebook_do_arrow (notebook, priv->click_child);
4439 if (priv->need_timer)
4441 GtkSettings *settings;
4444 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4445 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4447 priv->need_timer = FALSE;
4448 priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4449 (GSourceFunc) gtk_notebook_timer,
4450 (gpointer) notebook);
4460 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4462 GtkNotebookPrivate *priv = notebook->priv;
4463 GtkWidget *widget = GTK_WIDGET (notebook);
4467 GtkSettings *settings = gtk_widget_get_settings (widget);
4470 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4472 priv->timer = gdk_threads_add_timeout (timeout,
4473 (GSourceFunc) gtk_notebook_timer,
4474 (gpointer) notebook);
4475 priv->need_timer = TRUE;
4480 gtk_notebook_page_compare (gconstpointer a,
4483 return (((GtkNotebookPage *) a)->child != b);
4487 gtk_notebook_find_child (GtkNotebook *notebook,
4489 const gchar *function)
4491 GtkNotebookPrivate *priv = notebook->priv;
4492 GList *list = g_list_find_custom (priv->children, child,
4493 gtk_notebook_page_compare);
4495 #ifndef G_DISABLE_CHECKS
4496 if (!list && function)
4497 g_warning ("%s: unable to find child %p in notebook %p",
4498 function, child, notebook);
4505 gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4506 GtkNotebookPage *page)
4508 if (page->tab_label)
4510 if (page->mnemonic_activate_signal)
4511 g_signal_handler_disconnect (page->tab_label,
4512 page->mnemonic_activate_signal);
4513 page->mnemonic_activate_signal = 0;
4515 gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
4516 gtk_widget_unparent (page->tab_label);
4517 page->tab_label = NULL;
4522 gtk_notebook_real_remove (GtkNotebook *notebook,
4525 GtkNotebookPrivate *priv = notebook->priv;
4526 GtkNotebookPage *page;
4528 gint need_resize = FALSE;
4529 GtkWidget *tab_label;
4530 gboolean destroying;
4532 destroying = gtk_widget_in_destruction (notebook);
4534 next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4536 next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4538 priv->children = g_list_remove_link (priv->children, list);
4540 if (priv->cur_page == list->data)
4542 priv->cur_page = NULL;
4543 if (next_list && !destroying)
4544 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4547 if (priv->detached_tab == list->data)
4548 priv->detached_tab = NULL;
4550 if (list == priv->first_tab)
4551 priv->first_tab = next_list;
4552 if (list == priv->focus_tab && !destroying)
4553 gtk_notebook_switch_focus_tab (notebook, next_list);
4557 g_signal_handler_disconnect (page->child, page->notify_visible_handler);
4559 if (gtk_widget_get_visible (page->child) &&
4560 gtk_widget_get_visible (GTK_WIDGET (notebook)))
4563 gtk_widget_unparent (page->child);
4565 tab_label = page->tab_label;
4568 g_object_ref (tab_label);
4569 gtk_notebook_remove_tab_label (notebook, page);
4571 gtk_widget_destroy (tab_label);
4572 g_object_unref (tab_label);
4577 GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
4579 gtk_notebook_menu_label_unparent (parent, NULL);
4580 gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
4582 gtk_widget_queue_resize (priv->menu);
4584 if (!page->default_menu)
4585 g_object_unref (page->menu_label);
4589 if (page->last_focus_child)
4591 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4592 page->last_focus_child = NULL;
4595 g_slice_free (GtkNotebookPage, page);
4597 gtk_notebook_update_labels (notebook);
4599 gtk_widget_queue_resize (GTK_WIDGET (notebook));
4603 gtk_notebook_update_labels (GtkNotebook *notebook)
4605 GtkNotebookPrivate *priv = notebook->priv;
4606 GtkNotebookPage *page;
4611 if (!priv->show_tabs && !priv->menu)
4614 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
4616 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
4619 g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
4620 if (priv->show_tabs)
4622 if (page->default_tab)
4624 if (!page->tab_label)
4626 page->tab_label = gtk_label_new (string);
4627 gtk_widget_set_parent (page->tab_label,
4628 GTK_WIDGET (notebook));
4631 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
4634 if (gtk_widget_get_visible (page->child) &&
4635 !gtk_widget_get_visible (page->tab_label))
4636 gtk_widget_show (page->tab_label);
4637 else if (!gtk_widget_get_visible (page->child) &&
4638 gtk_widget_get_visible (page->tab_label))
4639 gtk_widget_hide (page->tab_label);
4641 if (priv->menu && page->default_menu)
4643 if (GTK_IS_LABEL (page->tab_label))
4644 gtk_label_set_text (GTK_LABEL (page->menu_label),
4645 gtk_label_get_label (GTK_LABEL (page->tab_label)));
4647 gtk_label_set_text (GTK_LABEL (page->menu_label), string);
4653 gtk_notebook_real_page_position (GtkNotebook *notebook,
4656 GtkNotebookPrivate *priv = notebook->priv;
4660 for (work = priv->children, count_start = 0;
4661 work && work != list; work = work->next)
4662 if (GTK_NOTEBOOK_PAGE (work)->pack == GTK_PACK_START)
4668 if (GTK_NOTEBOOK_PAGE (list)->pack == GTK_PACK_START)
4671 return (count_start + g_list_length (list) - 1);
4675 gtk_notebook_search_page (GtkNotebook *notebook,
4678 gboolean find_visible)
4680 GtkNotebookPrivate *priv = notebook->priv;
4681 GtkNotebookPage *page = NULL;
4682 GList *old_list = NULL;
4688 flag = GTK_PACK_END;
4692 flag = GTK_PACK_START;
4699 if (!page || page->pack == flag)
4707 list = priv->children;
4712 if (page->pack == flag &&
4714 (gtk_widget_get_visible (page->child) &&
4715 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4730 if (page->pack != flag &&
4732 (gtk_widget_get_visible (page->child) &&
4733 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4741 /* Private GtkNotebook Drawing Functions:
4743 * gtk_notebook_paint
4744 * gtk_notebook_draw_tab
4745 * gtk_notebook_draw_arrow
4748 gtk_notebook_paint (GtkWidget *widget,
4751 GtkNotebook *notebook;
4752 GtkNotebookPrivate *priv;
4753 GtkNotebookPage *page;
4754 GtkAllocation allocation;
4759 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
4760 gint gap_x = 0, gap_width = 0, step = STEP_PREV;
4764 notebook = GTK_NOTEBOOK (widget);
4765 priv = notebook->priv;
4766 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4767 tab_pos = get_effective_tab_pos (notebook);
4769 if ((!priv->show_tabs && !priv->show_border) ||
4770 !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
4773 gtk_widget_get_allocation (widget, &allocation);
4775 x = allocation.x + border_width;
4776 y = allocation.y + border_width;
4777 width = allocation.width - border_width * 2;
4778 height = allocation.height - border_width * 2;
4780 if (priv->show_border && (!priv->show_tabs || !priv->children))
4782 gtk_paint_box (gtk_widget_get_style (widget), cr,
4783 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4785 x, y, width, height);
4789 if (!priv->first_tab)
4790 priv->first_tab = priv->children;
4792 if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
4793 page = GTK_NOTEBOOK_PAGE (priv->first_tab);
4795 page = priv->cur_page;
4800 y += page->allocation.height;
4802 case GTK_POS_BOTTOM:
4803 height -= page->allocation.height;
4806 x += page->allocation.width;
4809 width -= page->allocation.width;
4813 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
4814 !gtk_widget_get_mapped (priv->cur_page->tab_label))
4824 case GTK_POS_BOTTOM:
4825 if (priv->operation == DRAG_OPERATION_REORDER)
4826 gap_x = priv->drag_window_x - allocation.x - border_width;
4828 gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
4830 gap_width = priv->cur_page->allocation.width;
4831 step = is_rtl ? STEP_NEXT : STEP_PREV;
4835 if (priv->operation == DRAG_OPERATION_REORDER)
4836 gap_x = priv->drag_window_y - border_width - allocation.y;
4838 gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
4840 gap_width = priv->cur_page->allocation.height;
4845 gtk_paint_box_gap (gtk_widget_get_style (widget), cr,
4846 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4848 x, y, width, height,
4849 tab_pos, gap_x, gap_width);
4852 children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
4855 page = children->data;
4856 children = gtk_notebook_search_page (notebook, children,
4858 if (!gtk_widget_get_visible (page->child))
4860 if (!gtk_widget_get_mapped (page->tab_label))
4862 else if (page != priv->cur_page)
4863 gtk_notebook_draw_tab (notebook, page, cr);
4866 if (showarrow && priv->scrollable)
4868 if (priv->has_before_previous)
4869 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
4870 if (priv->has_before_next)
4871 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
4872 if (priv->has_after_previous)
4873 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
4874 if (priv->has_after_next)
4875 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
4878 if (priv->operation != DRAG_OPERATION_REORDER)
4879 gtk_notebook_draw_tab (notebook, priv->cur_page, cr);
4883 gtk_notebook_draw_tab (GtkNotebook *notebook,
4884 GtkNotebookPage *page,
4887 GtkNotebookPrivate *priv;
4888 GtkStateType state_type;
4891 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
4892 !gtk_widget_get_mapped (page->tab_label) ||
4893 (page->allocation.width == 0) || (page->allocation.height == 0))
4896 widget = GTK_WIDGET (notebook);
4897 priv = notebook->priv;
4899 if (priv->cur_page == page)
4900 state_type = GTK_STATE_NORMAL;
4902 state_type = GTK_STATE_ACTIVE;
4904 gtk_paint_extension (gtk_widget_get_style (widget), cr,
4905 state_type, GTK_SHADOW_OUT,
4909 page->allocation.width,
4910 page->allocation.height,
4911 get_tab_gap_pos (notebook));
4913 if (gtk_widget_has_focus (widget) &&
4914 priv->cur_page == page)
4917 GtkAllocation allocation;
4919 gtk_widget_get_allocation (page->tab_label, &allocation);
4920 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
4922 gtk_paint_focus (gtk_widget_get_style (widget), cr,
4923 gtk_widget_get_state (widget), widget, "tab",
4924 allocation.x - focus_width,
4925 allocation.y - focus_width,
4926 allocation.width + 2 * focus_width,
4927 allocation.height + 2 * focus_width);
4932 gtk_notebook_draw_arrow (GtkNotebook *notebook,
4934 GtkNotebookArrow nbarrow)
4936 GtkNotebookPrivate *priv = notebook->priv;
4937 GtkStateType state_type;
4938 GtkShadowType shadow_type;
4940 GdkRectangle arrow_rect;
4942 gboolean is_rtl, left;
4943 gint scroll_arrow_hlength;
4944 gint scroll_arrow_vlength;
4947 widget = GTK_WIDGET (notebook);
4949 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
4951 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4952 left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
4953 (!ARROW_IS_LEFT (nbarrow) && is_rtl);
4955 gtk_widget_style_get (widget,
4956 "scroll-arrow-hlength", &scroll_arrow_hlength,
4957 "scroll-arrow-vlength", &scroll_arrow_vlength,
4960 if (priv->in_child == nbarrow)
4962 if (priv->click_child == nbarrow)
4963 state_type = GTK_STATE_ACTIVE;
4965 state_type = GTK_STATE_PRELIGHT;
4968 state_type = gtk_widget_get_state (widget);
4970 if (priv->click_child == nbarrow)
4971 shadow_type = GTK_SHADOW_IN;
4973 shadow_type = GTK_SHADOW_OUT;
4975 if (priv->focus_tab &&
4976 !gtk_notebook_search_page (notebook, priv->focus_tab,
4977 left ? STEP_PREV : STEP_NEXT, TRUE))
4979 shadow_type = GTK_SHADOW_ETCHED_IN;
4980 state_type = GTK_STATE_INSENSITIVE;
4983 if (priv->tab_pos == GTK_POS_LEFT ||
4984 priv->tab_pos == GTK_POS_RIGHT)
4986 arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_UP : GTK_ARROW_DOWN);
4987 arrow_size = scroll_arrow_vlength;
4991 arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT);
4992 arrow_size = scroll_arrow_hlength;
4995 gtk_paint_arrow (gtk_widget_get_style (widget),
4997 shadow_type, widget, "notebook",
4998 arrow, TRUE, arrow_rect.x, arrow_rect.y,
4999 arrow_size, arrow_size);
5002 /* Private GtkNotebook Size Allocate Functions:
5004 * gtk_notebook_tab_space
5005 * gtk_notebook_calculate_shown_tabs
5006 * gtk_notebook_calculate_tabs_allocation
5007 * gtk_notebook_pages_allocate
5008 * gtk_notebook_page_allocate
5009 * gtk_notebook_calc_tabs
5012 gtk_notebook_tab_space (GtkNotebook *notebook,
5013 gboolean *show_arrows,
5018 GtkNotebookPrivate *priv = notebook->priv;
5019 GtkAllocation allocation, action_allocation;
5023 gint tab_pos = get_effective_tab_pos (notebook);
5026 gint scroll_arrow_hlength;
5027 gint scroll_arrow_vlength;
5032 widget = GTK_WIDGET (notebook);
5033 children = priv->children;
5034 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5036 style = gtk_widget_get_style (widget);
5038 gtk_widget_style_get (GTK_WIDGET (notebook),
5039 "arrow-spacing", &arrow_spacing,
5040 "scroll-arrow-hlength", &scroll_arrow_hlength,
5041 "scroll-arrow-vlength", &scroll_arrow_vlength,
5044 border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5046 gtk_widget_get_allocation (widget, &allocation);
5051 case GTK_POS_BOTTOM:
5052 *min = allocation.x + border_width;
5053 *max = allocation.x + allocation.width - border_width;
5055 for (i = 0; i < N_ACTION_WIDGETS; i++)
5057 if (priv->action_widget[i])
5059 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5061 if ((i == ACTION_WIDGET_START && !is_rtl) ||
5062 (i == ACTION_WIDGET_END && is_rtl))
5063 *min += action_allocation.width + style->xthickness;
5065 *max -= action_allocation.width + style->xthickness;
5071 GtkNotebookPage *page;
5073 page = children->data;
5074 children = children->next;
5076 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5077 gtk_widget_get_visible (page->child))
5078 *tab_space += page->requisition.width;
5083 *min = allocation.y + border_width;
5084 *max = allocation.y + allocation.height - border_width;
5086 for (i = 0; i < N_ACTION_WIDGETS; i++)
5088 if (priv->action_widget[i])
5090 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5092 if (i == ACTION_WIDGET_START)
5093 *min += action_allocation.height + style->ythickness;
5095 *max -= action_allocation.height + style->ythickness;
5101 GtkNotebookPage *page;
5103 page = children->data;
5104 children = children->next;
5106 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5107 gtk_widget_get_visible (page->child))
5108 *tab_space += page->requisition.height;
5113 if (!priv->scrollable)
5114 *show_arrows = FALSE;
5117 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5122 case GTK_POS_BOTTOM:
5123 if (*tab_space > *max - *min - tab_overlap)
5125 *show_arrows = TRUE;
5127 /* take arrows into account */
5128 *tab_space = *max - *min - tab_overlap;
5130 if (priv->has_after_previous)
5132 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5133 *max -= arrow_spacing + scroll_arrow_hlength;
5136 if (priv->has_after_next)
5138 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5139 *max -= arrow_spacing + scroll_arrow_hlength;
5142 if (priv->has_before_previous)
5144 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5145 *min += arrow_spacing + scroll_arrow_hlength;
5148 if (priv->has_before_next)
5150 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5151 *min += arrow_spacing + scroll_arrow_hlength;
5157 if (*tab_space > *max - *min - tab_overlap)
5159 *show_arrows = TRUE;
5161 /* take arrows into account */
5162 *tab_space = *max - *min - tab_overlap;
5164 if (priv->has_after_previous || priv->has_after_next)
5166 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5167 *max -= arrow_spacing + scroll_arrow_vlength;
5170 if (priv->has_before_previous || priv->has_before_next)
5172 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5173 *min += arrow_spacing + scroll_arrow_vlength;
5182 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5183 gboolean show_arrows,
5189 gint *remaining_space)
5191 GtkNotebookPrivate *priv = notebook->priv;
5193 GtkContainer *container;
5195 GtkNotebookPage *page;
5196 gint tab_pos, tab_overlap;
5198 widget = GTK_WIDGET (notebook);
5199 container = GTK_CONTAINER (notebook);
5200 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5201 tab_pos = get_effective_tab_pos (notebook);
5203 if (show_arrows) /* first_tab <- focus_tab */
5205 *remaining_space = tab_space;
5207 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5208 gtk_widget_get_visible (priv->cur_page->child))
5210 gtk_notebook_calc_tabs (notebook,
5213 remaining_space, STEP_NEXT);
5216 if (tab_space <= 0 || *remaining_space <= 0)
5219 priv->first_tab = priv->focus_tab;
5220 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5222 page = priv->first_tab->data;
5223 *remaining_space = tab_space - page->requisition.width;
5230 if (priv->first_tab && priv->first_tab != priv->focus_tab)
5232 /* Is first_tab really predecessor of focus_tab? */
5233 page = priv->first_tab->data;
5234 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5235 gtk_widget_get_visible (page->child))
5236 for (children = priv->focus_tab;
5237 children && children != priv->first_tab;
5238 children = gtk_notebook_search_page (notebook,
5246 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5247 priv->first_tab = priv->focus_tab;
5249 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5253 /* calculate shown tabs counting backwards from the focus tab */
5254 gtk_notebook_calc_tabs (notebook,
5255 gtk_notebook_search_page (notebook,
5259 &(priv->first_tab), remaining_space,
5262 if (*remaining_space < 0)
5265 gtk_notebook_search_page (notebook, priv->first_tab,
5267 if (!priv->first_tab)
5268 priv->first_tab = priv->focus_tab;
5270 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5273 else /* focus_tab -> end */
5275 if (!priv->first_tab)
5276 priv->first_tab = gtk_notebook_search_page (notebook,
5281 gtk_notebook_calc_tabs (notebook,
5282 gtk_notebook_search_page (notebook,
5286 &children, remaining_space, STEP_NEXT);
5288 if (*remaining_space <= 0)
5289 *last_child = children;
5290 else /* start <- first_tab */
5295 gtk_notebook_calc_tabs (notebook,
5296 gtk_notebook_search_page (notebook,
5300 &children, remaining_space, STEP_PREV);
5302 if (*remaining_space == 0)
5303 priv->first_tab = children;
5305 priv->first_tab = gtk_notebook_search_page(notebook,
5312 if (*remaining_space < 0)
5314 /* calculate number of tabs */
5315 *remaining_space = - (*remaining_space);
5318 for (children = priv->first_tab;
5319 children && children != *last_child;
5320 children = gtk_notebook_search_page (notebook, children,
5325 *remaining_space = 0;
5328 /* unmap all non-visible tabs */
5329 for (children = gtk_notebook_search_page (notebook, NULL,
5331 children && children != priv->first_tab;
5332 children = gtk_notebook_search_page (notebook, children,
5335 page = children->data;
5337 if (page->tab_label &&
5338 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5339 gtk_widget_set_child_visible (page->tab_label, FALSE);
5342 for (children = *last_child; children;
5343 children = gtk_notebook_search_page (notebook, children,
5346 page = children->data;
5348 if (page->tab_label &&
5349 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5350 gtk_widget_set_child_visible (page->tab_label, FALSE);
5353 else /* !show_arrows */
5358 *remaining_space = max - min - tab_overlap - tab_space;
5359 children = priv->children;
5360 priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5364 page = children->data;
5365 children = children->next;
5367 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5368 !gtk_widget_get_visible (page->child))
5377 /* if notebook is homogeneous, all tabs are expanded */
5378 if (priv->homogeneous && *n)
5384 get_allocate_at_bottom (GtkWidget *widget,
5385 gint search_direction)
5387 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5388 gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5393 case GTK_POS_BOTTOM:
5395 return (search_direction == STEP_PREV);
5397 return (search_direction == STEP_NEXT);
5402 return (search_direction == STEP_PREV);
5410 gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5415 gint *remaining_space,
5416 gint *expanded_tabs,
5420 GtkNotebookPrivate *priv = notebook->priv;
5421 GtkAllocation allocation;
5423 GtkContainer *container;
5424 GtkNotebookPage *page;
5426 gboolean allocate_at_bottom;
5427 gint tab_overlap, tab_pos, tab_extra_space;
5428 gint left_x, right_x, top_y, bottom_y, anchor;
5429 gint xthickness, ythickness;
5431 gboolean gap_left, packing_changed;
5432 GtkAllocation child_allocation = { 0, };
5434 widget = GTK_WIDGET (notebook);
5435 container = GTK_CONTAINER (notebook);
5436 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5437 tab_pos = get_effective_tab_pos (notebook);
5438 allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5441 gtk_widget_get_allocation (widget, &allocation);
5443 border_width = gtk_container_get_border_width (container);
5444 child_allocation.x = allocation.x + border_width;
5445 child_allocation.y = allocation.y + border_width;
5447 style = gtk_widget_get_style (widget);
5448 xthickness = style->xthickness;
5449 ythickness = style->ythickness;
5453 case GTK_POS_BOTTOM:
5454 child_allocation.y = allocation.y + allocation.height -
5455 priv->cur_page->requisition.height - border_width;
5458 child_allocation.x = (allocate_at_bottom) ? max : min;
5459 child_allocation.height = priv->cur_page->requisition.height;
5460 anchor = child_allocation.x;
5464 child_allocation.x = allocation.x + allocation.width -
5465 priv->cur_page->requisition.width - border_width;
5468 child_allocation.y = (allocate_at_bottom) ? max : min;
5469 child_allocation.width = priv->cur_page->requisition.width;
5470 anchor = child_allocation.y;
5474 left_x = CLAMP (priv->mouse_x - priv->drag_offset_x,
5475 min, max - priv->cur_page->allocation.width);
5476 top_y = CLAMP (priv->mouse_y - priv->drag_offset_y,
5477 min, max - priv->cur_page->allocation.height);
5478 right_x = left_x + priv->cur_page->allocation.width;
5479 bottom_y = top_y + priv->cur_page->allocation.height;
5480 gap_left = packing_changed = FALSE;
5482 while (*children && *children != last_child)
5484 page = (*children)->data;
5486 if (direction == STEP_NEXT && page->pack != GTK_PACK_START)
5490 else if (priv->operation == DRAG_OPERATION_REORDER)
5491 packing_changed = TRUE;
5494 if (direction == STEP_NEXT)
5495 *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5498 *children = (*children)->next;
5500 if (page->pack != GTK_PACK_END || !gtk_widget_get_visible (page->child))
5504 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5507 tab_extra_space = 0;
5508 if (*expanded_tabs && (showarrow || page->expand || priv->homogeneous))
5510 tab_extra_space = *remaining_space / *expanded_tabs;
5511 *remaining_space -= tab_extra_space;
5518 case GTK_POS_BOTTOM:
5519 child_allocation.width = page->requisition.width + tab_overlap + tab_extra_space;
5521 /* make sure that the reordered tab doesn't go past the last position */
5522 if (priv->operation == DRAG_OPERATION_REORDER &&
5523 !gap_left && packing_changed)
5525 if (!allocate_at_bottom)
5527 if ((priv->cur_page->pack == GTK_PACK_START && left_x >= anchor) ||
5528 (priv->cur_page->pack == GTK_PACK_END && left_x < anchor))
5530 left_x = priv->drag_window_x = anchor;
5531 anchor += priv->cur_page->allocation.width - tab_overlap;
5536 if ((priv->cur_page->pack == GTK_PACK_START && right_x <= anchor) ||
5537 (priv->cur_page->pack == GTK_PACK_END && right_x > anchor))
5539 anchor -= priv->cur_page->allocation.width;
5540 left_x = priv->drag_window_x = anchor;
5541 anchor += tab_overlap;
5548 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5550 priv->drag_window_x = left_x;
5551 priv->drag_window_y = child_allocation.y;
5555 if (allocate_at_bottom)
5556 anchor -= child_allocation.width;
5558 if (priv->operation == DRAG_OPERATION_REORDER && page->pack == priv->cur_page->pack)
5560 if (!allocate_at_bottom &&
5562 left_x <= anchor + child_allocation.width / 2)
5563 anchor += priv->cur_page->allocation.width - tab_overlap;
5564 else if (allocate_at_bottom &&
5565 right_x >= anchor + child_allocation.width / 2 &&
5566 right_x <= anchor + child_allocation.width)
5567 anchor -= priv->cur_page->allocation.width - tab_overlap;
5570 child_allocation.x = anchor;
5576 child_allocation.height = page->requisition.height + tab_overlap + tab_extra_space;
5578 /* make sure that the reordered tab doesn't go past the last position */
5579 if (priv->operation == DRAG_OPERATION_REORDER &&
5580 !gap_left && packing_changed)
5582 if (!allocate_at_bottom &&
5583 ((priv->cur_page->pack == GTK_PACK_START && top_y >= anchor) ||
5584 (priv->cur_page->pack == GTK_PACK_END && top_y < anchor)))
5586 top_y = priv->drag_window_y = anchor;
5587 anchor += priv->cur_page->allocation.height - tab_overlap;
5593 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5595 priv->drag_window_x = child_allocation.x;
5596 priv->drag_window_y = top_y;
5600 if (allocate_at_bottom)
5601 anchor -= child_allocation.height;
5603 if (priv->operation == DRAG_OPERATION_REORDER && page->pack == priv->cur_page->pack)
5605 if (!allocate_at_bottom &&
5607 top_y <= anchor + child_allocation.height / 2)
5608 anchor += priv->cur_page->allocation.height - tab_overlap;
5609 else if (allocate_at_bottom &&
5610 bottom_y >= anchor + child_allocation.height / 2 &&
5611 bottom_y <= anchor + child_allocation.height)
5612 anchor -= priv->cur_page->allocation.height - tab_overlap;
5615 child_allocation.y = anchor;
5621 page->allocation = child_allocation;
5623 if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
5624 (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
5626 /* needs to be allocated at 0,0
5627 * to be shown in the drag window */
5628 page->allocation.x = 0;
5629 page->allocation.y = 0;
5632 if (page != priv->cur_page)
5637 page->allocation.y += ythickness;
5639 case GTK_POS_BOTTOM:
5640 page->allocation.height = MAX (1, page->allocation.height - ythickness);
5643 page->allocation.x += xthickness;
5646 page->allocation.width = MAX (1, page->allocation.width - xthickness);
5651 /* calculate whether to leave a gap based on reorder operation or not */
5655 case GTK_POS_BOTTOM:
5656 if (priv->operation != DRAG_OPERATION_REORDER ||
5657 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
5659 if (priv->operation == DRAG_OPERATION_REORDER)
5661 if (page->pack == priv->cur_page->pack &&
5662 !allocate_at_bottom &&
5663 left_x > anchor + child_allocation.width / 2 &&
5664 left_x <= anchor + child_allocation.width)
5665 anchor += priv->cur_page->allocation.width - tab_overlap;
5666 else if (page->pack == priv->cur_page->pack &&
5667 allocate_at_bottom &&
5668 right_x >= anchor &&
5669 right_x <= anchor + child_allocation.width / 2)
5670 anchor -= priv->cur_page->allocation.width - tab_overlap;
5673 if (!allocate_at_bottom)
5674 anchor += child_allocation.width - tab_overlap;
5676 anchor += tab_overlap;
5682 if (priv->operation != DRAG_OPERATION_REORDER ||
5683 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
5685 if (priv->operation == DRAG_OPERATION_REORDER)
5687 if (page->pack == priv->cur_page->pack &&
5688 !allocate_at_bottom &&
5689 top_y >= anchor + child_allocation.height / 2 &&
5690 top_y <= anchor + child_allocation.height)
5691 anchor += priv->cur_page->allocation.height - tab_overlap;
5692 else if (page->pack == priv->cur_page->pack &&
5693 allocate_at_bottom &&
5694 bottom_y >= anchor &&
5695 bottom_y <= anchor + child_allocation.height / 2)
5696 anchor -= priv->cur_page->allocation.height - tab_overlap;
5699 if (!allocate_at_bottom)
5700 anchor += child_allocation.height - tab_overlap;
5702 anchor += tab_overlap;
5708 /* set child visible */
5709 if (page->tab_label)
5710 gtk_widget_set_child_visible (page->tab_label, TRUE);
5713 /* Don't move the current tab past the last position during tabs reordering */
5715 priv->operation == DRAG_OPERATION_REORDER &&
5716 ((direction == STEP_NEXT && priv->cur_page->pack == GTK_PACK_START) ||
5717 ((direction == STEP_PREV || packing_changed) && priv->cur_page->pack == GTK_PACK_END)))
5722 case GTK_POS_BOTTOM:
5723 if (allocate_at_bottom)
5724 anchor -= priv->cur_page->allocation.width;
5726 if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
5727 (allocate_at_bottom && priv->drag_window_x < anchor))
5728 priv->drag_window_x = anchor;
5732 if (allocate_at_bottom)
5733 anchor -= priv->cur_page->allocation.height;
5735 if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
5736 (allocate_at_bottom && priv->drag_window_y < anchor))
5737 priv->drag_window_y = anchor;
5744 gtk_notebook_pages_allocate (GtkNotebook *notebook)
5746 GtkNotebookPrivate *priv = notebook->priv;
5747 GList *children = NULL;
5748 GList *last_child = NULL;
5749 gboolean showarrow = FALSE;
5750 gint tab_space, min, max, remaining_space;
5752 gboolean tab_allocations_changed = FALSE;
5754 if (!priv->show_tabs || !priv->children || !priv->cur_page)
5757 min = max = tab_space = remaining_space = 0;
5760 gtk_notebook_tab_space (notebook, &showarrow,
5761 &min, &max, &tab_space);
5763 gtk_notebook_calculate_shown_tabs (notebook, showarrow,
5764 min, max, tab_space, &last_child,
5765 &expanded_tabs, &remaining_space);
5767 children = priv->first_tab;
5768 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5769 showarrow, STEP_NEXT,
5770 &remaining_space, &expanded_tabs, min, max);
5771 if (children && children != last_child)
5773 children = priv->children;
5774 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5775 showarrow, STEP_PREV,
5776 &remaining_space, &expanded_tabs, min, max);
5779 children = priv->children;
5783 if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
5784 tab_allocations_changed = TRUE;
5785 children = children->next;
5788 if (!priv->first_tab)
5789 priv->first_tab = priv->children;
5791 if (tab_allocations_changed)
5792 gtk_notebook_redraw_tabs (notebook);
5796 gtk_notebook_page_allocate (GtkNotebook *notebook,
5797 GtkNotebookPage *page)
5799 GtkWidget *widget = GTK_WIDGET (notebook);
5800 GtkNotebookPrivate *priv = notebook->priv;
5801 GtkAllocation child_allocation, label_allocation;
5802 GtkRequisition tab_requisition;
5809 gint tab_pos = get_effective_tab_pos (notebook);
5810 gboolean tab_allocation_changed;
5811 gboolean was_visible = page->tab_allocated_visible;
5813 if (!page->tab_label ||
5814 !gtk_widget_get_visible (page->tab_label) ||
5815 !gtk_widget_get_child_visible (page->tab_label))
5817 page->tab_allocated_visible = FALSE;
5821 style = gtk_widget_get_style (widget);
5822 xthickness = style->xthickness;
5823 ythickness = style->ythickness;
5825 gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
5826 gtk_widget_style_get (widget,
5827 "focus-line-width", &focus_width,
5828 "tab-curvature", &tab_curvature,
5833 case GTK_POS_BOTTOM:
5834 padding = tab_curvature + focus_width + priv->tab_hborder;
5837 child_allocation.x = xthickness + focus_width + priv->tab_hborder;
5838 child_allocation.width = MAX (1, page->allocation.width - 2 * child_allocation.x);
5839 child_allocation.x += page->allocation.x;
5843 child_allocation.x = page->allocation.x +
5844 (page->allocation.width - tab_requisition.width) / 2;
5846 child_allocation.width = tab_requisition.width;
5849 child_allocation.y = priv->tab_vborder + focus_width + page->allocation.y;
5851 if (tab_pos == GTK_POS_TOP)
5852 child_allocation.y += ythickness;
5854 child_allocation.height = MAX (1, (page->allocation.height - ythickness -
5855 2 * (priv->tab_vborder + focus_width)));
5859 padding = tab_curvature + focus_width + priv->tab_vborder;
5862 child_allocation.y = ythickness + padding;
5863 child_allocation.height = MAX (1, (page->allocation.height -
5864 2 * child_allocation.y));
5865 child_allocation.y += page->allocation.y;
5869 child_allocation.y = page->allocation.y +
5870 (page->allocation.height - tab_requisition.height) / 2;
5872 child_allocation.height = tab_requisition.height;
5875 child_allocation.x = priv->tab_hborder + focus_width + page->allocation.x;
5877 if (tab_pos == GTK_POS_LEFT)
5878 child_allocation.x += xthickness;
5880 child_allocation.width = MAX (1, (page->allocation.width - xthickness -
5881 2 * (priv->tab_hborder + focus_width)));
5885 gtk_widget_get_allocation (page->tab_label, &label_allocation);
5886 tab_allocation_changed = (child_allocation.x != label_allocation.x ||
5887 child_allocation.y != label_allocation.y ||
5888 child_allocation.width != label_allocation.width ||
5889 child_allocation.height != label_allocation.height);
5891 gtk_widget_size_allocate (page->tab_label, &child_allocation);
5895 page->tab_allocated_visible = TRUE;
5896 tab_allocation_changed = TRUE;
5899 return tab_allocation_changed;
5903 gtk_notebook_calc_tabs (GtkNotebook *notebook,
5909 GtkNotebookPage *page = NULL;
5911 GList *last_list = NULL;
5912 GList *last_calculated_child = NULL;
5914 gint tab_pos = get_effective_tab_pos (notebook);
5915 guint real_direction;
5921 pack = GTK_NOTEBOOK_PAGE (start)->pack;
5922 if (pack == GTK_PACK_END)
5923 real_direction = (direction == STEP_PREV) ? STEP_NEXT : STEP_PREV;
5925 real_direction = direction;
5932 case GTK_POS_BOTTOM:
5935 page = children->data;
5936 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5937 gtk_widget_get_visible (page->child))
5939 if (page->pack == pack)
5941 *tab_space -= page->requisition.width;
5942 if (*tab_space < 0 || children == *end)
5946 *tab_space = - (*tab_space +
5947 page->requisition.width);
5949 if (*tab_space == 0 && direction == STEP_PREV)
5950 children = last_calculated_child;
5957 last_calculated_child = children;
5959 last_list = children;
5961 if (real_direction == STEP_NEXT)
5962 children = children->next;
5964 children = children->prev;
5971 page = children->data;
5972 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5973 gtk_widget_get_visible (page->child))
5975 if (page->pack == pack)
5977 *tab_space -= page->requisition.height;
5978 if (*tab_space < 0 || children == *end)
5982 *tab_space = - (*tab_space +
5983 page->requisition.height);
5985 if (*tab_space == 0 && direction == STEP_PREV)
5986 children = last_calculated_child;
5993 last_calculated_child = children;
5995 last_list = children;
5997 if (real_direction == STEP_NEXT)
5998 children = children->next;
6000 children = children->prev;
6004 if (real_direction == STEP_PREV)
6006 pack = (pack == GTK_PACK_END) ? GTK_PACK_START : GTK_PACK_END;
6007 real_direction = STEP_PREV;
6008 children = last_list;
6013 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6015 GtkNotebookPrivate *priv = notebook->priv;
6018 for (list = priv->children; list != NULL; list = list->next)
6020 GtkNotebookPage *page = list->data;
6022 if (page->tab_label)
6024 if (page == priv->cur_page)
6025 gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
6027 gtk_widget_set_state (page->tab_label, GTK_STATE_ACTIVE);
6032 /* Private GtkNotebook Page Switch Methods:
6034 * gtk_notebook_real_switch_page
6037 gtk_notebook_real_switch_page (GtkNotebook *notebook,
6041 GtkNotebookPrivate *priv = notebook->priv;
6042 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6043 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6044 gboolean child_has_focus;
6046 if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6049 /* save the value here, changing visibility changes focus */
6050 child_has_focus = priv->child_has_focus;
6053 gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6055 priv->cur_page = page;
6057 if (!priv->focus_tab ||
6058 priv->focus_tab->data != (gpointer) priv->cur_page)
6060 g_list_find (priv->children, priv->cur_page);
6062 gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6064 /* If the focus was on the previous page, move it to the first
6065 * element on the new page, if possible, or if not, to the
6068 if (child_has_focus)
6070 if (priv->cur_page->last_focus_child &&
6071 gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6072 gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6074 if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6075 gtk_widget_grab_focus (GTK_WIDGET (notebook));
6078 gtk_notebook_update_tab_states (notebook);
6079 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6080 g_object_notify (G_OBJECT (notebook), "page");
6083 /* Private GtkNotebook Page Switch Functions:
6085 * gtk_notebook_switch_page
6086 * gtk_notebook_page_select
6087 * gtk_notebook_switch_focus_tab
6088 * gtk_notebook_menu_switch_page
6091 gtk_notebook_switch_page (GtkNotebook *notebook,
6092 GtkNotebookPage *page)
6094 GtkNotebookPrivate *priv = notebook->priv;
6097 if (priv->cur_page == page)
6100 page_num = g_list_index (priv->children, page);
6102 g_signal_emit (notebook,
6103 notebook_signals[SWITCH_PAGE],
6110 gtk_notebook_page_select (GtkNotebook *notebook,
6111 gboolean move_focus)
6113 GtkNotebookPrivate *priv = notebook->priv;
6114 GtkNotebookPage *page;
6115 GtkDirectionType dir = GTK_DIR_DOWN; /* Quiet GCC */
6116 gint tab_pos = get_effective_tab_pos (notebook);
6118 if (!priv->focus_tab)
6121 page = priv->focus_tab->data;
6122 gtk_notebook_switch_page (notebook, page);
6131 case GTK_POS_BOTTOM:
6135 dir = GTK_DIR_RIGHT;
6142 if (gtk_widget_child_focus (page->child, dir))
6149 gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
6152 GtkNotebookPrivate *priv = notebook->priv;
6154 GtkNotebookPage *page;
6156 if (priv->focus_tab == new_child)
6159 old_child = priv->focus_tab;
6160 priv->focus_tab = new_child;
6162 if (priv->scrollable)
6163 gtk_notebook_redraw_arrows (notebook);
6165 if (!priv->show_tabs || !priv->focus_tab)
6168 page = priv->focus_tab->data;
6169 if (gtk_widget_get_mapped (page->tab_label))
6170 gtk_notebook_redraw_tabs (notebook);
6172 gtk_notebook_pages_allocate (notebook);
6174 gtk_notebook_switch_page (notebook, page);
6178 gtk_notebook_menu_switch_page (GtkWidget *widget,
6179 GtkNotebookPage *page)
6181 GtkNotebookPrivate *priv;
6182 GtkNotebook *notebook;
6187 parent = gtk_widget_get_parent (widget);
6188 notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6189 priv = notebook->priv;
6191 if (priv->cur_page == page)
6195 children = priv->children;
6196 while (children && children->data != page)
6198 children = children->next;
6202 g_signal_emit (notebook,
6203 notebook_signals[SWITCH_PAGE],
6209 /* Private GtkNotebook Menu Functions:
6211 * gtk_notebook_menu_item_create
6212 * gtk_notebook_menu_label_unparent
6213 * gtk_notebook_menu_detacher
6216 gtk_notebook_menu_item_create (GtkNotebook *notebook,
6219 GtkNotebookPrivate *priv = notebook->priv;
6220 GtkNotebookPage *page;
6221 GtkWidget *menu_item;
6224 if (page->default_menu)
6226 if (GTK_IS_LABEL (page->tab_label))
6227 page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6229 page->menu_label = gtk_label_new ("");
6230 gtk_misc_set_alignment (GTK_MISC (page->menu_label), 0.0, 0.5);
6233 gtk_widget_show (page->menu_label);
6234 menu_item = gtk_menu_item_new ();
6235 gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6236 gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6237 gtk_notebook_real_page_position (notebook, list));
6238 g_signal_connect (menu_item, "activate",
6239 G_CALLBACK (gtk_notebook_menu_switch_page), page);
6240 if (gtk_widget_get_visible (page->child))
6241 gtk_widget_show (menu_item);
6245 gtk_notebook_menu_label_unparent (GtkWidget *widget,
6248 gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6249 _gtk_bin_set_child (GTK_BIN (widget), NULL);
6253 gtk_notebook_menu_detacher (GtkWidget *widget,
6256 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6257 GtkNotebookPrivate *priv = notebook->priv;
6259 g_return_if_fail (priv->menu == (GtkWidget*) menu);
6264 /* Public GtkNotebook Page Insert/Remove Methods :
6266 * gtk_notebook_append_page
6267 * gtk_notebook_append_page_menu
6268 * gtk_notebook_prepend_page
6269 * gtk_notebook_prepend_page_menu
6270 * gtk_notebook_insert_page
6271 * gtk_notebook_insert_page_menu
6272 * gtk_notebook_remove_page
6275 * gtk_notebook_append_page:
6276 * @notebook: a #GtkNotebook
6277 * @child: the #GtkWidget to use as the contents of the page.
6278 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6279 * or %NULL to use the default label, 'page N'.
6281 * Appends a page to @notebook.
6283 * Return value: the index (starting from 0) of the appended
6284 * page in the notebook, or -1 if function fails
6287 gtk_notebook_append_page (GtkNotebook *notebook,
6289 GtkWidget *tab_label)
6291 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6292 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6293 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6295 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6299 * gtk_notebook_append_page_menu:
6300 * @notebook: a #GtkNotebook
6301 * @child: the #GtkWidget to use as the contents of the page.
6302 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6303 * or %NULL to use the default label, 'page N'.
6304 * @menu_label: (allow-none): the widget to use as a label for the page-switch
6305 * menu, if that is enabled. If %NULL, and @tab_label
6306 * is a #GtkLabel or %NULL, then the menu label will be
6307 * a newly created label with the same text as @tab_label;
6308 * If @tab_label is not a #GtkLabel, @menu_label must be
6309 * specified if the page-switch menu is to be used.
6311 * Appends a page to @notebook, specifying the widget to use as the
6312 * label in the popup menu.
6314 * Return value: the index (starting from 0) of the appended
6315 * page in the notebook, or -1 if function fails
6318 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6320 GtkWidget *tab_label,
6321 GtkWidget *menu_label)
6323 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6324 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6325 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6326 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6328 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6332 * gtk_notebook_prepend_page:
6333 * @notebook: a #GtkNotebook
6334 * @child: the #GtkWidget to use as the contents of the page.
6335 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6336 * or %NULL to use the default label, 'page N'.
6338 * Prepends a page to @notebook.
6340 * Return value: the index (starting from 0) of the prepended
6341 * page in the notebook, or -1 if function fails
6344 gtk_notebook_prepend_page (GtkNotebook *notebook,
6346 GtkWidget *tab_label)
6348 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6349 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6350 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6352 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6356 * gtk_notebook_prepend_page_menu:
6357 * @notebook: a #GtkNotebook
6358 * @child: the #GtkWidget to use as the contents of the page.
6359 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6360 * or %NULL to use the default label, 'page N'.
6361 * @menu_label: (allow-none): the widget to use as a label for the page-switch
6362 * menu, if that is enabled. If %NULL, and @tab_label
6363 * is a #GtkLabel or %NULL, then the menu label will be
6364 * a newly created label with the same text as @tab_label;
6365 * If @tab_label is not a #GtkLabel, @menu_label must be
6366 * specified if the page-switch menu is to be used.
6368 * Prepends a page to @notebook, specifying the widget to use as the
6369 * label in the popup menu.
6371 * Return value: the index (starting from 0) of the prepended
6372 * page in the notebook, or -1 if function fails
6375 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6377 GtkWidget *tab_label,
6378 GtkWidget *menu_label)
6380 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6381 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6382 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6383 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6385 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6389 * gtk_notebook_insert_page:
6390 * @notebook: a #GtkNotebook
6391 * @child: the #GtkWidget to use as the contents of the page.
6392 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6393 * or %NULL to use the default label, 'page N'.
6394 * @position: the index (starting at 0) at which to insert the page,
6395 * or -1 to append the page after all other pages.
6397 * Insert a page into @notebook at the given position.
6399 * Return value: the index (starting from 0) of the inserted
6400 * page in the notebook, or -1 if function fails
6403 gtk_notebook_insert_page (GtkNotebook *notebook,
6405 GtkWidget *tab_label,
6408 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6409 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6410 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6412 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6417 gtk_notebook_page_compare_tab (gconstpointer a,
6420 return (((GtkNotebookPage *) a)->tab_label != b);
6424 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6428 GtkNotebook *notebook = GTK_NOTEBOOK (data);
6429 GtkNotebookPrivate *priv = notebook->priv;
6432 list = g_list_find_custom (priv->children, child,
6433 gtk_notebook_page_compare_tab);
6436 GtkNotebookPage *page = list->data;
6438 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
6439 gtk_notebook_switch_page (notebook, page);
6440 focus_tabs_in (notebook);
6447 * gtk_notebook_insert_page_menu:
6448 * @notebook: a #GtkNotebook
6449 * @child: the #GtkWidget to use as the contents of the page.
6450 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6451 * or %NULL to use the default label, 'page N'.
6452 * @menu_label: (allow-none): the widget to use as a label for the page-switch
6453 * menu, if that is enabled. If %NULL, and @tab_label
6454 * is a #GtkLabel or %NULL, then the menu label will be
6455 * a newly created label with the same text as @tab_label;
6456 * If @tab_label is not a #GtkLabel, @menu_label must be
6457 * specified if the page-switch menu is to be used.
6458 * @position: the index (starting at 0) at which to insert the page,
6459 * or -1 to append the page after all other pages.
6461 * Insert a page into @notebook at the given position, specifying
6462 * the widget to use as the label in the popup menu.
6464 * Return value: the index (starting from 0) of the inserted
6465 * page in the notebook
6468 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
6470 GtkWidget *tab_label,
6471 GtkWidget *menu_label,
6474 GtkNotebookClass *class;
6476 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6477 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6478 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6479 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6481 class = GTK_NOTEBOOK_GET_CLASS (notebook);
6483 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
6487 * gtk_notebook_remove_page:
6488 * @notebook: a #GtkNotebook.
6489 * @page_num: the index of a notebook page, starting
6490 * from 0. If -1, the last page will
6493 * Removes a page from the notebook given its index
6497 gtk_notebook_remove_page (GtkNotebook *notebook,
6500 GtkNotebookPrivate *priv;
6503 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6505 priv = notebook->priv;
6508 list = g_list_nth (priv->children, page_num);
6510 list = g_list_last (priv->children);
6513 gtk_container_remove (GTK_CONTAINER (notebook),
6514 ((GtkNotebookPage *) list->data)->child);
6517 /* Public GtkNotebook Page Switch Methods :
6518 * gtk_notebook_get_current_page
6519 * gtk_notebook_page_num
6520 * gtk_notebook_set_current_page
6521 * gtk_notebook_next_page
6522 * gtk_notebook_prev_page
6525 * gtk_notebook_get_current_page:
6526 * @notebook: a #GtkNotebook
6528 * Returns the page number of the current page.
6530 * Return value: the index (starting from 0) of the current
6531 * page in the notebook. If the notebook has no pages, then
6532 * -1 will be returned.
6535 gtk_notebook_get_current_page (GtkNotebook *notebook)
6537 GtkNotebookPrivate *priv;
6539 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6541 priv = notebook->priv;
6543 if (!priv->cur_page)
6546 return g_list_index (priv->children, priv->cur_page);
6550 * gtk_notebook_get_nth_page:
6551 * @notebook: a #GtkNotebook
6552 * @page_num: the index of a page in the notebook, or -1
6553 * to get the last page.
6555 * Returns the child widget contained in page number @page_num.
6557 * Return value: (transfer none): the child widget, or %NULL if @page_num is
6561 gtk_notebook_get_nth_page (GtkNotebook *notebook,
6564 GtkNotebookPrivate *priv;
6565 GtkNotebookPage *page;
6568 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6570 priv = notebook->priv;
6573 list = g_list_nth (priv->children, page_num);
6575 list = g_list_last (priv->children);
6587 * gtk_notebook_get_n_pages:
6588 * @notebook: a #GtkNotebook
6590 * Gets the number of pages in a notebook.
6592 * Return value: the number of pages in the notebook.
6597 gtk_notebook_get_n_pages (GtkNotebook *notebook)
6599 GtkNotebookPrivate *priv;
6601 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
6603 priv = notebook->priv;
6605 return g_list_length (priv->children);
6609 * gtk_notebook_page_num:
6610 * @notebook: a #GtkNotebook
6611 * @child: a #GtkWidget
6613 * Finds the index of the page which contains the given child
6616 * Return value: the index of the page containing @child, or
6617 * -1 if @child is not in the notebook.
6620 gtk_notebook_page_num (GtkNotebook *notebook,
6623 GtkNotebookPrivate *priv;
6627 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6629 priv = notebook->priv;
6632 children = priv->children;
6635 GtkNotebookPage *page = children->data;
6637 if (page->child == child)
6640 children = children->next;
6648 * gtk_notebook_set_current_page:
6649 * @notebook: a #GtkNotebook
6650 * @page_num: index of the page to switch to, starting from 0.
6651 * If negative, the last page will be used. If greater
6652 * than the number of pages in the notebook, nothing
6655 * Switches to the page number @page_num.
6657 * Note that due to historical reasons, GtkNotebook refuses
6658 * to switch to a page unless the child widget is visible.
6659 * Therefore, it is recommended to show child widgets before
6660 * adding them to a notebook.
6663 gtk_notebook_set_current_page (GtkNotebook *notebook,
6666 GtkNotebookPrivate *priv;
6669 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6671 priv = notebook->priv;
6674 page_num = g_list_length (priv->children) - 1;
6676 list = g_list_nth (priv->children, page_num);
6678 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6682 * gtk_notebook_next_page:
6683 * @notebook: a #GtkNotebook
6685 * Switches to the next page. Nothing happens if the current page is
6689 gtk_notebook_next_page (GtkNotebook *notebook)
6691 GtkNotebookPrivate *priv;
6694 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6696 priv = notebook->priv;
6698 list = g_list_find (priv->children, priv->cur_page);
6702 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
6706 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6710 * gtk_notebook_prev_page:
6711 * @notebook: a #GtkNotebook
6713 * Switches to the previous page. Nothing happens if the current page
6714 * is the first page.
6717 gtk_notebook_prev_page (GtkNotebook *notebook)
6719 GtkNotebookPrivate *priv;
6722 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6724 priv = notebook->priv;
6726 list = g_list_find (priv->children, priv->cur_page);
6730 list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
6734 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6737 /* Public GtkNotebook/Tab Style Functions
6739 * gtk_notebook_set_show_border
6740 * gtk_notebook_get_show_border
6741 * gtk_notebook_set_show_tabs
6742 * gtk_notebook_get_show_tabs
6743 * gtk_notebook_set_tab_pos
6744 * gtk_notebook_get_tab_pos
6745 * gtk_notebook_set_scrollable
6746 * gtk_notebook_get_scrollable
6747 * gtk_notebook_get_tab_hborder
6748 * gtk_notebook_get_tab_vborder
6751 * gtk_notebook_set_show_border:
6752 * @notebook: a #GtkNotebook
6753 * @show_border: %TRUE if a bevel should be drawn around the notebook.
6755 * Sets whether a bevel will be drawn around the notebook pages.
6756 * This only has a visual effect when the tabs are not shown.
6757 * See gtk_notebook_set_show_tabs().
6760 gtk_notebook_set_show_border (GtkNotebook *notebook,
6761 gboolean show_border)
6763 GtkNotebookPrivate *priv;
6765 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6767 priv = notebook->priv;
6769 if (priv->show_border != show_border)
6771 priv->show_border = show_border;
6773 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6774 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6776 g_object_notify (G_OBJECT (notebook), "show-border");
6781 * gtk_notebook_get_show_border:
6782 * @notebook: a #GtkNotebook
6784 * Returns whether a bevel will be drawn around the notebook pages. See
6785 * gtk_notebook_set_show_border().
6787 * Return value: %TRUE if the bevel is drawn
6790 gtk_notebook_get_show_border (GtkNotebook *notebook)
6792 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6794 return notebook->priv->show_border;
6798 * gtk_notebook_set_show_tabs:
6799 * @notebook: a #GtkNotebook
6800 * @show_tabs: %TRUE if the tabs should be shown.
6802 * Sets whether to show the tabs for the notebook or not.
6805 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
6808 GtkNotebookPrivate *priv;
6809 GtkNotebookPage *page;
6813 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6815 priv = notebook->priv;
6817 show_tabs = show_tabs != FALSE;
6819 if (priv->show_tabs == show_tabs)
6822 priv->show_tabs = show_tabs;
6823 children = priv->children;
6827 gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
6831 page = children->data;
6832 children = children->next;
6833 if (page->default_tab)
6835 gtk_widget_destroy (page->tab_label);
6836 page->tab_label = NULL;
6839 gtk_widget_hide (page->tab_label);
6844 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
6845 gtk_notebook_update_labels (notebook);
6848 for (i = 0; i < N_ACTION_WIDGETS; i++)
6850 if (priv->action_widget[i])
6851 gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
6854 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6856 g_object_notify (G_OBJECT (notebook), "show-tabs");
6860 * gtk_notebook_get_show_tabs:
6861 * @notebook: a #GtkNotebook
6863 * Returns whether the tabs of the notebook are shown. See
6864 * gtk_notebook_set_show_tabs().
6866 * Return value: %TRUE if the tabs are shown
6869 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
6871 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6873 return notebook->priv->show_tabs;
6877 * gtk_notebook_set_tab_pos:
6878 * @notebook: a #GtkNotebook.
6879 * @pos: the edge to draw the tabs at.
6881 * Sets the edge at which the tabs for switching pages in the
6882 * notebook are drawn.
6885 gtk_notebook_set_tab_pos (GtkNotebook *notebook,
6886 GtkPositionType pos)
6888 GtkNotebookPrivate *priv;
6890 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6892 priv = notebook->priv;
6894 if (priv->tab_pos != pos)
6896 priv->tab_pos = pos;
6897 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6898 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6901 g_object_notify (G_OBJECT (notebook), "tab-pos");
6905 * gtk_notebook_get_tab_pos:
6906 * @notebook: a #GtkNotebook
6908 * Gets the edge at which the tabs for switching pages in the
6909 * notebook are drawn.
6911 * Return value: the edge at which the tabs are drawn
6914 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
6916 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
6918 return notebook->priv->tab_pos;
6922 * gtk_notebook_set_scrollable:
6923 * @notebook: a #GtkNotebook
6924 * @scrollable: %TRUE if scroll arrows should be added
6926 * Sets whether the tab label area will have arrows for scrolling if
6927 * there are too many tabs to fit in the area.
6930 gtk_notebook_set_scrollable (GtkNotebook *notebook,
6931 gboolean scrollable)
6933 GtkNotebookPrivate *priv;
6935 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6937 priv = notebook->priv;
6939 scrollable = (scrollable != FALSE);
6941 if (scrollable != priv->scrollable)
6943 priv->scrollable = scrollable;
6945 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6946 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6948 g_object_notify (G_OBJECT (notebook), "scrollable");
6953 * gtk_notebook_get_scrollable:
6954 * @notebook: a #GtkNotebook
6956 * Returns whether the tab label area has arrows for scrolling. See
6957 * gtk_notebook_set_scrollable().
6959 * Return value: %TRUE if arrows for scrolling are present
6962 gtk_notebook_get_scrollable (GtkNotebook *notebook)
6964 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6966 return notebook->priv->scrollable;
6970 * gtk_notebook_get_tab_hborder:
6971 * @notebook: a #GtkNotebook
6973 * Returns the horizontal width of a tab border.
6975 * Return value: horizontal width of a tab border
6980 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
6982 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6984 return notebook->priv->tab_hborder;
6988 * gtk_notebook_get_tab_vborder:
6989 * @notebook: a #GtkNotebook
6991 * Returns the vertical width of a tab border.
6993 * Return value: vertical width of a tab border
6998 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7000 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7002 return notebook->priv->tab_vborder;
7006 /* Public GtkNotebook Popup Menu Methods:
7008 * gtk_notebook_popup_enable
7009 * gtk_notebook_popup_disable
7014 * gtk_notebook_popup_enable:
7015 * @notebook: a #GtkNotebook
7017 * Enables the popup menu: if the user clicks with the right mouse button on
7018 * the tab labels, a menu with all the pages will be popped up.
7021 gtk_notebook_popup_enable (GtkNotebook *notebook)
7023 GtkNotebookPrivate *priv;
7026 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7028 priv = notebook->priv;
7033 priv->menu = gtk_menu_new ();
7034 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7036 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7037 gtk_notebook_menu_item_create (notebook, list);
7039 gtk_notebook_update_labels (notebook);
7040 gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7041 GTK_WIDGET (notebook),
7042 gtk_notebook_menu_detacher);
7044 g_object_notify (G_OBJECT (notebook), "enable-popup");
7048 * gtk_notebook_popup_disable:
7049 * @notebook: a #GtkNotebook
7051 * Disables the popup menu.
7054 gtk_notebook_popup_disable (GtkNotebook *notebook)
7056 GtkNotebookPrivate *priv;
7058 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7060 priv = notebook->priv;
7065 gtk_container_foreach (GTK_CONTAINER (priv->menu),
7066 (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7067 gtk_widget_destroy (priv->menu);
7069 g_object_notify (G_OBJECT (notebook), "enable-popup");
7072 /* Public GtkNotebook Page Properties Functions:
7074 * gtk_notebook_get_tab_label
7075 * gtk_notebook_set_tab_label
7076 * gtk_notebook_set_tab_label_text
7077 * gtk_notebook_get_menu_label
7078 * gtk_notebook_set_menu_label
7079 * gtk_notebook_set_menu_label_text
7080 * gtk_notebook_get_tab_reorderable
7081 * gtk_notebook_set_tab_reorderable
7082 * gtk_notebook_get_tab_detachable
7083 * gtk_notebook_set_tab_detachable
7087 * gtk_notebook_get_tab_label:
7088 * @notebook: a #GtkNotebook
7091 * Returns the tab label widget for the page @child. %NULL is returned
7092 * if @child is not in @notebook or if no tab label has specifically
7093 * been set for @child.
7095 * Return value: (transfer none): the tab label
7098 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7103 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7104 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7106 list = CHECK_FIND_CHILD (notebook, child);
7110 if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7113 return GTK_NOTEBOOK_PAGE (list)->tab_label;
7117 * gtk_notebook_set_tab_label:
7118 * @notebook: a #GtkNotebook
7120 * @tab_label: (allow-none): the tab label widget to use, or %NULL for default tab
7123 * Changes the tab label for @child. If %NULL is specified
7124 * for @tab_label, then the page will have the label 'page N'.
7127 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7129 GtkWidget *tab_label)
7131 GtkNotebookPrivate *priv;
7132 GtkNotebookPage *page;
7135 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7136 g_return_if_fail (GTK_IS_WIDGET (child));
7138 priv = notebook->priv;
7140 list = CHECK_FIND_CHILD (notebook, child);
7144 /* a NULL pointer indicates a default_tab setting, otherwise
7145 * we need to set the associated label
7149 if (page->tab_label == tab_label)
7153 gtk_notebook_remove_tab_label (notebook, page);
7157 page->default_tab = FALSE;
7158 page->tab_label = tab_label;
7159 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7163 page->default_tab = TRUE;
7164 page->tab_label = NULL;
7166 if (priv->show_tabs)
7170 g_snprintf (string, sizeof(string), _("Page %u"),
7171 gtk_notebook_real_page_position (notebook, list));
7172 page->tab_label = gtk_label_new (string);
7173 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7177 if (page->tab_label)
7178 page->mnemonic_activate_signal =
7179 g_signal_connect (page->tab_label,
7180 "mnemonic-activate",
7181 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7184 if (priv->show_tabs && gtk_widget_get_visible (child))
7186 gtk_widget_show (page->tab_label);
7187 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7190 gtk_notebook_update_tab_states (notebook);
7191 gtk_widget_child_notify (child, "tab-label");
7195 * gtk_notebook_set_tab_label_text:
7196 * @notebook: a #GtkNotebook
7198 * @tab_text: the label text
7200 * Creates a new label and sets it as the tab label for the page
7201 * containing @child.
7204 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7206 const gchar *tab_text)
7208 GtkWidget *tab_label = NULL;
7210 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7213 tab_label = gtk_label_new (tab_text);
7214 gtk_notebook_set_tab_label (notebook, child, tab_label);
7215 gtk_widget_child_notify (child, "tab-label");
7219 * gtk_notebook_get_tab_label_text:
7220 * @notebook: a #GtkNotebook
7221 * @child: a widget contained in a page of @notebook
7223 * Retrieves the text of the tab label for the page containing
7226 * Return value: the text of the tab label, or %NULL if the
7227 * tab label widget is not a #GtkLabel. The
7228 * string is owned by the widget and must not
7231 G_CONST_RETURN gchar *
7232 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7235 GtkWidget *tab_label;
7237 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7238 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7240 tab_label = gtk_notebook_get_tab_label (notebook, child);
7242 if (GTK_IS_LABEL (tab_label))
7243 return gtk_label_get_text (GTK_LABEL (tab_label));
7249 * gtk_notebook_get_menu_label:
7250 * @notebook: a #GtkNotebook
7251 * @child: a widget contained in a page of @notebook
7253 * Retrieves the menu label widget of the page containing @child.
7255 * Return value: (transfer none): the menu label, or %NULL if the
7256 * notebook page does not have a menu label other than the
7257 * default (the tab label).
7260 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7265 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7266 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7268 list = CHECK_FIND_CHILD (notebook, child);
7272 if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7275 return GTK_NOTEBOOK_PAGE (list)->menu_label;
7279 * gtk_notebook_set_menu_label:
7280 * @notebook: a #GtkNotebook
7281 * @child: the child widget
7282 * @menu_label: (allow-none): the menu label, or NULL for default
7284 * Changes the menu label for the page containing @child.
7287 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7289 GtkWidget *menu_label)
7291 GtkNotebookPrivate *priv;
7292 GtkNotebookPage *page;
7295 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7296 g_return_if_fail (GTK_IS_WIDGET (child));
7298 priv = notebook->priv;
7300 list = CHECK_FIND_CHILD (notebook, child);
7305 if (page->menu_label)
7308 gtk_container_remove (GTK_CONTAINER (priv->menu),
7309 gtk_widget_get_parent (page->menu_label));
7311 if (!page->default_menu)
7312 g_object_unref (page->menu_label);
7317 page->menu_label = menu_label;
7318 g_object_ref_sink (page->menu_label);
7319 page->default_menu = FALSE;
7322 page->default_menu = TRUE;
7325 gtk_notebook_menu_item_create (notebook, list);
7326 gtk_widget_child_notify (child, "menu-label");
7330 * gtk_notebook_set_menu_label_text:
7331 * @notebook: a #GtkNotebook
7332 * @child: the child widget
7333 * @menu_text: the label text
7335 * Creates a new label and sets it as the menu label of @child.
7338 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7340 const gchar *menu_text)
7342 GtkWidget *menu_label = NULL;
7344 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7348 menu_label = gtk_label_new (menu_text);
7349 gtk_misc_set_alignment (GTK_MISC (menu_label), 0.0, 0.5);
7351 gtk_notebook_set_menu_label (notebook, child, menu_label);
7352 gtk_widget_child_notify (child, "menu-label");
7356 * gtk_notebook_get_menu_label_text:
7357 * @notebook: a #GtkNotebook
7358 * @child: the child widget of a page of the notebook.
7360 * Retrieves the text of the menu label for the page containing
7363 * Return value: the text of the tab label, or %NULL if the
7364 * widget does not have a menu label other than
7365 * the default menu label, or the menu label widget
7366 * is not a #GtkLabel. The string is owned by
7367 * the widget and must not be freed.
7369 G_CONST_RETURN gchar *
7370 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7373 GtkWidget *menu_label;
7375 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7376 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7378 menu_label = gtk_notebook_get_menu_label (notebook, child);
7380 if (GTK_IS_LABEL (menu_label))
7381 return gtk_label_get_text (GTK_LABEL (menu_label));
7386 /* Helper function called when pages are reordered
7389 gtk_notebook_child_reordered (GtkNotebook *notebook,
7390 GtkNotebookPage *page)
7392 GtkNotebookPrivate *priv = notebook->priv;
7396 GtkWidget *menu_item;
7398 menu_item = gtk_widget_get_parent (page->menu_label);
7399 gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7400 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7401 gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7404 gtk_notebook_update_tab_states (notebook);
7405 gtk_notebook_update_labels (notebook);
7409 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7413 GtkPackType pack_type)
7415 GtkNotebookPrivate *priv;
7416 GtkNotebookPage *page;
7419 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7420 g_return_if_fail (GTK_IS_WIDGET (child));
7422 priv = notebook->priv;
7424 list = CHECK_FIND_CHILD (notebook, child);
7429 expand = expand != FALSE;
7430 fill = fill != FALSE;
7431 if (page->pack == pack_type && page->expand == expand && page->fill == fill)
7434 gtk_widget_freeze_child_notify (child);
7435 page->expand = expand;
7436 gtk_widget_child_notify (child, "tab-expand");
7438 gtk_widget_child_notify (child, "tab-fill");
7439 if (page->pack != pack_type)
7441 page->pack = pack_type;
7442 gtk_notebook_child_reordered (notebook, page);
7444 gtk_widget_child_notify (child, "tab-pack");
7445 gtk_widget_child_notify (child, "position");
7446 if (priv->show_tabs)
7447 gtk_notebook_pages_allocate (notebook);
7448 gtk_widget_thaw_child_notify (child);
7452 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
7456 GtkPackType *pack_type)
7460 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7461 g_return_if_fail (GTK_IS_WIDGET (child));
7463 list = CHECK_FIND_CHILD (notebook, child);
7468 *expand = GTK_NOTEBOOK_PAGE (list)->expand;
7470 *fill = GTK_NOTEBOOK_PAGE (list)->fill;
7472 *pack_type = GTK_NOTEBOOK_PAGE (list)->pack;
7476 * gtk_notebook_reorder_child:
7477 * @notebook: a #GtkNotebook
7478 * @child: the child to move
7479 * @position: the new position, or -1 to move to the end
7481 * Reorders the page containing @child, so that it appears in position
7482 * @position. If @position is greater than or equal to the number of
7483 * children in the list or negative, @child will be moved to the end
7487 gtk_notebook_reorder_child (GtkNotebook *notebook,
7491 GtkNotebookPrivate *priv;
7492 GList *list, *new_list;
7493 GtkNotebookPage *page;
7497 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7498 g_return_if_fail (GTK_IS_WIDGET (child));
7500 priv = notebook->priv;
7502 list = CHECK_FIND_CHILD (notebook, child);
7506 max_pos = g_list_length (priv->children) - 1;
7507 if (position < 0 || position > max_pos)
7510 old_pos = g_list_position (priv->children, list);
7512 if (old_pos == position)
7516 priv->children = g_list_delete_link (priv->children, list);
7518 priv->children = g_list_insert (priv->children, page, position);
7519 new_list = g_list_nth (priv->children, position);
7521 /* Fix up GList references in GtkNotebook structure */
7522 if (priv->first_tab == list)
7523 priv->first_tab = new_list;
7524 if (priv->focus_tab == list)
7525 priv->focus_tab = new_list;
7527 gtk_widget_freeze_child_notify (child);
7529 /* Move around the menu items if necessary */
7530 gtk_notebook_child_reordered (notebook, page);
7531 gtk_widget_child_notify (child, "tab-pack");
7532 gtk_widget_child_notify (child, "position");
7534 if (priv->show_tabs)
7535 gtk_notebook_pages_allocate (notebook);
7537 gtk_widget_thaw_child_notify (child);
7539 g_signal_emit (notebook,
7540 notebook_signals[PAGE_REORDERED],
7547 * gtk_notebook_set_group_name:
7548 * @notebook: a #GtkNotebook
7549 * @name: (allow-none): the name of the notebook group, or %NULL to unset it
7551 * Sets a group name for @notebook.
7553 * Notebooks with the same name will be able to exchange tabs
7554 * via drag and drop. A notebook with a %NULL group name will
7555 * not be able to exchange tabs with any other notebook.
7560 gtk_notebook_set_group_name (GtkNotebook *notebook,
7561 const gchar *group_name)
7563 GtkNotebookPrivate *priv;
7566 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7568 priv = notebook->priv;
7570 group = g_quark_from_string (group_name);
7572 if (priv->group != group)
7574 priv->group = group;
7575 g_object_notify (G_OBJECT (notebook), "group-name");
7580 * gtk_notebook_get_group_name:
7581 * @notebook: a #GtkNotebook
7583 * Gets the current group name for @notebook.
7585 * Return Value: (transfer none): the group name,
7586 * or %NULL if none is set.
7591 gtk_notebook_get_group_name (GtkNotebook *notebook)
7593 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7595 return g_quark_to_string (notebook->priv->group);
7599 * gtk_notebook_get_tab_reorderable:
7600 * @notebook: a #GtkNotebook
7601 * @child: a child #GtkWidget
7603 * Gets whether the tab can be reordered via drag and drop or not.
7605 * Return Value: %TRUE if the tab is reorderable.
7610 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
7615 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7616 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7618 list = CHECK_FIND_CHILD (notebook, child);
7622 return GTK_NOTEBOOK_PAGE (list)->reorderable;
7626 * gtk_notebook_set_tab_reorderable:
7627 * @notebook: a #GtkNotebook
7628 * @child: a child #GtkWidget
7629 * @reorderable: whether the tab is reorderable or not.
7631 * Sets whether the notebook tab can be reordered
7632 * via drag and drop or not.
7637 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
7639 gboolean reorderable)
7643 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7644 g_return_if_fail (GTK_IS_WIDGET (child));
7646 list = CHECK_FIND_CHILD (notebook, child);
7650 if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
7652 GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
7653 gtk_widget_child_notify (child, "reorderable");
7658 * gtk_notebook_get_tab_detachable:
7659 * @notebook: a #GtkNotebook
7660 * @child: a child #GtkWidget
7662 * Returns whether the tab contents can be detached from @notebook.
7664 * Return Value: TRUE if the tab is detachable.
7669 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
7674 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7675 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7677 list = CHECK_FIND_CHILD (notebook, child);
7681 return GTK_NOTEBOOK_PAGE (list)->detachable;
7685 * gtk_notebook_set_tab_detachable:
7686 * @notebook: a #GtkNotebook
7687 * @child: a child #GtkWidget
7688 * @detachable: whether the tab is detachable or not
7690 * Sets whether the tab can be detached from @notebook to another
7691 * notebook or widget.
7693 * Note that 2 notebooks must share a common group identificator
7694 * (see gtk_notebook_set_group()) to allow automatic tabs
7695 * interchange between them.
7697 * If you want a widget to interact with a notebook through DnD
7698 * (i.e.: accept dragged tabs from it) it must be set as a drop
7699 * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
7700 * will fill the selection with a GtkWidget** pointing to the child
7701 * widget that corresponds to the dropped tab.
7704 * on_drop_zone_drag_data_received (GtkWidget *widget,
7705 * GdkDragContext *context,
7708 * GtkSelectionData *selection_data,
7711 * gpointer user_data)
7713 * GtkWidget *notebook;
7714 * GtkWidget **child;
7716 * notebook = gtk_drag_get_source_widget (context);
7717 * child = (void*) selection_data->data;
7719 * process_widget (*child);
7720 * gtk_container_remove (GTK_CONTAINER (notebook), *child);
7724 * If you want a notebook to accept drags from other widgets,
7725 * you will have to set your own DnD code to do it.
7730 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
7732 gboolean detachable)
7736 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7737 g_return_if_fail (GTK_IS_WIDGET (child));
7739 list = CHECK_FIND_CHILD (notebook, child);
7743 if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
7745 GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
7746 gtk_widget_child_notify (child, "detachable");
7751 * gtk_notebook_get_action_widget:
7752 * @notebook: a #GtkNotebook
7753 * @pack_type: pack type of the action widget to receive
7755 * Gets one of the action widgets. See gtk_notebook_set_action_widget().
7757 * Returns: (transfer none): The action widget with the given @pack_type
7758 * or %NULL when this action widget has not been set
7763 gtk_notebook_get_action_widget (GtkNotebook *notebook,
7764 GtkPackType pack_type)
7766 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7768 return notebook->priv->action_widget[pack_type];
7772 * gtk_notebook_set_action_widget:
7773 * @notebook: a #GtkNotebook
7774 * @widget: a #GtkWidget
7775 * @pack_type: pack type of the action widget
7777 * Sets @widget as one of the action widgets. Depending on the pack type
7778 * the widget will be placed before or after the tabs. You can use
7779 * a #GtkBox if you need to pack more than one widget on the same side.
7781 * Note that action widgets are "internal" children of the notebook and thus
7782 * not included in the list returned from gtk_container_foreach().
7787 gtk_notebook_set_action_widget (GtkNotebook *notebook,
7789 GtkPackType pack_type)
7791 GtkNotebookPrivate *priv;
7793 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7794 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
7795 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
7797 priv = notebook->priv;
7799 if (priv->action_widget[pack_type])
7800 gtk_widget_unparent (priv->action_widget[pack_type]);
7802 priv->action_widget[pack_type] = widget;
7806 gtk_widget_set_child_visible (widget, priv->show_tabs);
7807 gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
7810 gtk_widget_queue_resize (GTK_WIDGET (notebook));