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 /*** GtkObject Methods ***/
266 static void gtk_notebook_destroy (GtkObject *object);
267 static void gtk_notebook_set_property (GObject *object,
271 static void gtk_notebook_get_property (GObject *object,
276 /*** GtkWidget Methods ***/
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 GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
541 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
542 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
543 GtkBindingSet *binding_set;
545 gobject_class->set_property = gtk_notebook_set_property;
546 gobject_class->get_property = gtk_notebook_get_property;
547 object_class->destroy = gtk_notebook_destroy;
549 widget_class->map = gtk_notebook_map;
550 widget_class->unmap = gtk_notebook_unmap;
551 widget_class->realize = gtk_notebook_realize;
552 widget_class->unrealize = gtk_notebook_unrealize;
553 widget_class->size_request = gtk_notebook_size_request;
554 widget_class->size_allocate = gtk_notebook_size_allocate;
555 widget_class->draw = gtk_notebook_draw;
556 widget_class->button_press_event = gtk_notebook_button_press;
557 widget_class->button_release_event = gtk_notebook_button_release;
558 widget_class->popup_menu = gtk_notebook_popup_menu;
559 widget_class->leave_notify_event = gtk_notebook_leave_notify;
560 widget_class->motion_notify_event = gtk_notebook_motion_notify;
561 widget_class->grab_notify = gtk_notebook_grab_notify;
562 widget_class->state_changed = gtk_notebook_state_changed;
563 widget_class->focus_in_event = gtk_notebook_focus_in;
564 widget_class->focus_out_event = gtk_notebook_focus_out;
565 widget_class->focus = gtk_notebook_focus;
566 widget_class->style_set = gtk_notebook_style_set;
567 widget_class->drag_begin = gtk_notebook_drag_begin;
568 widget_class->drag_end = gtk_notebook_drag_end;
569 widget_class->drag_motion = gtk_notebook_drag_motion;
570 widget_class->drag_leave = gtk_notebook_drag_leave;
571 widget_class->drag_drop = gtk_notebook_drag_drop;
572 widget_class->drag_data_get = gtk_notebook_drag_data_get;
573 widget_class->drag_data_received = gtk_notebook_drag_data_received;
575 container_class->add = gtk_notebook_add;
576 container_class->remove = gtk_notebook_remove;
577 container_class->forall = gtk_notebook_forall;
578 container_class->set_focus_child = gtk_notebook_set_focus_child;
579 container_class->get_child_property = gtk_notebook_get_child_property;
580 container_class->set_child_property = gtk_notebook_set_child_property;
581 container_class->child_type = gtk_notebook_child_type;
583 class->switch_page = gtk_notebook_real_switch_page;
584 class->insert_page = gtk_notebook_real_insert_page;
586 class->focus_tab = gtk_notebook_focus_tab;
587 class->select_page = gtk_notebook_select_page;
588 class->change_current_page = gtk_notebook_change_current_page;
589 class->move_focus_out = gtk_notebook_move_focus_out;
590 class->reorder_tab = gtk_notebook_reorder_tab;
591 class->create_window = gtk_notebook_create_window;
593 g_object_class_install_property (gobject_class,
595 g_param_spec_int ("page",
597 P_("The index of the current page"),
601 GTK_PARAM_READWRITE));
602 g_object_class_install_property (gobject_class,
604 g_param_spec_enum ("tab-pos",
606 P_("Which side of the notebook holds the tabs"),
607 GTK_TYPE_POSITION_TYPE,
609 GTK_PARAM_READWRITE));
610 g_object_class_install_property (gobject_class,
612 g_param_spec_boolean ("show-tabs",
614 P_("Whether tabs should be shown"),
616 GTK_PARAM_READWRITE));
617 g_object_class_install_property (gobject_class,
619 g_param_spec_boolean ("show-border",
621 P_("Whether the border should be shown"),
623 GTK_PARAM_READWRITE));
624 g_object_class_install_property (gobject_class,
626 g_param_spec_boolean ("scrollable",
628 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
630 GTK_PARAM_READWRITE));
631 g_object_class_install_property (gobject_class,
633 g_param_spec_boolean ("enable-popup",
635 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
637 GTK_PARAM_READWRITE));
640 * GtkNotebook:group-name:
642 * Group name for tab drag and drop.
646 g_object_class_install_property (gobject_class,
648 g_param_spec_string ("group-name",
650 P_("Group name for tab drag and drop"),
652 GTK_PARAM_READWRITE));
654 gtk_container_class_install_child_property (container_class,
655 CHILD_PROP_TAB_LABEL,
656 g_param_spec_string ("tab-label",
658 P_("The string displayed on the child's tab label"),
660 GTK_PARAM_READWRITE));
661 gtk_container_class_install_child_property (container_class,
662 CHILD_PROP_MENU_LABEL,
663 g_param_spec_string ("menu-label",
665 P_("The string displayed in the child's menu entry"),
667 GTK_PARAM_READWRITE));
668 gtk_container_class_install_child_property (container_class,
670 g_param_spec_int ("position",
672 P_("The index of the child in the parent"),
674 GTK_PARAM_READWRITE));
675 gtk_container_class_install_child_property (container_class,
676 CHILD_PROP_TAB_EXPAND,
677 g_param_spec_boolean ("tab-expand",
679 P_("Whether to expand the child's tab"),
681 GTK_PARAM_READWRITE));
682 gtk_container_class_install_child_property (container_class,
684 g_param_spec_boolean ("tab-fill",
686 P_("Whether the child's tab should fill the allocated area"),
688 GTK_PARAM_READWRITE));
689 gtk_container_class_install_child_property (container_class,
691 g_param_spec_enum ("tab-pack",
693 P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
694 GTK_TYPE_PACK_TYPE, GTK_PACK_START,
695 GTK_PARAM_READWRITE));
696 gtk_container_class_install_child_property (container_class,
697 CHILD_PROP_REORDERABLE,
698 g_param_spec_boolean ("reorderable",
699 P_("Tab reorderable"),
700 P_("Whether the tab is reorderable by user action"),
702 GTK_PARAM_READWRITE));
703 gtk_container_class_install_child_property (container_class,
704 CHILD_PROP_DETACHABLE,
705 g_param_spec_boolean ("detachable",
706 P_("Tab detachable"),
707 P_("Whether the tab is detachable"),
709 GTK_PARAM_READWRITE));
712 * GtkNotebook:has-secondary-backward-stepper:
714 * The "has-secondary-backward-stepper" property determines whether
715 * a second backward arrow button is displayed on the opposite end
720 gtk_widget_class_install_style_property (widget_class,
721 g_param_spec_boolean ("has-secondary-backward-stepper",
722 P_("Secondary backward stepper"),
723 P_("Display a second backward arrow button on the opposite end of the tab area"),
725 GTK_PARAM_READABLE));
728 * GtkNotebook:has-secondary-forward-stepper:
730 * The "has-secondary-forward-stepper" property determines whether
731 * a second forward arrow button is displayed on the opposite end
736 gtk_widget_class_install_style_property (widget_class,
737 g_param_spec_boolean ("has-secondary-forward-stepper",
738 P_("Secondary forward stepper"),
739 P_("Display a second forward arrow button on the opposite end of the tab area"),
741 GTK_PARAM_READABLE));
744 * GtkNotebook:has-backward-stepper:
746 * The "has-backward-stepper" property determines whether
747 * the standard backward arrow button is displayed.
751 gtk_widget_class_install_style_property (widget_class,
752 g_param_spec_boolean ("has-backward-stepper",
753 P_("Backward stepper"),
754 P_("Display the standard backward arrow button"),
756 GTK_PARAM_READABLE));
759 * GtkNotebook:has-forward-stepper:
761 * The "has-forward-stepper" property determines whether
762 * the standard forward arrow button is displayed.
766 gtk_widget_class_install_style_property (widget_class,
767 g_param_spec_boolean ("has-forward-stepper",
768 P_("Forward stepper"),
769 P_("Display the standard forward arrow button"),
771 GTK_PARAM_READABLE));
774 * GtkNotebook:tab-overlap:
776 * The "tab-overlap" property defines size of tab overlap
781 gtk_widget_class_install_style_property (widget_class,
782 g_param_spec_int ("tab-overlap",
784 P_("Size of tab overlap area"),
788 GTK_PARAM_READABLE));
791 * GtkNotebook:tab-curvature:
793 * The "tab-curvature" property defines size of tab curvature.
797 gtk_widget_class_install_style_property (widget_class,
798 g_param_spec_int ("tab-curvature",
800 P_("Size of tab curvature"),
804 GTK_PARAM_READABLE));
807 * GtkNotebook:arrow-spacing:
809 * The "arrow-spacing" property defines the spacing between the scroll
810 * arrows and the tabs.
814 gtk_widget_class_install_style_property (widget_class,
815 g_param_spec_int ("arrow-spacing",
817 P_("Scroll arrow spacing"),
821 GTK_PARAM_READABLE));
823 notebook_signals[SWITCH_PAGE] =
824 g_signal_new (I_("switch-page"),
825 G_TYPE_FROM_CLASS (gobject_class),
827 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
829 _gtk_marshal_VOID__OBJECT_UINT,
833 notebook_signals[FOCUS_TAB] =
834 g_signal_new (I_("focus-tab"),
835 G_TYPE_FROM_CLASS (gobject_class),
836 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
837 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
839 _gtk_marshal_BOOLEAN__ENUM,
841 GTK_TYPE_NOTEBOOK_TAB);
842 notebook_signals[SELECT_PAGE] =
843 g_signal_new (I_("select-page"),
844 G_TYPE_FROM_CLASS (gobject_class),
845 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
846 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
848 _gtk_marshal_BOOLEAN__BOOLEAN,
851 notebook_signals[CHANGE_CURRENT_PAGE] =
852 g_signal_new (I_("change-current-page"),
853 G_TYPE_FROM_CLASS (gobject_class),
854 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
855 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
857 _gtk_marshal_BOOLEAN__INT,
860 notebook_signals[MOVE_FOCUS_OUT] =
861 g_signal_new (I_("move-focus-out"),
862 G_TYPE_FROM_CLASS (gobject_class),
863 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
864 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
866 _gtk_marshal_VOID__ENUM,
868 GTK_TYPE_DIRECTION_TYPE);
869 notebook_signals[REORDER_TAB] =
870 g_signal_new (I_("reorder-tab"),
871 G_TYPE_FROM_CLASS (gobject_class),
872 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
873 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
875 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
877 GTK_TYPE_DIRECTION_TYPE,
880 * GtkNotebook::page-reordered:
881 * @notebook: the #GtkNotebook
882 * @child: the child #GtkWidget affected
883 * @page_num: the new page number for @child
885 * the ::page-reordered signal is emitted in the notebook
886 * right after a page has been reordered.
890 notebook_signals[PAGE_REORDERED] =
891 g_signal_new (I_("page-reordered"),
892 G_TYPE_FROM_CLASS (gobject_class),
895 _gtk_marshal_VOID__OBJECT_UINT,
900 * GtkNotebook::page-removed:
901 * @notebook: the #GtkNotebook
902 * @child: the child #GtkWidget affected
903 * @page_num: the @child page number
905 * the ::page-removed signal is emitted in the notebook
906 * right after a page is removed from the notebook.
910 notebook_signals[PAGE_REMOVED] =
911 g_signal_new (I_("page-removed"),
912 G_TYPE_FROM_CLASS (gobject_class),
915 _gtk_marshal_VOID__OBJECT_UINT,
920 * GtkNotebook::page-added:
921 * @notebook: the #GtkNotebook
922 * @child: the child #GtkWidget affected
923 * @page_num: the new page number for @child
925 * the ::page-added signal is emitted in the notebook
926 * right after a page is added to the notebook.
930 notebook_signals[PAGE_ADDED] =
931 g_signal_new (I_("page-added"),
932 G_TYPE_FROM_CLASS (gobject_class),
935 _gtk_marshal_VOID__OBJECT_UINT,
941 * GtkNotebook::create-window:
942 * @notebook: the #GtkNotebook emitting the signal
943 * @page: the tab of @notebook that is being detached
944 * @x: the X coordinate where the drop happens
945 * @y: the Y coordinate where the drop happens
947 * The ::create-window signal is emitted when a detachable
948 * tab is dropped on the root window.
950 * A handler for this signal can create a window containing
951 * a notebook where the tab will be attached. It is also
952 * responsible for moving/resizing the window and adding the
953 * necessary properties to the notebook (e.g. the
954 * #GtkNotebook:group ).
956 * Returns: a #GtkNotebook that @page should be added to, or %NULL.
960 notebook_signals[CREATE_WINDOW] =
961 g_signal_new (I_("create-window"),
962 G_TYPE_FROM_CLASS (gobject_class),
964 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
965 gtk_object_handled_accumulator, NULL,
966 _gtk_marshal_OBJECT__OBJECT_INT_INT,
967 GTK_TYPE_NOTEBOOK, 3,
968 GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
970 binding_set = gtk_binding_set_by_class (class);
971 gtk_binding_entry_add_signal (binding_set,
974 G_TYPE_BOOLEAN, FALSE);
975 gtk_binding_entry_add_signal (binding_set,
978 G_TYPE_BOOLEAN, FALSE);
980 gtk_binding_entry_add_signal (binding_set,
983 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
984 gtk_binding_entry_add_signal (binding_set,
987 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
988 gtk_binding_entry_add_signal (binding_set,
991 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
992 gtk_binding_entry_add_signal (binding_set,
995 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
997 gtk_binding_entry_add_signal (binding_set,
998 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
999 "change-current-page", 1,
1001 gtk_binding_entry_add_signal (binding_set,
1002 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1003 "change-current-page", 1,
1006 gtk_binding_entry_add_signal (binding_set,
1007 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1008 "change-current-page", 1,
1010 gtk_binding_entry_add_signal (binding_set,
1011 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1012 "change-current-page", 1,
1015 add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1016 add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1017 add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1018 add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1020 add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1021 add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1022 add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1023 add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1024 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1025 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1026 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1027 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1029 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1030 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1032 g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1036 gtk_notebook_init (GtkNotebook *notebook)
1038 GtkNotebookPrivate *priv;
1040 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1041 gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1043 notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1045 GtkNotebookPrivate);
1046 priv = notebook->priv;
1048 priv->cur_page = NULL;
1049 priv->children = NULL;
1050 priv->first_tab = NULL;
1051 priv->focus_tab = NULL;
1052 priv->event_window = NULL;
1055 priv->tab_hborder = 2;
1056 priv->tab_vborder = 2;
1058 priv->show_tabs = TRUE;
1059 priv->show_border = TRUE;
1060 priv->tab_pos = GTK_POS_TOP;
1061 priv->scrollable = FALSE;
1063 priv->click_child = 0;
1065 priv->need_timer = 0;
1066 priv->child_has_focus = FALSE;
1067 priv->have_visible_child = FALSE;
1068 priv->focus_out = FALSE;
1070 priv->has_before_previous = 1;
1071 priv->has_before_next = 0;
1072 priv->has_after_previous = 0;
1073 priv->has_after_next = 1;
1076 priv->pressed_button = -1;
1077 priv->dnd_timer = 0;
1078 priv->switch_tab_timer = 0;
1079 priv->source_targets = gtk_target_list_new (notebook_targets,
1080 G_N_ELEMENTS (notebook_targets));
1081 priv->operation = DRAG_OPERATION_NONE;
1082 priv->detached_tab = NULL;
1083 priv->during_detach = FALSE;
1084 priv->has_scrolled = FALSE;
1086 gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1087 notebook_targets, G_N_ELEMENTS (notebook_targets),
1090 g_signal_connect (G_OBJECT (notebook), "drag-failed",
1091 G_CALLBACK (gtk_notebook_drag_failed), NULL);
1093 gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1097 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1099 iface->add_child = gtk_notebook_buildable_add_child;
1103 gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1104 GtkBuilder *builder,
1108 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1110 if (type && strcmp (type, "tab") == 0)
1114 page = gtk_notebook_get_nth_page (notebook, -1);
1115 /* To set the tab label widget, we must have already a child
1116 * inside the tab container. */
1117 g_assert (page != NULL);
1118 gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1120 else if (type && strcmp (type, "action-start") == 0)
1122 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1124 else if (type && strcmp (type, "action-end") == 0)
1126 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1129 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1131 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1135 gtk_notebook_select_page (GtkNotebook *notebook,
1136 gboolean move_focus)
1138 GtkNotebookPrivate *priv = notebook->priv;
1140 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1142 gtk_notebook_page_select (notebook, move_focus);
1150 gtk_notebook_focus_tab (GtkNotebook *notebook,
1151 GtkNotebookTab type)
1153 GtkNotebookPrivate *priv = notebook->priv;
1156 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1160 case GTK_NOTEBOOK_TAB_FIRST:
1161 list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1163 gtk_notebook_switch_focus_tab (notebook, list);
1165 case GTK_NOTEBOOK_TAB_LAST:
1166 list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1168 gtk_notebook_switch_focus_tab (notebook, list);
1179 gtk_notebook_change_current_page (GtkNotebook *notebook,
1182 GtkNotebookPrivate *priv = notebook->priv;
1183 GList *current = NULL;
1185 if (!priv->show_tabs)
1189 current = g_list_find (priv->children, priv->cur_page);
1193 current = gtk_notebook_search_page (notebook, current,
1194 offset < 0 ? STEP_PREV : STEP_NEXT,
1199 gboolean wrap_around;
1201 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1202 "gtk-keynav-wrap-around", &wrap_around,
1206 current = gtk_notebook_search_page (notebook, NULL,
1207 offset < 0 ? STEP_PREV : STEP_NEXT,
1213 offset += offset < 0 ? 1 : -1;
1217 gtk_notebook_switch_page (notebook, current->data);
1219 gtk_widget_error_bell (GTK_WIDGET (notebook));
1224 static GtkDirectionType
1225 get_effective_direction (GtkNotebook *notebook,
1226 GtkDirectionType direction)
1228 GtkNotebookPrivate *priv = notebook->priv;
1230 /* Remap the directions into the effective direction it would be for a
1231 * GTK_POS_TOP notebook
1234 #define D(rest) GTK_DIR_##rest
1236 static const GtkDirectionType translate_direction[2][4][6] = {
1237 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1238 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1239 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1240 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1241 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1242 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1243 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1244 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1249 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1251 return translate_direction[text_dir][priv->tab_pos][direction];
1255 get_effective_tab_pos (GtkNotebook *notebook)
1257 GtkNotebookPrivate *priv = notebook->priv;
1259 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1261 switch (priv->tab_pos)
1264 return GTK_POS_RIGHT;
1266 return GTK_POS_LEFT;
1271 return priv->tab_pos;
1275 get_tab_gap_pos (GtkNotebook *notebook)
1277 gint tab_pos = get_effective_tab_pos (notebook);
1278 gint gap_side = GTK_POS_BOTTOM;
1283 gap_side = GTK_POS_BOTTOM;
1285 case GTK_POS_BOTTOM:
1286 gap_side = GTK_POS_TOP;
1289 gap_side = GTK_POS_RIGHT;
1292 gap_side = GTK_POS_LEFT;
1300 gtk_notebook_move_focus_out (GtkNotebook *notebook,
1301 GtkDirectionType direction_type)
1303 GtkNotebookPrivate *priv = notebook->priv;
1304 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1305 GtkWidget *toplevel;
1307 if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1308 if (focus_tabs_in (notebook))
1310 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1311 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1314 /* At this point, we know we should be focusing out of the notebook entirely. We
1315 * do this by setting a flag, then propagating the focus motion to the notebook.
1317 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1318 if (!gtk_widget_is_toplevel (toplevel))
1321 g_object_ref (notebook);
1323 priv->focus_out = TRUE;
1324 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1325 priv->focus_out = FALSE;
1327 g_object_unref (notebook);
1331 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1333 GtkNotebookPrivate *priv = notebook->priv;
1336 if (position == tab)
1337 return g_list_position (priv->children, tab);
1339 /* check that we aren't inserting the tab in the
1340 * same relative position, taking packing into account */
1341 elem = (position) ? position->prev : g_list_last (priv->children);
1343 while (elem && elem != tab && GTK_NOTEBOOK_PAGE (elem)->pack != GTK_NOTEBOOK_PAGE (tab)->pack)
1347 return g_list_position (priv->children, tab);
1349 /* now actually reorder the tab */
1350 if (priv->first_tab == tab)
1351 priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1354 priv->children = g_list_remove_link (priv->children, tab);
1357 elem = g_list_last (priv->children);
1360 elem = position->prev;
1361 position->prev = tab;
1367 priv->children = tab;
1370 tab->next = position;
1372 return g_list_position (priv->children, tab);
1376 gtk_notebook_reorder_tab (GtkNotebook *notebook,
1377 GtkDirectionType direction_type,
1378 gboolean move_to_last)
1380 GtkNotebookPrivate *priv = notebook->priv;
1381 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1382 GtkNotebookPage *page;
1383 GList *last, *child;
1386 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1389 if (!priv->cur_page ||
1390 !priv->cur_page->reorderable)
1393 if (effective_direction != GTK_DIR_LEFT &&
1394 effective_direction != GTK_DIR_RIGHT)
1399 child = priv->focus_tab;
1404 child = gtk_notebook_search_page (notebook, last,
1405 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1408 while (child && GTK_NOTEBOOK_PAGE (last)->pack == GTK_NOTEBOOK_PAGE (child)->pack);
1413 child = gtk_notebook_search_page (notebook, priv->focus_tab,
1414 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1417 if (!child || child->data == priv->cur_page)
1422 if (page->pack == priv->cur_page->pack)
1424 if (effective_direction == GTK_DIR_RIGHT)
1425 page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child->next : child, priv->focus_tab);
1427 page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child : child->next, priv->focus_tab);
1429 gtk_notebook_pages_allocate (notebook);
1431 g_signal_emit (notebook,
1432 notebook_signals[PAGE_REORDERED],
1434 ((GtkNotebookPage *) priv->focus_tab->data)->child,
1446 * Creates a new #GtkNotebook widget with no pages.
1448 * Return value: the newly created #GtkNotebook
1451 gtk_notebook_new (void)
1453 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1456 /* Private GtkObject Methods :
1458 * gtk_notebook_destroy
1459 * gtk_notebook_set_arg
1460 * gtk_notebook_get_arg
1463 gtk_notebook_destroy (GtkObject *object)
1465 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1466 GtkNotebookPrivate *priv = notebook->priv;
1469 gtk_notebook_popup_disable (notebook);
1471 if (priv->source_targets)
1473 gtk_target_list_unref (priv->source_targets);
1474 priv->source_targets = NULL;
1477 if (priv->switch_tab_timer)
1479 g_source_remove (priv->switch_tab_timer);
1480 priv->switch_tab_timer = 0;
1483 GTK_OBJECT_CLASS (gtk_notebook_parent_class)->destroy (object);
1487 gtk_notebook_set_property (GObject *object,
1489 const GValue *value,
1492 GtkNotebook *notebook;
1494 notebook = GTK_NOTEBOOK (object);
1498 case PROP_SHOW_TABS:
1499 gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1501 case PROP_SHOW_BORDER:
1502 gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1504 case PROP_SCROLLABLE:
1505 gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1507 case PROP_ENABLE_POPUP:
1508 if (g_value_get_boolean (value))
1509 gtk_notebook_popup_enable (notebook);
1511 gtk_notebook_popup_disable (notebook);
1514 gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1517 gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1519 case PROP_GROUP_NAME:
1520 gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1523 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1529 gtk_notebook_get_property (GObject *object,
1534 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1535 GtkNotebookPrivate *priv = notebook->priv;
1539 case PROP_SHOW_TABS:
1540 g_value_set_boolean (value, priv->show_tabs);
1542 case PROP_SHOW_BORDER:
1543 g_value_set_boolean (value, priv->show_border);
1545 case PROP_SCROLLABLE:
1546 g_value_set_boolean (value, priv->scrollable);
1548 case PROP_ENABLE_POPUP:
1549 g_value_set_boolean (value, priv->menu != NULL);
1552 g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1555 g_value_set_enum (value, priv->tab_pos);
1557 case PROP_GROUP_NAME:
1558 g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1561 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1566 /* Private GtkWidget Methods :
1569 * gtk_notebook_unmap
1570 * gtk_notebook_realize
1571 * gtk_notebook_size_request
1572 * gtk_notebook_size_allocate
1574 * gtk_notebook_scroll
1575 * gtk_notebook_button_press
1576 * gtk_notebook_button_release
1577 * gtk_notebook_popup_menu
1578 * gtk_notebook_leave_notify
1579 * gtk_notebook_motion_notify
1580 * gtk_notebook_focus_in
1581 * gtk_notebook_focus_out
1582 * gtk_notebook_style_set
1583 * gtk_notebook_drag_begin
1584 * gtk_notebook_drag_end
1585 * gtk_notebook_drag_failed
1586 * gtk_notebook_drag_motion
1587 * gtk_notebook_drag_drop
1588 * gtk_notebook_drag_data_get
1589 * gtk_notebook_drag_data_received
1592 gtk_notebook_get_event_window_position (GtkNotebook *notebook,
1593 GdkRectangle *rectangle)
1595 GtkNotebookPrivate *priv = notebook->priv;
1596 GtkAllocation allocation, action_allocation;
1597 GtkWidget *widget = GTK_WIDGET (notebook);
1598 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1599 GtkNotebookPage *visible_page = NULL;
1601 gint tab_pos = get_effective_tab_pos (notebook);
1605 for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1607 GtkNotebookPage *page = tmp_list->data;
1608 if (gtk_widget_get_visible (page->child))
1610 visible_page = page;
1615 if (priv->show_tabs && visible_page)
1619 gtk_widget_get_allocation (widget, &allocation);
1621 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1622 rectangle->x = allocation.x + border_width;
1623 rectangle->y = allocation.y + border_width;
1628 case GTK_POS_BOTTOM:
1629 rectangle->width = allocation.width - 2 * border_width;
1630 rectangle->height = visible_page->requisition.height;
1631 if (tab_pos == GTK_POS_BOTTOM)
1632 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1634 for (i = 0; i < N_ACTION_WIDGETS; i++)
1636 if (priv->action_widget[i] &&
1637 gtk_widget_get_visible (priv->action_widget[i]))
1639 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1641 rectangle->width -= action_allocation.width;
1642 if ((!is_rtl && i == ACTION_WIDGET_START) ||
1643 (is_rtl && i == ACTION_WIDGET_END))
1644 rectangle->x += action_allocation.width;
1650 rectangle->width = visible_page->requisition.width;
1651 rectangle->height = allocation.height - 2 * border_width;
1652 if (tab_pos == GTK_POS_RIGHT)
1653 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1655 for (i = 0; i < N_ACTION_WIDGETS; i++)
1657 if (priv->action_widget[i] &&
1658 gtk_widget_get_visible (priv->action_widget[i]))
1660 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1662 rectangle->height -= action_allocation.height;
1664 if (i == ACTION_WIDGET_START)
1665 rectangle->y += action_allocation.height;
1678 rectangle->x = rectangle->y = 0;
1679 rectangle->width = rectangle->height = 10;
1687 gtk_notebook_map (GtkWidget *widget)
1689 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1690 GtkNotebookPrivate *priv = notebook->priv;
1691 GtkNotebookPage *page;
1695 gtk_widget_set_mapped (widget, TRUE);
1697 if (priv->cur_page &&
1698 gtk_widget_get_visible (priv->cur_page->child) &&
1699 !gtk_widget_get_mapped (priv->cur_page->child))
1700 gtk_widget_map (priv->cur_page->child);
1702 for (i = 0; i < N_ACTION_WIDGETS; i++)
1704 if (priv->action_widget[i] &&
1705 gtk_widget_get_visible (priv->action_widget[i]) &&
1706 GTK_WIDGET_CHILD_VISIBLE (priv->action_widget[i]) &&
1707 !gtk_widget_get_mapped (priv->action_widget[i]))
1708 gtk_widget_map (priv->action_widget[i]);
1711 if (priv->scrollable)
1712 gtk_notebook_pages_allocate (notebook);
1715 children = priv->children;
1719 page = children->data;
1720 children = children->next;
1722 if (page->tab_label &&
1723 gtk_widget_get_visible (page->tab_label) &&
1724 !gtk_widget_get_mapped (page->tab_label))
1725 gtk_widget_map (page->tab_label);
1729 if (gtk_notebook_get_event_window_position (notebook, NULL))
1730 gdk_window_show_unraised (priv->event_window);
1734 gtk_notebook_unmap (GtkWidget *widget)
1736 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1737 GtkNotebookPrivate *priv = notebook->priv;
1739 stop_scrolling (notebook);
1741 gtk_widget_set_mapped (widget, FALSE);
1743 gdk_window_hide (priv->event_window);
1745 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1749 gtk_notebook_realize (GtkWidget *widget)
1751 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1752 GtkNotebookPrivate *priv = notebook->priv;
1754 GdkWindowAttr attributes;
1755 gint attributes_mask;
1756 GdkRectangle event_window_pos;
1758 gtk_widget_set_realized (widget, TRUE);
1760 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1762 window = gtk_widget_get_parent_window (widget);
1763 gtk_widget_set_window (widget, window);
1764 g_object_ref (window);
1766 attributes.window_type = GDK_WINDOW_CHILD;
1767 attributes.x = event_window_pos.x;
1768 attributes.y = event_window_pos.y;
1769 attributes.width = event_window_pos.width;
1770 attributes.height = event_window_pos.height;
1771 attributes.wclass = GDK_INPUT_ONLY;
1772 attributes.event_mask = gtk_widget_get_events (widget);
1773 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1774 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1775 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1776 attributes_mask = GDK_WA_X | GDK_WA_Y;
1778 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1779 &attributes, attributes_mask);
1780 gdk_window_set_user_data (priv->event_window, notebook);
1782 gtk_widget_style_attach (widget);
1786 gtk_notebook_unrealize (GtkWidget *widget)
1788 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1789 GtkNotebookPrivate *priv = notebook->priv;
1791 gdk_window_set_user_data (priv->event_window, NULL);
1792 gdk_window_destroy (priv->event_window);
1793 priv->event_window = NULL;
1795 if (priv->drag_window)
1797 gdk_window_set_user_data (priv->drag_window, NULL);
1798 gdk_window_destroy (priv->drag_window);
1799 priv->drag_window = NULL;
1802 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1806 gtk_notebook_size_request (GtkWidget *widget,
1807 GtkRequisition *requisition)
1809 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1810 GtkNotebookPrivate *priv = notebook->priv;
1811 GtkNotebookPage *page;
1813 GtkRequisition child_requisition;
1814 GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
1815 gboolean switch_page = FALSE;
1821 gint scroll_arrow_hlength;
1822 gint scroll_arrow_vlength;
1825 gtk_widget_style_get (widget,
1826 "focus-line-width", &focus_width,
1827 "tab-overlap", &tab_overlap,
1828 "tab-curvature", &tab_curvature,
1829 "arrow-spacing", &arrow_spacing,
1830 "scroll-arrow-hlength", &scroll_arrow_hlength,
1831 "scroll-arrow-vlength", &scroll_arrow_vlength,
1834 requisition->width = 0;
1835 requisition->height = 0;
1837 for (children = priv->children, vis_pages = 0; children;
1838 children = children->next)
1841 page = children->data;
1843 if (gtk_widget_get_visible (page->child))
1846 gtk_widget_get_preferred_size (page->child,
1847 &child_requisition, NULL);
1849 requisition->width = MAX (requisition->width,
1850 child_requisition.width);
1851 requisition->height = MAX (requisition->height,
1852 child_requisition.height);
1854 if (priv->menu && page->menu_label)
1856 parent = gtk_widget_get_parent (page->menu_label);
1857 if (parent && !gtk_widget_get_visible (parent))
1858 gtk_widget_show (parent);
1863 if (page == priv->cur_page)
1866 if (priv->menu && page->menu_label)
1868 parent = gtk_widget_get_parent (page->menu_label);
1869 if (parent && gtk_widget_get_visible (parent))
1870 gtk_widget_hide (parent);
1875 if (priv->show_border || priv->show_tabs)
1879 style = gtk_widget_get_style (widget);
1881 requisition->width += style->xthickness * 2;
1882 requisition->height += style->ythickness * 2;
1884 if (priv->show_tabs)
1887 gint tab_height = 0;
1891 gint action_width = 0;
1892 gint action_height = 0;
1894 for (children = priv->children; children;
1895 children = children->next)
1897 page = children->data;
1899 if (gtk_widget_get_visible (page->child))
1901 if (!gtk_widget_get_visible (page->tab_label))
1902 gtk_widget_show (page->tab_label);
1904 gtk_widget_get_preferred_size (page->tab_label,
1905 &child_requisition, NULL);
1907 page->requisition.width = child_requisition.width + 2 * style->xthickness;
1908 page->requisition.height = child_requisition.height + 2 * style->ythickness;
1910 switch (priv->tab_pos)
1913 case GTK_POS_BOTTOM:
1914 page->requisition.height += 2 * (priv->tab_vborder +
1916 tab_height = MAX (tab_height, page->requisition.height);
1917 tab_max = MAX (tab_max, page->requisition.width);
1921 page->requisition.width += 2 * (priv->tab_hborder +
1923 tab_width = MAX (tab_width, page->requisition.width);
1924 tab_max = MAX (tab_max, page->requisition.height);
1928 else if (gtk_widget_get_visible (page->tab_label))
1929 gtk_widget_hide (page->tab_label);
1932 children = priv->children;
1936 for (i = 0; i < N_ACTION_WIDGETS; i++)
1938 if (priv->action_widget[i])
1940 gtk_widget_get_preferred_size (priv->action_widget[i],
1941 &action_widget_requisition[i], NULL);
1942 action_widget_requisition[i].width += style->xthickness;
1943 action_widget_requisition[i].height += style->ythickness;
1947 switch (priv->tab_pos)
1950 case GTK_POS_BOTTOM:
1951 if (tab_height == 0)
1954 if (priv->scrollable && vis_pages > 1 &&
1955 requisition->width < tab_width)
1956 tab_height = MAX (tab_height, scroll_arrow_hlength);
1958 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
1959 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
1961 padding = 2 * (tab_curvature + focus_width +
1962 priv->tab_hborder) - tab_overlap;
1966 page = children->data;
1967 children = children->next;
1969 if (!gtk_widget_get_visible (page->child))
1972 if (priv->homogeneous)
1973 page->requisition.width = tab_max;
1975 page->requisition.width += padding;
1977 tab_width += page->requisition.width;
1978 page->requisition.height = tab_height;
1981 if (priv->scrollable && vis_pages > 1 &&
1982 requisition->width < tab_width)
1983 tab_width = tab_max + 2 * (scroll_arrow_hlength + arrow_spacing);
1985 action_width += action_widget_requisition[ACTION_WIDGET_START].width;
1986 action_width += action_widget_requisition[ACTION_WIDGET_END].width;
1987 if (priv->homogeneous && !priv->scrollable)
1988 requisition->width = MAX (requisition->width,
1989 vis_pages * tab_max +
1990 tab_overlap + action_width);
1992 requisition->width = MAX (requisition->width,
1993 tab_width + tab_overlap + action_width);
1995 requisition->height += tab_height;
2002 if (priv->scrollable && vis_pages > 1 &&
2003 requisition->height < tab_height)
2004 tab_width = MAX (tab_width,
2005 arrow_spacing + 2 * scroll_arrow_vlength);
2007 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2008 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2010 padding = 2 * (tab_curvature + focus_width +
2011 priv->tab_vborder) - tab_overlap;
2016 page = children->data;
2017 children = children->next;
2019 if (!gtk_widget_get_visible (page->child))
2022 page->requisition.width = tab_width;
2024 if (priv->homogeneous)
2025 page->requisition.height = tab_max;
2027 page->requisition.height += padding;
2029 tab_height += page->requisition.height;
2032 if (priv->scrollable && vis_pages > 1 &&
2033 requisition->height < tab_height)
2034 tab_height = tab_max + (2 * scroll_arrow_vlength + arrow_spacing);
2035 action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2036 action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2038 if (priv->homogeneous && !priv->scrollable)
2039 requisition->height =
2040 MAX (requisition->height,
2041 vis_pages * tab_max + tab_overlap + action_height);
2043 requisition->height =
2044 MAX (requisition->height,
2045 tab_height + tab_overlap + action_height);
2047 if (!priv->homogeneous || priv->scrollable)
2049 requisition->height = MAX (requisition->height,
2050 vis_pages * tab_max +
2053 requisition->width += tab_width;
2060 for (children = priv->children; children;
2061 children = children->next)
2063 page = children->data;
2065 if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2066 gtk_widget_hide (page->tab_label);
2071 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2073 requisition->width += border_width * 2;
2074 requisition->height += border_width * 2;
2080 for (children = priv->children; children;
2081 children = children->next)
2083 page = children->data;
2084 if (gtk_widget_get_visible (page->child))
2086 gtk_notebook_switch_page (notebook, page);
2091 else if (gtk_widget_get_visible (widget))
2093 requisition->width = border_width * 2;
2094 requisition->height = border_width * 2;
2097 if (vis_pages && !priv->cur_page)
2099 children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2102 priv->first_tab = children;
2103 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2109 gtk_notebook_size_allocate (GtkWidget *widget,
2110 GtkAllocation *allocation)
2112 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2113 GtkNotebookPrivate *priv = notebook->priv;
2115 gint tab_pos = get_effective_tab_pos (notebook);
2119 style = gtk_widget_get_style (widget);
2121 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2123 gtk_widget_set_allocation (widget, allocation);
2125 if (gtk_widget_get_realized (widget))
2127 GdkRectangle position;
2129 if (gtk_notebook_get_event_window_position (notebook, &position))
2131 gdk_window_move_resize (priv->event_window,
2132 position.x, position.y,
2133 position.width, position.height);
2134 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2135 gdk_window_show_unraised (priv->event_window);
2138 gdk_window_hide (priv->event_window);
2143 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2144 GtkNotebookPage *page;
2145 GtkAllocation child_allocation;
2149 child_allocation.x = allocation->x + border_width;
2150 child_allocation.y = allocation->y + border_width;
2151 child_allocation.width = MAX (1, allocation->width - border_width * 2);
2152 child_allocation.height = MAX (1, allocation->height - border_width * 2);
2154 if (priv->show_tabs || priv->show_border)
2156 child_allocation.x += style->xthickness;
2157 child_allocation.y += style->ythickness;
2158 child_allocation.width = MAX (1, child_allocation.width - style->xthickness * 2);
2159 child_allocation.height = MAX (1, child_allocation.height - style->ythickness * 2);
2161 if (priv->show_tabs && priv->children && priv->cur_page)
2166 child_allocation.y += priv->cur_page->requisition.height;
2167 case GTK_POS_BOTTOM:
2168 child_allocation.height =
2169 MAX (1, child_allocation.height -
2170 priv->cur_page->requisition.height);
2173 child_allocation.x += priv->cur_page->requisition.width;
2175 child_allocation.width =
2176 MAX (1, child_allocation.width -
2177 priv->cur_page->requisition.width);
2181 for (i = 0; i < N_ACTION_WIDGETS; i++)
2183 GtkAllocation widget_allocation;
2184 GtkRequisition requisition;
2186 if (!priv->action_widget[i])
2189 widget_allocation.x = allocation->x + border_width;
2190 widget_allocation.y = allocation->y + border_width;
2191 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2193 gtk_widget_get_preferred_size (priv->action_widget[i],
2194 &requisition, NULL);
2198 case GTK_POS_BOTTOM:
2199 widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2202 widget_allocation.width = requisition.width;
2203 widget_allocation.height = priv->cur_page->requisition.height - style->ythickness;
2205 if ((i == ACTION_WIDGET_START && is_rtl) ||
2206 (i == ACTION_WIDGET_END && !is_rtl))
2207 widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2208 if (tab_pos == GTK_POS_TOP) /* no fall through */
2209 widget_allocation.y += 2 * focus_width;
2212 widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2215 widget_allocation.height = requisition.height;
2216 widget_allocation.width = priv->cur_page->requisition.width - style->xthickness;
2218 if (i == ACTION_WIDGET_END)
2219 widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2220 if (tab_pos == GTK_POS_LEFT) /* no fall through */
2221 widget_allocation.x += 2 * focus_width;
2225 gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2230 children = priv->children;
2233 page = children->data;
2234 children = children->next;
2236 if (gtk_widget_get_visible (page->child))
2237 gtk_widget_size_allocate (page->child, &child_allocation);
2240 gtk_notebook_pages_allocate (notebook);
2245 gtk_notebook_draw (GtkWidget *widget,
2248 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2249 GtkNotebookPrivate *priv = notebook->priv;
2250 GtkAllocation allocation;
2254 gtk_widget_get_allocation (widget, &allocation);
2256 window = gtk_widget_get_window (widget);
2257 if (gtk_cairo_should_draw_window (cr, window))
2261 cairo_translate (cr, -allocation.x, -allocation.y);
2262 gtk_notebook_paint (widget, cr);
2266 if (priv->show_tabs)
2268 GtkNotebookPage *page;
2271 for (pages = priv->children; pages; pages = pages->next)
2273 page = GTK_NOTEBOOK_PAGE (pages);
2275 if (gtk_widget_get_parent (page->tab_label) == widget)
2276 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2277 page->tab_label, cr);
2281 if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2282 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2283 priv->cur_page->child,
2285 if (priv->show_tabs)
2287 for (i = 0; i < N_ACTION_WIDGETS; i++)
2289 if (priv->action_widget[i])
2290 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2291 priv->action_widget[i], cr);
2296 if (priv->operation == DRAG_OPERATION_REORDER &&
2297 gtk_cairo_should_draw_window (cr, priv->drag_window))
2300 gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2302 /* FIXME: This is a workaround to make tabs reordering work better
2303 * with engines with rounded tabs. If the drag window background
2304 * isn't set, the rounded corners would be black.
2306 * Ideally, these corners should be made transparent, Either by using
2307 * ARGB visuals or shape windows.
2309 gdk_cairo_set_source_color (cr, >k_widget_get_style (widget)->bg [GTK_STATE_NORMAL]);
2312 gtk_notebook_draw_tab (notebook,
2318 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2319 priv->cur_page->tab_label, cr);
2326 gtk_notebook_show_arrows (GtkNotebook *notebook)
2328 GtkNotebookPrivate *priv = notebook->priv;
2329 gboolean show_arrow = FALSE;
2332 if (!priv->scrollable)
2335 children = priv->children;
2338 GtkNotebookPage *page = children->data;
2340 if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2343 children = children->next;
2350 gtk_notebook_get_arrow_rect (GtkNotebook *notebook,
2351 GdkRectangle *rectangle,
2352 GtkNotebookArrow arrow)
2354 GtkNotebookPrivate *priv = notebook->priv;
2355 GdkRectangle event_window_pos;
2356 gboolean before = ARROW_IS_BEFORE (arrow);
2357 gboolean left = ARROW_IS_LEFT (arrow);
2359 if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2361 gint scroll_arrow_hlength;
2362 gint scroll_arrow_vlength;
2364 gtk_widget_style_get (GTK_WIDGET (notebook),
2365 "scroll-arrow-hlength", &scroll_arrow_hlength,
2366 "scroll-arrow-vlength", &scroll_arrow_vlength,
2369 switch (priv->tab_pos)
2373 rectangle->width = scroll_arrow_vlength;
2374 rectangle->height = scroll_arrow_vlength;
2376 if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2377 (!before && (priv->has_after_previous != priv->has_after_next)))
2378 rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2380 rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2382 rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2383 rectangle->y = event_window_pos.y;
2385 rectangle->y += event_window_pos.height - rectangle->height;
2389 case GTK_POS_BOTTOM:
2390 rectangle->width = scroll_arrow_hlength;
2391 rectangle->height = scroll_arrow_hlength;
2395 if (left || !priv->has_before_previous)
2396 rectangle->x = event_window_pos.x;
2398 rectangle->x = event_window_pos.x + rectangle->width;
2402 if (!left || !priv->has_after_next)
2403 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2405 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2407 rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2413 static GtkNotebookArrow
2414 gtk_notebook_get_arrow (GtkNotebook *notebook,
2418 GtkNotebookPrivate *priv = notebook->priv;
2419 GdkRectangle arrow_rect;
2420 GdkRectangle event_window_pos;
2423 GtkNotebookArrow arrow[4];
2425 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2426 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2427 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2428 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2430 if (gtk_notebook_show_arrows (notebook))
2432 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2433 for (i = 0; i < 4; i++)
2435 if (arrow[i] == ARROW_NONE)
2438 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2440 x0 = x - arrow_rect.x;
2441 y0 = y - arrow_rect.y;
2443 if (y0 >= 0 && y0 < arrow_rect.height &&
2444 x0 >= 0 && x0 < arrow_rect.width)
2453 gtk_notebook_do_arrow (GtkNotebook *notebook,
2454 GtkNotebookArrow arrow)
2456 GtkNotebookPrivate *priv = notebook->priv;
2457 GtkWidget *widget = GTK_WIDGET (notebook);
2458 gboolean is_rtl, left;
2460 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2461 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2462 (!ARROW_IS_LEFT (arrow) && is_rtl);
2464 if (!priv->focus_tab ||
2465 gtk_notebook_search_page (notebook, priv->focus_tab,
2466 left ? STEP_PREV : STEP_NEXT,
2469 gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2470 gtk_widget_grab_focus (widget);
2475 gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2476 GtkNotebookArrow arrow,
2479 GtkNotebookPrivate *priv = notebook->priv;
2480 GtkWidget *widget = GTK_WIDGET (notebook);
2481 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2482 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2483 (!ARROW_IS_LEFT (arrow) && is_rtl);
2485 if (!gtk_widget_has_focus (widget))
2486 gtk_widget_grab_focus (widget);
2488 priv->button = button;
2489 priv->click_child = arrow;
2493 gtk_notebook_do_arrow (notebook, arrow);
2494 gtk_notebook_set_scroll_timer (notebook);
2496 else if (button == 2)
2497 gtk_notebook_page_select (notebook, TRUE);
2498 else if (button == 3)
2499 gtk_notebook_switch_focus_tab (notebook,
2500 gtk_notebook_search_page (notebook,
2502 left ? STEP_NEXT : STEP_PREV,
2504 gtk_notebook_redraw_arrows (notebook);
2510 get_widget_coordinates (GtkWidget *widget,
2515 GdkWindow *window = ((GdkEventAny *)event)->window;
2518 if (!gdk_event_get_coords (event, &tx, &ty))
2521 while (window && window != gtk_widget_get_window (widget))
2523 gint window_x, window_y;
2525 gdk_window_get_position (window, &window_x, &window_y);
2529 window = gdk_window_get_parent (window);
2544 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2546 GtkNotebookPrivate *priv = notebook->priv;
2547 GtkNotebookPage *page;
2550 children = priv->children;
2553 page = children->data;
2555 if (gtk_widget_get_visible (page->child) &&
2556 page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2557 (x >= page->allocation.x) &&
2558 (y >= page->allocation.y) &&
2559 (x <= (page->allocation.x + page->allocation.width)) &&
2560 (y <= (page->allocation.y + page->allocation.height)))
2563 children = children->next;
2570 gtk_notebook_button_press (GtkWidget *widget,
2571 GdkEventButton *event)
2573 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2574 GtkNotebookPrivate *priv = notebook->priv;
2575 GtkNotebookPage *page;
2577 GtkNotebookArrow arrow;
2580 if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2584 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2587 arrow = gtk_notebook_get_arrow (notebook, x, y);
2589 return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2591 if (event->button == 3 && priv->menu)
2593 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2594 NULL, NULL, 3, event->time);
2598 if (event->button != 1)
2601 priv->button = event->button;
2603 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2605 gboolean page_changed, was_focus;
2608 page_changed = page != priv->cur_page;
2609 was_focus = gtk_widget_is_focus (widget);
2611 gtk_notebook_switch_focus_tab (notebook, tab);
2612 gtk_widget_grab_focus (widget);
2614 if (page_changed && !was_focus)
2615 gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2617 /* save press to possibly begin a drag */
2618 if (page->reorderable || page->detachable)
2620 priv->during_detach = FALSE;
2621 priv->during_reorder = FALSE;
2622 priv->pressed_button = event->button;
2627 priv->drag_begin_x = priv->mouse_x;
2628 priv->drag_begin_y = priv->mouse_y;
2629 priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2630 priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2638 popup_position_func (GtkMenu *menu,
2644 GtkNotebook *notebook = data;
2645 GtkNotebookPrivate *priv = notebook->priv;
2646 GtkAllocation allocation;
2648 GtkRequisition requisition;
2650 if (priv->focus_tab)
2652 GtkNotebookPage *page;
2654 page = priv->focus_tab->data;
2655 w = page->tab_label;
2659 w = GTK_WIDGET (notebook);
2662 gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2664 gtk_widget_get_allocation (w, &allocation);
2665 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2666 &requisition, NULL);
2668 if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
2669 *x += allocation.x + allocation.width - requisition.width;
2673 *y += allocation.y + allocation.height;
2679 gtk_notebook_popup_menu (GtkWidget *widget)
2681 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2682 GtkNotebookPrivate *priv = notebook->priv;
2686 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2687 popup_position_func, notebook,
2688 0, gtk_get_current_event_time ());
2689 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
2697 stop_scrolling (GtkNotebook *notebook)
2699 GtkNotebookPrivate *priv = notebook->priv;
2703 g_source_remove (priv->timer);
2705 priv->need_timer = FALSE;
2707 priv->click_child = 0;
2709 gtk_notebook_redraw_arrows (notebook);
2713 get_drop_position (GtkNotebook *notebook,
2716 GtkNotebookPrivate *priv = notebook->priv;
2717 GList *children, *last_child;
2718 GtkNotebookPage *page;
2725 is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
2726 children = priv->children;
2731 page = children->data;
2733 if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
2734 gtk_widget_get_visible (page->child) &&
2736 gtk_widget_get_mapped (page->tab_label) &&
2739 switch (priv->tab_pos)
2742 case GTK_POS_BOTTOM:
2745 if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_X (page) > x) ||
2746 (page->pack == GTK_PACK_END && PAGE_MIDDLE_X (page) < x))
2751 if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_X (page) < x) ||
2752 (page->pack == GTK_PACK_END && PAGE_MIDDLE_X (page) > x))
2759 if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_Y (page) > y) ||
2760 (page->pack == GTK_PACK_END && PAGE_MIDDLE_Y (page) < y))
2766 last_child = children->next;
2769 children = children->next;
2776 show_drag_window (GtkNotebook *notebook,
2777 GtkNotebookPrivate *priv,
2778 GtkNotebookPage *page,
2781 GtkWidget *widget = GTK_WIDGET (notebook);
2783 if (!priv->drag_window)
2785 GdkWindowAttr attributes;
2786 guint attributes_mask;
2788 attributes.x = page->allocation.x;
2789 attributes.y = page->allocation.y;
2790 attributes.width = page->allocation.width;
2791 attributes.height = page->allocation.height;
2792 attributes.window_type = GDK_WINDOW_CHILD;
2793 attributes.wclass = GDK_INPUT_OUTPUT;
2794 attributes.visual = gtk_widget_get_visual (widget);
2795 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2796 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2798 priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
2801 gdk_window_set_user_data (priv->drag_window, widget);
2804 g_object_ref (page->tab_label);
2805 gtk_widget_unparent (page->tab_label);
2806 gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
2807 gtk_widget_set_parent (page->tab_label, widget);
2808 g_object_unref (page->tab_label);
2810 gdk_window_show (priv->drag_window);
2812 /* the grab will dissapear when the window is hidden */
2813 gdk_device_grab (device, priv->drag_window,
2814 GDK_OWNERSHIP_WINDOW, FALSE,
2815 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2816 NULL, GDK_CURRENT_TIME);
2819 /* This function undoes the reparenting that happens both when drag_window
2820 * is shown for reordering and when the DnD icon is shown for detaching
2823 hide_drag_window (GtkNotebook *notebook,
2824 GtkNotebookPrivate *priv,
2825 GtkNotebookPage *page)
2827 GtkWidget *widget = GTK_WIDGET (notebook);
2828 GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
2830 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
2831 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
2833 g_object_ref (page->tab_label);
2835 if (GTK_IS_WINDOW (parent))
2837 /* parent widget is the drag window */
2838 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
2841 gtk_widget_unparent (page->tab_label);
2843 gtk_widget_set_parent (page->tab_label, widget);
2844 g_object_unref (page->tab_label);
2847 if (priv->drag_window &&
2848 gdk_window_is_visible (priv->drag_window))
2849 gdk_window_hide (priv->drag_window);
2853 gtk_notebook_stop_reorder (GtkNotebook *notebook)
2855 GtkNotebookPrivate *priv = notebook->priv;
2856 GtkNotebookPage *page;
2858 if (priv->operation == DRAG_OPERATION_DETACH)
2859 page = priv->detached_tab;
2861 page = priv->cur_page;
2863 if (!page || !page->tab_label)
2866 priv->pressed_button = -1;
2868 if (page->reorderable || page->detachable)
2870 if (priv->during_reorder)
2872 gint old_page_num, page_num;
2875 element = get_drop_position (notebook, page->pack);
2876 old_page_num = g_list_position (priv->children, priv->focus_tab);
2877 page_num = reorder_tab (notebook, element, priv->focus_tab);
2878 gtk_notebook_child_reordered (notebook, page);
2880 if (priv->has_scrolled || old_page_num != page_num)
2881 g_signal_emit (notebook,
2882 notebook_signals[PAGE_REORDERED], 0,
2883 page->child, page_num);
2885 priv->has_scrolled = FALSE;
2886 priv->during_reorder = FALSE;
2889 hide_drag_window (notebook, priv, page);
2891 priv->operation = DRAG_OPERATION_NONE;
2892 gtk_notebook_pages_allocate (notebook);
2894 if (priv->dnd_timer)
2896 g_source_remove (priv->dnd_timer);
2897 priv->dnd_timer = 0;
2903 gtk_notebook_button_release (GtkWidget *widget,
2904 GdkEventButton *event)
2906 GtkNotebook *notebook;
2907 GtkNotebookPrivate *priv;
2908 GtkNotebookPage *page;
2910 if (event->type != GDK_BUTTON_RELEASE)
2913 notebook = GTK_NOTEBOOK (widget);
2914 priv = notebook->priv;
2916 page = priv->cur_page;
2918 if (!priv->during_detach &&
2919 page->reorderable &&
2920 event->button == priv->pressed_button)
2921 gtk_notebook_stop_reorder (notebook);
2923 if (event->button == priv->button)
2925 stop_scrolling (notebook);
2933 gtk_notebook_leave_notify (GtkWidget *widget,
2934 GdkEventCrossing *event)
2936 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2937 GtkNotebookPrivate *priv = notebook->priv;
2940 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2946 gtk_notebook_redraw_arrows (notebook);
2952 static GtkNotebookPointerPosition
2953 get_pointer_position (GtkNotebook *notebook)
2955 GtkNotebookPrivate *priv = notebook->priv;
2956 GtkWidget *widget = GTK_WIDGET (notebook);
2957 gint wx, wy, width, height;
2960 if (!priv->scrollable)
2961 return POINTER_BETWEEN;
2963 gdk_window_get_position (priv->event_window, &wx, &wy);
2964 width = gdk_window_get_width (priv->event_window);
2965 height = gdk_window_get_height (priv->event_window);
2967 if (priv->tab_pos == GTK_POS_TOP ||
2968 priv->tab_pos == GTK_POS_BOTTOM)
2972 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2973 x = priv->mouse_x - wx;
2975 if (x > width - SCROLL_THRESHOLD)
2976 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
2977 else if (x < SCROLL_THRESHOLD)
2978 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
2980 return POINTER_BETWEEN;
2986 y = priv->mouse_y - wy;
2987 if (y > height - SCROLL_THRESHOLD)
2988 return POINTER_AFTER;
2989 else if (y < SCROLL_THRESHOLD)
2990 return POINTER_BEFORE;
2992 return POINTER_BETWEEN;
2997 scroll_notebook_timer (gpointer data)
2999 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3000 GtkNotebookPrivate *priv = notebook->priv;
3001 GtkNotebookPointerPosition pointer_position;
3002 GList *element, *first_tab;
3004 pointer_position = get_pointer_position (notebook);
3006 element = get_drop_position (notebook, priv->cur_page->pack);
3007 reorder_tab (notebook, element, priv->focus_tab);
3008 first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3009 (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3013 priv->first_tab = first_tab;
3014 gtk_notebook_pages_allocate (notebook);
3016 gdk_window_move_resize (priv->drag_window,
3017 priv->drag_window_x,
3018 priv->drag_window_y,
3019 priv->cur_page->allocation.width,
3020 priv->cur_page->allocation.height);
3021 gdk_window_raise (priv->drag_window);
3028 check_threshold (GtkNotebook *notebook,
3032 GtkNotebookPrivate *priv = notebook->priv;
3035 GdkRectangle rectangle = { 0, }; /* shut up gcc */
3036 GtkSettings *settings;
3038 widget = GTK_WIDGET (notebook);
3039 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3040 g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3042 /* we want a large threshold */
3043 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3045 gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3046 rectangle.width = gdk_window_get_width (priv->event_window);
3047 rectangle.height = gdk_window_get_height (priv->event_window);
3049 rectangle.x -= dnd_threshold;
3050 rectangle.width += 2 * dnd_threshold;
3051 rectangle.y -= dnd_threshold;
3052 rectangle.height += 2 * dnd_threshold;
3054 return (current_x < rectangle.x ||
3055 current_x > rectangle.x + rectangle.width ||
3056 current_y < rectangle.y ||
3057 current_y > rectangle.y + rectangle.height);
3061 gtk_notebook_motion_notify (GtkWidget *widget,
3062 GdkEventMotion *event)
3064 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3065 GtkNotebookPrivate *priv = notebook->priv;
3066 GtkNotebookPage *page;
3067 GtkNotebookArrow arrow;
3068 GtkNotebookPointerPosition pointer_position;
3069 GtkSettings *settings;
3073 page = priv->cur_page;
3078 if (!(event->state & GDK_BUTTON1_MASK) &&
3079 priv->pressed_button != -1)
3081 gtk_notebook_stop_reorder (notebook);
3082 stop_scrolling (notebook);
3085 if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3088 priv->timestamp = event->time;
3090 /* While animating the move, event->x is relative to the flying tab
3091 * (priv->drag_window has a pointer grab), but we need coordinates relative to
3092 * the notebook widget.
3094 gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3095 priv->mouse_x = event->x_root - x_win;
3096 priv->mouse_y = event->y_root - y_win;
3098 arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3099 if (arrow != priv->in_child)
3101 priv->in_child = arrow;
3102 gtk_notebook_redraw_arrows (notebook);
3105 if (priv->pressed_button == -1)
3108 if (page->detachable &&
3109 check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3111 priv->detached_tab = priv->cur_page;
3112 priv->during_detach = TRUE;
3114 gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3115 priv->pressed_button, (GdkEvent*) event);
3119 if (page->reorderable &&
3120 (priv->during_reorder ||
3121 gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3123 priv->during_reorder = TRUE;
3124 pointer_position = get_pointer_position (notebook);
3126 if (event->window == priv->drag_window &&
3127 pointer_position != POINTER_BETWEEN &&
3128 gtk_notebook_show_arrows (notebook))
3131 if (!priv->dnd_timer)
3133 priv->has_scrolled = TRUE;
3134 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3135 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3137 priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3138 scroll_notebook_timer,
3139 (gpointer) notebook);
3144 if (priv->dnd_timer)
3146 g_source_remove (priv->dnd_timer);
3147 priv->dnd_timer = 0;
3151 if (event->window == priv->drag_window ||
3152 priv->operation != DRAG_OPERATION_REORDER)
3154 /* the drag operation is beginning, create the window */
3155 if (priv->operation != DRAG_OPERATION_REORDER)
3157 priv->operation = DRAG_OPERATION_REORDER;
3158 show_drag_window (notebook, priv, page, event->device);
3161 gtk_notebook_pages_allocate (notebook);
3162 gdk_window_move_resize (priv->drag_window,
3163 priv->drag_window_x,
3164 priv->drag_window_y,
3165 page->allocation.width,
3166 page->allocation.height);
3174 gtk_notebook_grab_notify (GtkWidget *widget,
3175 gboolean was_grabbed)
3177 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3181 gtk_notebook_stop_reorder (notebook);
3182 stop_scrolling (notebook);
3187 gtk_notebook_state_changed (GtkWidget *widget,
3188 GtkStateType previous_state)
3190 if (!gtk_widget_is_sensitive (widget))
3191 stop_scrolling (GTK_NOTEBOOK (widget));
3195 gtk_notebook_focus_in (GtkWidget *widget,
3196 GdkEventFocus *event)
3198 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3204 gtk_notebook_focus_out (GtkWidget *widget,
3205 GdkEventFocus *event)
3207 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3213 gtk_notebook_style_set (GtkWidget *widget,
3216 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3217 GtkNotebookPrivate *priv = notebook->priv;
3219 gboolean has_before_previous;
3220 gboolean has_before_next;
3221 gboolean has_after_previous;
3222 gboolean has_after_next;
3224 gtk_widget_style_get (widget,
3225 "has-backward-stepper", &has_before_previous,
3226 "has-secondary-forward-stepper", &has_before_next,
3227 "has-secondary-backward-stepper", &has_after_previous,
3228 "has-forward-stepper", &has_after_next,
3231 priv->has_before_previous = has_before_previous;
3232 priv->has_before_next = has_before_next;
3233 priv->has_after_previous = has_after_previous;
3234 priv->has_after_next = has_after_next;
3236 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_set (widget, previous);
3240 on_drag_icon_draw (GtkWidget *widget,
3244 GtkWidget *notebook, *child;
3245 GtkRequisition requisition;
3248 notebook = GTK_WIDGET (data);
3249 child = gtk_bin_get_child (GTK_BIN (widget));
3251 gtk_widget_get_preferred_size (widget,
3252 &requisition, NULL);
3253 gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3255 gtk_paint_extension (gtk_widget_get_style (notebook),
3257 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3260 requisition.width, requisition.height,
3263 gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3269 gtk_notebook_drag_begin (GtkWidget *widget,
3270 GdkDragContext *context)
3272 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3273 GtkNotebookPrivate *priv = notebook->priv;
3274 GtkWidget *tab_label;
3276 if (priv->dnd_timer)
3278 g_source_remove (priv->dnd_timer);
3279 priv->dnd_timer = 0;
3282 priv->operation = DRAG_OPERATION_DETACH;
3283 gtk_notebook_pages_allocate (notebook);
3285 tab_label = priv->detached_tab->tab_label;
3287 hide_drag_window (notebook, priv, priv->cur_page);
3288 g_object_ref (tab_label);
3289 gtk_widget_unparent (tab_label);
3291 priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3292 gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3293 gtk_widget_get_screen (widget));
3294 gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3295 gtk_widget_set_size_request (priv->dnd_window,
3296 priv->detached_tab->allocation.width,
3297 priv->detached_tab->allocation.height);
3298 g_object_unref (tab_label);
3300 g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3301 G_CALLBACK (on_drag_icon_draw), notebook);
3303 gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3307 gtk_notebook_drag_end (GtkWidget *widget,
3308 GdkDragContext *context)
3310 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3311 GtkNotebookPrivate *priv = notebook->priv;
3313 gtk_notebook_stop_reorder (notebook);
3315 if (priv->detached_tab)
3316 gtk_notebook_switch_page (notebook, priv->detached_tab);
3318 _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3319 gtk_widget_destroy (priv->dnd_window);
3320 priv->dnd_window = NULL;
3322 priv->operation = DRAG_OPERATION_NONE;
3325 static GtkNotebook *
3326 gtk_notebook_create_window (GtkNotebook *notebook,
3335 gtk_notebook_drag_failed (GtkWidget *widget,
3336 GdkDragContext *context,
3337 GtkDragResult result,
3340 if (result == GTK_DRAG_RESULT_NO_TARGET)
3342 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3343 GtkNotebookPrivate *priv = notebook->priv;
3344 GtkNotebook *dest_notebook = NULL;
3345 GdkDisplay *display;
3348 display = gtk_widget_get_display (widget);
3349 gdk_display_get_pointer (display, NULL, &x, &y, NULL);
3351 g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3352 priv->detached_tab->child, x, y, &dest_notebook);
3355 do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3364 gtk_notebook_switch_tab_timeout (gpointer data)
3366 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3367 GtkNotebookPrivate *priv = notebook->priv;
3371 priv->switch_tab_timer = 0;
3375 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
3377 /* FIXME: hack, we don't want the
3378 * focus to move fom the source widget
3380 priv->child_has_focus = FALSE;
3381 gtk_notebook_switch_focus_tab (notebook, tab);
3388 gtk_notebook_drag_motion (GtkWidget *widget,
3389 GdkDragContext *context,
3394 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3395 GtkNotebookPrivate *priv = notebook->priv;
3396 GtkAllocation allocation;
3397 GdkRectangle position;
3398 GtkSettings *settings;
3399 GtkNotebookArrow arrow;
3401 GdkAtom target, tab_target;
3403 gtk_widget_get_allocation (widget, &allocation);
3405 arrow = gtk_notebook_get_arrow (notebook,
3410 priv->click_child = arrow;
3411 gtk_notebook_set_scroll_timer (notebook);
3412 gdk_drag_status (context, 0, time);
3416 stop_scrolling (notebook);
3417 target = gtk_drag_dest_find_target (widget, context, NULL);
3418 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3420 if (target == tab_target)
3422 GQuark group, source_group;
3423 GtkNotebook *source;
3424 GtkWidget *source_child;
3426 source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3427 source_child = source->priv->cur_page->child;
3429 group = notebook->priv->group;
3430 source_group = source->priv->group;
3432 if (group != 0 && group == source_group &&
3433 !(widget == source_child ||
3434 gtk_widget_is_ancestor (widget, source_child)))
3436 gdk_drag_status (context, GDK_ACTION_MOVE, time);
3441 /* it's a tab, but doesn't share
3442 * ID with this notebook */
3443 gdk_drag_status (context, 0, time);
3450 if (gtk_notebook_get_event_window_position (notebook, &position) &&
3451 x >= position.x && x <= position.x + position.width &&
3452 y >= position.y && y <= position.y + position.height)
3457 if (!priv->switch_tab_timer)
3459 settings = gtk_widget_get_settings (widget);
3461 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3462 priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3463 gtk_notebook_switch_tab_timeout,
3469 if (priv->switch_tab_timer)
3471 g_source_remove (priv->switch_tab_timer);
3472 priv->switch_tab_timer = 0;
3476 return (target == tab_target) ? TRUE : FALSE;
3480 gtk_notebook_drag_leave (GtkWidget *widget,
3481 GdkDragContext *context,
3484 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3485 GtkNotebookPrivate *priv = notebook->priv;
3487 if (priv->switch_tab_timer)
3489 g_source_remove (priv->switch_tab_timer);
3490 priv->switch_tab_timer = 0;
3493 stop_scrolling (GTK_NOTEBOOK (widget));
3497 gtk_notebook_drag_drop (GtkWidget *widget,
3498 GdkDragContext *context,
3503 GdkAtom target, tab_target;
3505 target = gtk_drag_dest_find_target (widget, context, NULL);
3506 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3508 if (target == tab_target)
3510 gtk_drag_get_data (widget, context, target, time);
3518 do_detach_tab (GtkNotebook *from,
3524 GtkNotebookPrivate *to_priv = to->priv;
3525 GtkAllocation to_allocation;
3526 GtkWidget *tab_label, *menu_label;
3527 gboolean tab_expand, tab_fill, reorderable, detachable;
3532 menu_label = gtk_notebook_get_menu_label (from, child);
3535 g_object_ref (menu_label);
3537 tab_label = gtk_notebook_get_tab_label (from, child);
3540 g_object_ref (tab_label);
3542 g_object_ref (child);
3544 gtk_container_child_get (GTK_CONTAINER (from),
3546 "tab-expand", &tab_expand,
3547 "tab-fill", &tab_fill,
3548 "tab-pack", &tab_pack,
3549 "reorderable", &reorderable,
3550 "detachable", &detachable,
3553 gtk_container_remove (GTK_CONTAINER (from), child);
3555 gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3556 to_priv->mouse_x = x + to_allocation.x;
3557 to_priv->mouse_y = y + to_allocation.y;
3559 element = get_drop_position (to, tab_pack);
3560 page_num = g_list_position (to_priv->children, element);
3561 gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3563 gtk_container_child_set (GTK_CONTAINER (to), child,
3564 "tab-pack", tab_pack,
3565 "tab-expand", tab_expand,
3566 "tab-fill", tab_fill,
3567 "reorderable", reorderable,
3568 "detachable", detachable,
3571 g_object_unref (child);
3574 g_object_unref (tab_label);
3577 g_object_unref (menu_label);
3579 gtk_notebook_set_current_page (to, page_num);
3583 gtk_notebook_drag_data_get (GtkWidget *widget,
3584 GdkDragContext *context,
3585 GtkSelectionData *data,
3589 if (data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3591 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3592 GtkNotebookPrivate *priv = notebook->priv;
3594 gtk_selection_data_set (data,
3597 (void*) &priv->detached_tab->child,
3603 gtk_notebook_drag_data_received (GtkWidget *widget,
3604 GdkDragContext *context,
3607 GtkSelectionData *data,
3611 GtkNotebook *notebook;
3612 GtkWidget *source_widget;
3615 notebook = GTK_NOTEBOOK (widget);
3616 source_widget = gtk_drag_get_source_widget (context);
3618 if (source_widget &&
3619 data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3621 child = (void*) data->data;
3623 do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3624 gtk_drag_finish (context, TRUE, FALSE, time);
3627 gtk_drag_finish (context, FALSE, FALSE, time);
3630 /* Private GtkContainer Methods :
3632 * gtk_notebook_set_child_arg
3633 * gtk_notebook_get_child_arg
3635 * gtk_notebook_remove
3636 * gtk_notebook_focus
3637 * gtk_notebook_set_focus_child
3638 * gtk_notebook_child_type
3639 * gtk_notebook_forall
3642 gtk_notebook_set_child_property (GtkContainer *container,
3645 const GValue *value,
3650 GtkPackType pack_type;
3652 /* not finding child's page is valid for menus or labels */
3653 if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3656 switch (property_id)
3658 case CHILD_PROP_TAB_LABEL:
3659 /* a NULL pointer indicates a default_tab setting, otherwise
3660 * we need to set the associated label
3662 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
3663 g_value_get_string (value));
3665 case CHILD_PROP_MENU_LABEL:
3666 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
3667 g_value_get_string (value));
3669 case CHILD_PROP_POSITION:
3670 gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
3671 g_value_get_int (value));
3673 case CHILD_PROP_TAB_EXPAND:
3674 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3675 &expand, &fill, &pack_type);
3676 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3677 g_value_get_boolean (value),
3680 case CHILD_PROP_TAB_FILL:
3681 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3682 &expand, &fill, &pack_type);
3683 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3685 g_value_get_boolean (value),
3688 case CHILD_PROP_TAB_PACK:
3689 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3690 &expand, &fill, &pack_type);
3691 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3693 g_value_get_enum (value));
3695 case CHILD_PROP_REORDERABLE:
3696 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
3697 g_value_get_boolean (value));
3699 case CHILD_PROP_DETACHABLE:
3700 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
3701 g_value_get_boolean (value));
3704 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3710 gtk_notebook_get_child_property (GtkContainer *container,
3716 GtkNotebook *notebook = GTK_NOTEBOOK (container);
3717 GtkNotebookPrivate *priv = notebook->priv;
3722 GtkPackType pack_type;
3724 /* not finding child's page is valid for menus or labels */
3725 list = gtk_notebook_find_child (notebook, child, NULL);
3728 /* nothing to set on labels or menus */
3729 g_param_value_set_default (pspec, value);
3733 switch (property_id)
3735 case CHILD_PROP_TAB_LABEL:
3736 label = gtk_notebook_get_tab_label (notebook, child);
3738 if (GTK_IS_LABEL (label))
3739 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
3741 g_value_set_string (value, NULL);
3743 case CHILD_PROP_MENU_LABEL:
3744 label = gtk_notebook_get_menu_label (notebook, child);
3746 if (GTK_IS_LABEL (label))
3747 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
3749 g_value_set_string (value, NULL);
3751 case CHILD_PROP_POSITION:
3752 g_value_set_int (value, g_list_position (priv->children, list));
3754 case CHILD_PROP_TAB_EXPAND:
3755 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3756 &expand, NULL, NULL);
3757 g_value_set_boolean (value, expand);
3759 case CHILD_PROP_TAB_FILL:
3760 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3762 g_value_set_boolean (value, fill);
3764 case CHILD_PROP_TAB_PACK:
3765 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3766 NULL, NULL, &pack_type);
3767 g_value_set_enum (value, pack_type);
3769 case CHILD_PROP_REORDERABLE:
3770 g_value_set_boolean (value,
3771 gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
3773 case CHILD_PROP_DETACHABLE:
3774 g_value_set_boolean (value,
3775 gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
3778 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3784 gtk_notebook_add (GtkContainer *container,
3787 gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget,
3792 gtk_notebook_remove (GtkContainer *container,
3795 GtkNotebook *notebook = GTK_NOTEBOOK (container);
3796 GtkNotebookPrivate *priv = notebook->priv;
3797 GtkNotebookPage *page;
3801 children = priv->children;
3804 page = children->data;
3806 if (page->child == widget)
3810 children = children->next;
3813 if (children == NULL)
3816 g_object_ref (widget);
3818 gtk_notebook_real_remove (notebook, children);
3820 g_signal_emit (notebook,
3821 notebook_signals[PAGE_REMOVED],
3826 g_object_unref (widget);
3830 focus_tabs_in (GtkNotebook *notebook)
3832 GtkNotebookPrivate *priv = notebook->priv;
3834 if (priv->show_tabs && priv->cur_page)
3836 gtk_widget_grab_focus (GTK_WIDGET (notebook));
3838 gtk_notebook_switch_focus_tab (notebook,
3839 g_list_find (priv->children,
3849 focus_tabs_move (GtkNotebook *notebook,
3850 GtkDirectionType direction,
3851 gint search_direction)
3853 GtkNotebookPrivate *priv = notebook->priv;
3856 new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
3857 search_direction, TRUE);
3860 gboolean wrap_around;
3862 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
3863 "gtk-keynav-wrap-around", &wrap_around,
3867 new_page = gtk_notebook_search_page (notebook, NULL,
3868 search_direction, TRUE);
3872 gtk_notebook_switch_focus_tab (notebook, new_page);
3874 gtk_widget_error_bell (GTK_WIDGET (notebook));
3880 focus_child_in (GtkNotebook *notebook,
3881 GtkDirectionType direction)
3883 GtkNotebookPrivate *priv = notebook->priv;
3886 return gtk_widget_child_focus (priv->cur_page->child, direction);
3892 focus_action_in (GtkNotebook *notebook,
3894 GtkDirectionType direction)
3896 GtkNotebookPrivate *priv = notebook->priv;
3898 if (priv->action_widget[action] &&
3899 gtk_widget_get_visible (priv->action_widget[action]))
3900 return gtk_widget_child_focus (priv->action_widget[action], direction);
3905 /* Focus in the notebook can either be on the pages, or on
3906 * the tabs or on the action_widgets.
3909 gtk_notebook_focus (GtkWidget *widget,
3910 GtkDirectionType direction)
3912 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3913 GtkNotebookPrivate *priv = notebook->priv;
3914 GtkWidget *old_focus_child;
3915 GtkDirectionType effective_direction;
3919 gboolean widget_is_focus;
3920 GtkContainer *container;
3922 container = GTK_CONTAINER (widget);
3924 if (priv->tab_pos == GTK_POS_TOP ||
3925 priv->tab_pos == GTK_POS_LEFT)
3927 first_action = ACTION_WIDGET_START;
3928 last_action = ACTION_WIDGET_END;
3932 first_action = ACTION_WIDGET_END;
3933 last_action = ACTION_WIDGET_START;
3936 if (priv->focus_out)
3938 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
3942 widget_is_focus = gtk_widget_is_focus (widget);
3943 old_focus_child = gtk_container_get_focus_child (container);
3945 effective_direction = get_effective_direction (notebook, direction);
3947 if (old_focus_child) /* Focus on page child or action widget */
3949 if (gtk_widget_child_focus (old_focus_child, direction))
3952 if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
3954 switch (effective_direction)
3957 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3959 return focus_tabs_in (notebook);
3967 case GTK_DIR_TAB_FORWARD:
3968 if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
3969 focus_child_in (notebook, direction))
3971 return focus_tabs_in (notebook);
3972 case GTK_DIR_TAB_BACKWARD:
3975 g_assert_not_reached ();
3979 else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
3981 switch (effective_direction)
3984 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
3988 return focus_tabs_in (notebook);
3994 case GTK_DIR_TAB_FORWARD:
3996 case GTK_DIR_TAB_BACKWARD:
3997 if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
3998 focus_child_in (notebook, direction))
4000 return focus_tabs_in (notebook);
4002 g_assert_not_reached ();
4008 switch (effective_direction)
4010 case GTK_DIR_TAB_BACKWARD:
4012 /* Focus onto the tabs */
4013 return focus_tabs_in (notebook);
4018 case GTK_DIR_TAB_FORWARD:
4019 return focus_action_in (notebook, last_action, direction);
4023 else if (widget_is_focus) /* Focus was on tabs */
4025 switch (effective_direction)
4027 case GTK_DIR_TAB_BACKWARD:
4028 return focus_action_in (notebook, first_action, direction);
4031 case GTK_DIR_TAB_FORWARD:
4032 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4034 return focus_action_in (notebook, last_action, direction);
4036 /* We use TAB_FORWARD rather than direction so that we focus a more
4037 * predictable widget for the user; users may be using arrow focusing
4038 * in this situation even if they don't usually use arrow focusing.
4040 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4042 return focus_tabs_move (notebook, direction, STEP_PREV);
4044 return focus_tabs_move (notebook, direction, STEP_NEXT);
4047 else /* Focus was not on widget */
4049 switch (effective_direction)
4051 case GTK_DIR_TAB_FORWARD:
4053 if (focus_action_in (notebook, first_action, direction))
4055 if (focus_tabs_in (notebook))
4057 if (focus_action_in (notebook, last_action, direction))
4059 if (focus_child_in (notebook, direction))
4062 case GTK_DIR_TAB_BACKWARD:
4063 if (focus_action_in (notebook, last_action, direction))
4065 if (focus_child_in (notebook, direction))
4067 if (focus_tabs_in (notebook))
4069 if (focus_action_in (notebook, first_action, direction))
4074 return focus_child_in (notebook, direction);
4078 g_assert_not_reached ();
4083 gtk_notebook_set_focus_child (GtkContainer *container,
4086 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4087 GtkNotebookPrivate *priv = notebook->priv;
4088 GtkWidget *page_child;
4089 GtkWidget *toplevel;
4091 /* If the old focus widget was within a page of the notebook,
4092 * (child may either be NULL or not in this case), record it
4093 * for future use if we switch to the page with a mnemonic.
4096 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4097 if (toplevel && gtk_widget_is_toplevel (toplevel))
4099 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4102 if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4104 GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4107 GtkNotebookPage *page = list->data;
4109 if (page->last_focus_child)
4110 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4112 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4113 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4119 page_child = gtk_widget_get_parent (page_child);
4125 g_return_if_fail (GTK_IS_WIDGET (child));
4127 priv->child_has_focus = TRUE;
4128 if (!priv->focus_tab)
4131 GtkNotebookPage *page;
4133 children = priv->children;
4136 page = children->data;
4137 if (page->child == child || page->tab_label == child)
4138 gtk_notebook_switch_focus_tab (notebook, children);
4139 children = children->next;
4144 priv->child_has_focus = FALSE;
4146 GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4150 gtk_notebook_forall (GtkContainer *container,
4151 gboolean include_internals,
4152 GtkCallback callback,
4153 gpointer callback_data)
4155 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4156 GtkNotebookPrivate *priv = notebook->priv;
4160 children = priv->children;
4163 GtkNotebookPage *page;
4165 page = children->data;
4166 children = children->next;
4167 (* callback) (page->child, callback_data);
4169 if (include_internals)
4171 if (page->tab_label)
4172 (* callback) (page->tab_label, callback_data);
4176 if (include_internals) {
4177 for (i = 0; i < N_ACTION_WIDGETS; i++)
4179 if (priv->action_widget[i])
4180 (* callback) (priv->action_widget[i], callback_data);
4186 gtk_notebook_child_type (GtkContainer *container)
4188 return GTK_TYPE_WIDGET;
4191 /* Private GtkNotebook Methods:
4193 * gtk_notebook_real_insert_page
4196 page_visible_cb (GtkWidget *page,
4200 GtkNotebook *notebook = GTK_NOTEBOOK (data);
4201 GtkNotebookPrivate *priv = notebook->priv;
4205 if (priv->cur_page &&
4206 priv->cur_page->child == page &&
4207 !gtk_widget_get_visible (page))
4209 list = g_list_find (priv->children, priv->cur_page);
4212 next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4214 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4218 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4223 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4225 GtkWidget *tab_label,
4226 GtkWidget *menu_label,
4229 GtkNotebookPrivate *priv = notebook->priv;
4230 GtkNotebookPage *page;
4233 gtk_widget_freeze_child_notify (child);
4235 page = g_slice_new0 (GtkNotebookPage);
4236 page->child = child;
4238 nchildren = g_list_length (priv->children);
4239 if ((position < 0) || (position > nchildren))
4240 position = nchildren;
4242 priv->children = g_list_insert (priv->children, page, position);
4246 page->default_tab = TRUE;
4247 if (priv->show_tabs)
4248 tab_label = gtk_label_new (NULL);
4250 page->tab_label = tab_label;
4251 page->menu_label = menu_label;
4252 page->expand = FALSE;
4254 page->pack = GTK_PACK_START;
4257 page->default_menu = TRUE;
4259 g_object_ref_sink (page->menu_label);
4262 gtk_notebook_menu_item_create (notebook,
4263 g_list_find (priv->children, page));
4265 gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4267 gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4269 gtk_notebook_update_labels (notebook);
4271 if (!priv->first_tab)
4272 priv->first_tab = priv->children;
4274 /* child visible will be turned on by switch_page below */
4275 if (priv->cur_page != page)
4276 gtk_widget_set_child_visible (child, FALSE);
4280 if (priv->show_tabs && gtk_widget_get_visible (child))
4281 gtk_widget_show (tab_label);
4283 gtk_widget_hide (tab_label);
4285 page->mnemonic_activate_signal =
4286 g_signal_connect (tab_label,
4287 "mnemonic-activate",
4288 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4292 page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4293 G_CALLBACK (page_visible_cb), notebook);
4295 g_signal_emit (notebook,
4296 notebook_signals[PAGE_ADDED],
4301 if (!priv->cur_page)
4303 gtk_notebook_switch_page (notebook, page);
4304 /* focus_tab is set in the switch_page method */
4305 gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4308 gtk_notebook_update_tab_states (notebook);
4310 if (priv->scrollable)
4311 gtk_notebook_redraw_arrows (notebook);
4313 gtk_widget_child_notify (child, "tab-expand");
4314 gtk_widget_child_notify (child, "tab-fill");
4315 gtk_widget_child_notify (child, "tab-pack");
4316 gtk_widget_child_notify (child, "tab-label");
4317 gtk_widget_child_notify (child, "menu-label");
4318 gtk_widget_child_notify (child, "position");
4319 gtk_widget_thaw_child_notify (child);
4321 /* The page-added handler might have reordered the pages, re-get the position */
4322 return gtk_notebook_page_num (notebook, child);
4325 /* Private GtkNotebook Functions:
4327 * gtk_notebook_redraw_tabs
4328 * gtk_notebook_real_remove
4329 * gtk_notebook_update_labels
4330 * gtk_notebook_timer
4331 * gtk_notebook_set_scroll_timer
4332 * gtk_notebook_page_compare
4333 * gtk_notebook_real_page_position
4334 * gtk_notebook_search_page
4337 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4339 GtkNotebookPrivate *priv = notebook->priv;
4340 GtkAllocation allocation;
4342 GtkNotebookPage *page;
4344 GdkRectangle redraw_rect;
4346 gint tab_pos = get_effective_tab_pos (notebook);
4348 widget = GTK_WIDGET (notebook);
4349 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4351 if (!gtk_widget_get_mapped (widget) || !priv->first_tab)
4354 page = priv->first_tab->data;
4356 redraw_rect.x = border;
4357 redraw_rect.y = border;
4359 style = gtk_widget_get_style (widget);
4360 gtk_widget_get_allocation (widget, &allocation);
4364 case GTK_POS_BOTTOM:
4365 redraw_rect.y = allocation.height - border -
4366 page->allocation.height - style->ythickness;
4368 if (page != priv->cur_page)
4369 redraw_rect.y -= style->ythickness;
4372 redraw_rect.width = allocation.width - 2 * border;
4373 redraw_rect.height = page->allocation.height + style->ythickness;
4375 if (page != priv->cur_page)
4376 redraw_rect.height += style->ythickness;
4379 redraw_rect.x = allocation.width - border -
4380 page->allocation.width - style->xthickness;
4382 if (page != priv->cur_page)
4383 redraw_rect.x -= style->xthickness;
4386 redraw_rect.width = page->allocation.width + style->xthickness;
4387 redraw_rect.height = allocation.height - 2 * border;
4389 if (page != priv->cur_page)
4390 redraw_rect.width += style->xthickness;
4394 redraw_rect.x += allocation.x;
4395 redraw_rect.y += allocation.y;
4397 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4398 &redraw_rect, TRUE);
4402 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4404 GtkNotebookPrivate *priv = notebook->priv;
4406 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4407 gtk_notebook_show_arrows (notebook))
4411 GtkNotebookArrow arrow[4];
4413 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4414 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4415 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4416 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4418 for (i = 0; i < 4; i++)
4420 if (arrow[i] == ARROW_NONE)
4423 gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4424 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4431 gtk_notebook_timer (GtkNotebook *notebook)
4433 GtkNotebookPrivate *priv = notebook->priv;
4434 gboolean retval = FALSE;
4438 gtk_notebook_do_arrow (notebook, priv->click_child);
4440 if (priv->need_timer)
4442 GtkSettings *settings;
4445 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4446 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4448 priv->need_timer = FALSE;
4449 priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4450 (GSourceFunc) gtk_notebook_timer,
4451 (gpointer) notebook);
4461 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4463 GtkNotebookPrivate *priv = notebook->priv;
4464 GtkWidget *widget = GTK_WIDGET (notebook);
4468 GtkSettings *settings = gtk_widget_get_settings (widget);
4471 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4473 priv->timer = gdk_threads_add_timeout (timeout,
4474 (GSourceFunc) gtk_notebook_timer,
4475 (gpointer) notebook);
4476 priv->need_timer = TRUE;
4481 gtk_notebook_page_compare (gconstpointer a,
4484 return (((GtkNotebookPage *) a)->child != b);
4488 gtk_notebook_find_child (GtkNotebook *notebook,
4490 const gchar *function)
4492 GtkNotebookPrivate *priv = notebook->priv;
4493 GList *list = g_list_find_custom (priv->children, child,
4494 gtk_notebook_page_compare);
4496 #ifndef G_DISABLE_CHECKS
4497 if (!list && function)
4498 g_warning ("%s: unable to find child %p in notebook %p",
4499 function, child, notebook);
4506 gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4507 GtkNotebookPage *page)
4509 if (page->tab_label)
4511 if (page->mnemonic_activate_signal)
4512 g_signal_handler_disconnect (page->tab_label,
4513 page->mnemonic_activate_signal);
4514 page->mnemonic_activate_signal = 0;
4516 gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
4517 gtk_widget_unparent (page->tab_label);
4518 page->tab_label = NULL;
4523 gtk_notebook_real_remove (GtkNotebook *notebook,
4526 GtkNotebookPrivate *priv = notebook->priv;
4527 GtkNotebookPage *page;
4529 gint need_resize = FALSE;
4530 GtkWidget *tab_label;
4532 gboolean destroying;
4534 destroying = GTK_OBJECT_FLAGS (notebook) & GTK_IN_DESTRUCTION;
4536 next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4538 next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4540 priv->children = g_list_remove_link (priv->children, list);
4542 if (priv->cur_page == list->data)
4544 priv->cur_page = NULL;
4545 if (next_list && !destroying)
4546 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4549 if (priv->detached_tab == list->data)
4550 priv->detached_tab = NULL;
4552 if (list == priv->first_tab)
4553 priv->first_tab = next_list;
4554 if (list == priv->focus_tab && !destroying)
4555 gtk_notebook_switch_focus_tab (notebook, next_list);
4559 g_signal_handler_disconnect (page->child, page->notify_visible_handler);
4561 if (gtk_widget_get_visible (page->child) &&
4562 gtk_widget_get_visible (GTK_WIDGET (notebook)))
4565 gtk_widget_unparent (page->child);
4567 tab_label = page->tab_label;
4570 g_object_ref (tab_label);
4571 gtk_notebook_remove_tab_label (notebook, page);
4573 gtk_widget_destroy (tab_label);
4574 g_object_unref (tab_label);
4579 GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
4581 gtk_notebook_menu_label_unparent (parent, NULL);
4582 gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
4584 gtk_widget_queue_resize (priv->menu);
4586 if (!page->default_menu)
4587 g_object_unref (page->menu_label);
4591 if (page->last_focus_child)
4593 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4594 page->last_focus_child = NULL;
4597 g_slice_free (GtkNotebookPage, page);
4599 gtk_notebook_update_labels (notebook);
4601 gtk_widget_queue_resize (GTK_WIDGET (notebook));
4605 gtk_notebook_update_labels (GtkNotebook *notebook)
4607 GtkNotebookPrivate *priv = notebook->priv;
4608 GtkNotebookPage *page;
4613 if (!priv->show_tabs && !priv->menu)
4616 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
4618 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
4621 g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
4622 if (priv->show_tabs)
4624 if (page->default_tab)
4626 if (!page->tab_label)
4628 page->tab_label = gtk_label_new (string);
4629 gtk_widget_set_parent (page->tab_label,
4630 GTK_WIDGET (notebook));
4633 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
4636 if (gtk_widget_get_visible (page->child) &&
4637 !gtk_widget_get_visible (page->tab_label))
4638 gtk_widget_show (page->tab_label);
4639 else if (!gtk_widget_get_visible (page->child) &&
4640 gtk_widget_get_visible (page->tab_label))
4641 gtk_widget_hide (page->tab_label);
4643 if (priv->menu && page->default_menu)
4645 if (GTK_IS_LABEL (page->tab_label))
4646 gtk_label_set_text (GTK_LABEL (page->menu_label),
4647 gtk_label_get_label (GTK_LABEL (page->tab_label)));
4649 gtk_label_set_text (GTK_LABEL (page->menu_label), string);
4655 gtk_notebook_real_page_position (GtkNotebook *notebook,
4658 GtkNotebookPrivate *priv = notebook->priv;
4662 for (work = priv->children, count_start = 0;
4663 work && work != list; work = work->next)
4664 if (GTK_NOTEBOOK_PAGE (work)->pack == GTK_PACK_START)
4670 if (GTK_NOTEBOOK_PAGE (list)->pack == GTK_PACK_START)
4673 return (count_start + g_list_length (list) - 1);
4677 gtk_notebook_search_page (GtkNotebook *notebook,
4680 gboolean find_visible)
4682 GtkNotebookPrivate *priv = notebook->priv;
4683 GtkNotebookPage *page = NULL;
4684 GList *old_list = NULL;
4690 flag = GTK_PACK_END;
4694 flag = GTK_PACK_START;
4701 if (!page || page->pack == flag)
4709 list = priv->children;
4714 if (page->pack == flag &&
4716 (gtk_widget_get_visible (page->child) &&
4717 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4732 if (page->pack != flag &&
4734 (gtk_widget_get_visible (page->child) &&
4735 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4743 /* Private GtkNotebook Drawing Functions:
4745 * gtk_notebook_paint
4746 * gtk_notebook_draw_tab
4747 * gtk_notebook_draw_arrow
4750 gtk_notebook_paint (GtkWidget *widget,
4753 GtkNotebook *notebook;
4754 GtkNotebookPrivate *priv;
4755 GtkNotebookPage *page;
4756 GtkAllocation allocation;
4761 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
4762 gint gap_x = 0, gap_width = 0, step = STEP_PREV;
4766 notebook = GTK_NOTEBOOK (widget);
4767 priv = notebook->priv;
4768 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4769 tab_pos = get_effective_tab_pos (notebook);
4771 if ((!priv->show_tabs && !priv->show_border) ||
4772 !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
4775 gtk_widget_get_allocation (widget, &allocation);
4777 x = allocation.x + border_width;
4778 y = allocation.y + border_width;
4779 width = allocation.width - border_width * 2;
4780 height = allocation.height - border_width * 2;
4782 if (priv->show_border && (!priv->show_tabs || !priv->children))
4784 gtk_paint_box (gtk_widget_get_style (widget), cr,
4785 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4787 x, y, width, height);
4791 if (!priv->first_tab)
4792 priv->first_tab = priv->children;
4794 if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
4795 page = GTK_NOTEBOOK_PAGE (priv->first_tab);
4797 page = priv->cur_page;
4802 y += page->allocation.height;
4804 case GTK_POS_BOTTOM:
4805 height -= page->allocation.height;
4808 x += page->allocation.width;
4811 width -= page->allocation.width;
4815 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
4816 !gtk_widget_get_mapped (priv->cur_page->tab_label))
4826 case GTK_POS_BOTTOM:
4827 if (priv->operation == DRAG_OPERATION_REORDER)
4828 gap_x = priv->drag_window_x - allocation.x - border_width;
4830 gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
4832 gap_width = priv->cur_page->allocation.width;
4833 step = is_rtl ? STEP_NEXT : STEP_PREV;
4837 if (priv->operation == DRAG_OPERATION_REORDER)
4838 gap_x = priv->drag_window_y - border_width - allocation.y;
4840 gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
4842 gap_width = priv->cur_page->allocation.height;
4847 gtk_paint_box_gap (gtk_widget_get_style (widget), cr,
4848 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4850 x, y, width, height,
4851 tab_pos, gap_x, gap_width);
4854 children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
4857 page = children->data;
4858 children = gtk_notebook_search_page (notebook, children,
4860 if (!gtk_widget_get_visible (page->child))
4862 if (!gtk_widget_get_mapped (page->tab_label))
4864 else if (page != priv->cur_page)
4865 gtk_notebook_draw_tab (notebook, page, cr);
4868 if (showarrow && priv->scrollable)
4870 if (priv->has_before_previous)
4871 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
4872 if (priv->has_before_next)
4873 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
4874 if (priv->has_after_previous)
4875 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
4876 if (priv->has_after_next)
4877 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
4880 if (priv->operation != DRAG_OPERATION_REORDER)
4881 gtk_notebook_draw_tab (notebook, priv->cur_page, cr);
4885 gtk_notebook_draw_tab (GtkNotebook *notebook,
4886 GtkNotebookPage *page,
4889 GtkNotebookPrivate *priv;
4890 GtkStateType state_type;
4893 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
4894 !gtk_widget_get_mapped (page->tab_label) ||
4895 (page->allocation.width == 0) || (page->allocation.height == 0))
4898 widget = GTK_WIDGET (notebook);
4899 priv = notebook->priv;
4901 if (priv->cur_page == page)
4902 state_type = GTK_STATE_NORMAL;
4904 state_type = GTK_STATE_ACTIVE;
4906 gtk_paint_extension (gtk_widget_get_style (widget), cr,
4907 state_type, GTK_SHADOW_OUT,
4911 page->allocation.width,
4912 page->allocation.height,
4913 get_tab_gap_pos (notebook));
4915 if (gtk_widget_has_focus (widget) &&
4916 priv->cur_page == page)
4919 GtkAllocation allocation;
4921 gtk_widget_get_allocation (page->tab_label, &allocation);
4922 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
4924 gtk_paint_focus (gtk_widget_get_style (widget), cr,
4925 gtk_widget_get_state (widget), widget, "tab",
4926 allocation.x - focus_width,
4927 allocation.y - focus_width,
4928 allocation.width + 2 * focus_width,
4929 allocation.height + 2 * focus_width);
4934 gtk_notebook_draw_arrow (GtkNotebook *notebook,
4936 GtkNotebookArrow nbarrow)
4938 GtkNotebookPrivate *priv = notebook->priv;
4939 GtkStateType state_type;
4940 GtkShadowType shadow_type;
4942 GdkRectangle arrow_rect;
4944 gboolean is_rtl, left;
4945 gint scroll_arrow_hlength;
4946 gint scroll_arrow_vlength;
4949 widget = GTK_WIDGET (notebook);
4951 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
4953 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4954 left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
4955 (!ARROW_IS_LEFT (nbarrow) && is_rtl);
4957 gtk_widget_style_get (widget,
4958 "scroll-arrow-hlength", &scroll_arrow_hlength,
4959 "scroll-arrow-vlength", &scroll_arrow_vlength,
4962 if (priv->in_child == nbarrow)
4964 if (priv->click_child == nbarrow)
4965 state_type = GTK_STATE_ACTIVE;
4967 state_type = GTK_STATE_PRELIGHT;
4970 state_type = gtk_widget_get_state (widget);
4972 if (priv->click_child == nbarrow)
4973 shadow_type = GTK_SHADOW_IN;
4975 shadow_type = GTK_SHADOW_OUT;
4977 if (priv->focus_tab &&
4978 !gtk_notebook_search_page (notebook, priv->focus_tab,
4979 left ? STEP_PREV : STEP_NEXT, TRUE))
4981 shadow_type = GTK_SHADOW_ETCHED_IN;
4982 state_type = GTK_STATE_INSENSITIVE;
4985 if (priv->tab_pos == GTK_POS_LEFT ||
4986 priv->tab_pos == GTK_POS_RIGHT)
4988 arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_UP : GTK_ARROW_DOWN);
4989 arrow_size = scroll_arrow_vlength;
4993 arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT);
4994 arrow_size = scroll_arrow_hlength;
4997 gtk_paint_arrow (gtk_widget_get_style (widget),
4999 shadow_type, widget, "notebook",
5000 arrow, TRUE, arrow_rect.x, arrow_rect.y,
5001 arrow_size, arrow_size);
5004 /* Private GtkNotebook Size Allocate Functions:
5006 * gtk_notebook_tab_space
5007 * gtk_notebook_calculate_shown_tabs
5008 * gtk_notebook_calculate_tabs_allocation
5009 * gtk_notebook_pages_allocate
5010 * gtk_notebook_page_allocate
5011 * gtk_notebook_calc_tabs
5014 gtk_notebook_tab_space (GtkNotebook *notebook,
5015 gboolean *show_arrows,
5020 GtkNotebookPrivate *priv = notebook->priv;
5021 GtkAllocation allocation, action_allocation;
5025 gint tab_pos = get_effective_tab_pos (notebook);
5028 gint scroll_arrow_hlength;
5029 gint scroll_arrow_vlength;
5034 widget = GTK_WIDGET (notebook);
5035 children = priv->children;
5036 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5038 style = gtk_widget_get_style (widget);
5040 gtk_widget_style_get (GTK_WIDGET (notebook),
5041 "arrow-spacing", &arrow_spacing,
5042 "scroll-arrow-hlength", &scroll_arrow_hlength,
5043 "scroll-arrow-vlength", &scroll_arrow_vlength,
5046 border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5048 gtk_widget_get_allocation (widget, &allocation);
5053 case GTK_POS_BOTTOM:
5054 *min = allocation.x + border_width;
5055 *max = allocation.x + allocation.width - border_width;
5057 for (i = 0; i < N_ACTION_WIDGETS; i++)
5059 if (priv->action_widget[i])
5061 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5063 if ((i == ACTION_WIDGET_START && !is_rtl) ||
5064 (i == ACTION_WIDGET_END && is_rtl))
5065 *min += action_allocation.width + style->xthickness;
5067 *max -= action_allocation.width + style->xthickness;
5073 GtkNotebookPage *page;
5075 page = children->data;
5076 children = children->next;
5078 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5079 gtk_widget_get_visible (page->child))
5080 *tab_space += page->requisition.width;
5085 *min = allocation.y + border_width;
5086 *max = allocation.y + allocation.height - border_width;
5088 for (i = 0; i < N_ACTION_WIDGETS; i++)
5090 if (priv->action_widget[i])
5092 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5094 if (i == ACTION_WIDGET_START)
5095 *min += action_allocation.height + style->ythickness;
5097 *max -= action_allocation.height + style->ythickness;
5103 GtkNotebookPage *page;
5105 page = children->data;
5106 children = children->next;
5108 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5109 gtk_widget_get_visible (page->child))
5110 *tab_space += page->requisition.height;
5115 if (!priv->scrollable)
5116 *show_arrows = FALSE;
5119 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5124 case GTK_POS_BOTTOM:
5125 if (*tab_space > *max - *min - tab_overlap)
5127 *show_arrows = TRUE;
5129 /* take arrows into account */
5130 *tab_space = *max - *min - tab_overlap;
5132 if (priv->has_after_previous)
5134 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5135 *max -= arrow_spacing + scroll_arrow_hlength;
5138 if (priv->has_after_next)
5140 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5141 *max -= arrow_spacing + scroll_arrow_hlength;
5144 if (priv->has_before_previous)
5146 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5147 *min += arrow_spacing + scroll_arrow_hlength;
5150 if (priv->has_before_next)
5152 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5153 *min += arrow_spacing + scroll_arrow_hlength;
5159 if (*tab_space > *max - *min - tab_overlap)
5161 *show_arrows = TRUE;
5163 /* take arrows into account */
5164 *tab_space = *max - *min - tab_overlap;
5166 if (priv->has_after_previous || priv->has_after_next)
5168 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5169 *max -= arrow_spacing + scroll_arrow_vlength;
5172 if (priv->has_before_previous || priv->has_before_next)
5174 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5175 *min += arrow_spacing + scroll_arrow_vlength;
5184 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5185 gboolean show_arrows,
5191 gint *remaining_space)
5193 GtkNotebookPrivate *priv = notebook->priv;
5195 GtkContainer *container;
5197 GtkNotebookPage *page;
5198 gint tab_pos, tab_overlap;
5200 widget = GTK_WIDGET (notebook);
5201 container = GTK_CONTAINER (notebook);
5202 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5203 tab_pos = get_effective_tab_pos (notebook);
5205 if (show_arrows) /* first_tab <- focus_tab */
5207 *remaining_space = tab_space;
5209 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5210 gtk_widget_get_visible (priv->cur_page->child))
5212 gtk_notebook_calc_tabs (notebook,
5215 remaining_space, STEP_NEXT);
5218 if (tab_space <= 0 || *remaining_space <= 0)
5221 priv->first_tab = priv->focus_tab;
5222 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5224 page = priv->first_tab->data;
5225 *remaining_space = tab_space - page->requisition.width;
5232 if (priv->first_tab && priv->first_tab != priv->focus_tab)
5234 /* Is first_tab really predecessor of focus_tab? */
5235 page = priv->first_tab->data;
5236 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5237 gtk_widget_get_visible (page->child))
5238 for (children = priv->focus_tab;
5239 children && children != priv->first_tab;
5240 children = gtk_notebook_search_page (notebook,
5248 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5249 priv->first_tab = priv->focus_tab;
5251 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5255 /* calculate shown tabs counting backwards from the focus tab */
5256 gtk_notebook_calc_tabs (notebook,
5257 gtk_notebook_search_page (notebook,
5261 &(priv->first_tab), remaining_space,
5264 if (*remaining_space < 0)
5267 gtk_notebook_search_page (notebook, priv->first_tab,
5269 if (!priv->first_tab)
5270 priv->first_tab = priv->focus_tab;
5272 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5275 else /* focus_tab -> end */
5277 if (!priv->first_tab)
5278 priv->first_tab = gtk_notebook_search_page (notebook,
5283 gtk_notebook_calc_tabs (notebook,
5284 gtk_notebook_search_page (notebook,
5288 &children, remaining_space, STEP_NEXT);
5290 if (*remaining_space <= 0)
5291 *last_child = children;
5292 else /* start <- first_tab */
5297 gtk_notebook_calc_tabs (notebook,
5298 gtk_notebook_search_page (notebook,
5302 &children, remaining_space, STEP_PREV);
5304 if (*remaining_space == 0)
5305 priv->first_tab = children;
5307 priv->first_tab = gtk_notebook_search_page(notebook,
5314 if (*remaining_space < 0)
5316 /* calculate number of tabs */
5317 *remaining_space = - (*remaining_space);
5320 for (children = priv->first_tab;
5321 children && children != *last_child;
5322 children = gtk_notebook_search_page (notebook, children,
5327 *remaining_space = 0;
5330 /* unmap all non-visible tabs */
5331 for (children = gtk_notebook_search_page (notebook, NULL,
5333 children && children != priv->first_tab;
5334 children = gtk_notebook_search_page (notebook, children,
5337 page = children->data;
5339 if (page->tab_label &&
5340 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5341 gtk_widget_set_child_visible (page->tab_label, FALSE);
5344 for (children = *last_child; children;
5345 children = gtk_notebook_search_page (notebook, children,
5348 page = children->data;
5350 if (page->tab_label &&
5351 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5352 gtk_widget_set_child_visible (page->tab_label, FALSE);
5355 else /* !show_arrows */
5360 *remaining_space = max - min - tab_overlap - tab_space;
5361 children = priv->children;
5362 priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5366 page = children->data;
5367 children = children->next;
5369 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5370 !gtk_widget_get_visible (page->child))
5379 /* if notebook is homogeneous, all tabs are expanded */
5380 if (priv->homogeneous && *n)
5386 get_allocate_at_bottom (GtkWidget *widget,
5387 gint search_direction)
5389 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5390 gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5395 case GTK_POS_BOTTOM:
5397 return (search_direction == STEP_PREV);
5399 return (search_direction == STEP_NEXT);
5404 return (search_direction == STEP_PREV);
5412 gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5417 gint *remaining_space,
5418 gint *expanded_tabs,
5422 GtkNotebookPrivate *priv = notebook->priv;
5423 GtkAllocation allocation;
5425 GtkContainer *container;
5426 GtkNotebookPage *page;
5428 gboolean allocate_at_bottom;
5429 gint tab_overlap, tab_pos, tab_extra_space;
5430 gint left_x, right_x, top_y, bottom_y, anchor;
5431 gint xthickness, ythickness;
5433 gboolean gap_left, packing_changed;
5434 GtkAllocation child_allocation = { 0, };
5436 widget = GTK_WIDGET (notebook);
5437 container = GTK_CONTAINER (notebook);
5438 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5439 tab_pos = get_effective_tab_pos (notebook);
5440 allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5443 gtk_widget_get_allocation (widget, &allocation);
5445 border_width = gtk_container_get_border_width (container);
5446 child_allocation.x = allocation.x + border_width;
5447 child_allocation.y = allocation.y + border_width;
5449 style = gtk_widget_get_style (widget);
5450 xthickness = style->xthickness;
5451 ythickness = style->ythickness;
5455 case GTK_POS_BOTTOM:
5456 child_allocation.y = allocation.y + allocation.height -
5457 priv->cur_page->requisition.height - border_width;
5460 child_allocation.x = (allocate_at_bottom) ? max : min;
5461 child_allocation.height = priv->cur_page->requisition.height;
5462 anchor = child_allocation.x;
5466 child_allocation.x = allocation.x + allocation.width -
5467 priv->cur_page->requisition.width - border_width;
5470 child_allocation.y = (allocate_at_bottom) ? max : min;
5471 child_allocation.width = priv->cur_page->requisition.width;
5472 anchor = child_allocation.y;
5476 left_x = CLAMP (priv->mouse_x - priv->drag_offset_x,
5477 min, max - priv->cur_page->allocation.width);
5478 top_y = CLAMP (priv->mouse_y - priv->drag_offset_y,
5479 min, max - priv->cur_page->allocation.height);
5480 right_x = left_x + priv->cur_page->allocation.width;
5481 bottom_y = top_y + priv->cur_page->allocation.height;
5482 gap_left = packing_changed = FALSE;
5484 while (*children && *children != last_child)
5486 page = (*children)->data;
5488 if (direction == STEP_NEXT && page->pack != GTK_PACK_START)
5492 else if (priv->operation == DRAG_OPERATION_REORDER)
5493 packing_changed = TRUE;
5496 if (direction == STEP_NEXT)
5497 *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5500 *children = (*children)->next;
5502 if (page->pack != GTK_PACK_END || !gtk_widget_get_visible (page->child))
5506 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5509 tab_extra_space = 0;
5510 if (*expanded_tabs && (showarrow || page->expand || priv->homogeneous))
5512 tab_extra_space = *remaining_space / *expanded_tabs;
5513 *remaining_space -= tab_extra_space;
5520 case GTK_POS_BOTTOM:
5521 child_allocation.width = page->requisition.width + tab_overlap + tab_extra_space;
5523 /* make sure that the reordered tab doesn't go past the last position */
5524 if (priv->operation == DRAG_OPERATION_REORDER &&
5525 !gap_left && packing_changed)
5527 if (!allocate_at_bottom)
5529 if ((priv->cur_page->pack == GTK_PACK_START && left_x >= anchor) ||
5530 (priv->cur_page->pack == GTK_PACK_END && left_x < anchor))
5532 left_x = priv->drag_window_x = anchor;
5533 anchor += priv->cur_page->allocation.width - tab_overlap;
5538 if ((priv->cur_page->pack == GTK_PACK_START && right_x <= anchor) ||
5539 (priv->cur_page->pack == GTK_PACK_END && right_x > anchor))
5541 anchor -= priv->cur_page->allocation.width;
5542 left_x = priv->drag_window_x = anchor;
5543 anchor += tab_overlap;
5550 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5552 priv->drag_window_x = left_x;
5553 priv->drag_window_y = child_allocation.y;
5557 if (allocate_at_bottom)
5558 anchor -= child_allocation.width;
5560 if (priv->operation == DRAG_OPERATION_REORDER && page->pack == priv->cur_page->pack)
5562 if (!allocate_at_bottom &&
5564 left_x <= anchor + child_allocation.width / 2)
5565 anchor += priv->cur_page->allocation.width - tab_overlap;
5566 else if (allocate_at_bottom &&
5567 right_x >= anchor + child_allocation.width / 2 &&
5568 right_x <= anchor + child_allocation.width)
5569 anchor -= priv->cur_page->allocation.width - tab_overlap;
5572 child_allocation.x = anchor;
5578 child_allocation.height = page->requisition.height + tab_overlap + tab_extra_space;
5580 /* make sure that the reordered tab doesn't go past the last position */
5581 if (priv->operation == DRAG_OPERATION_REORDER &&
5582 !gap_left && packing_changed)
5584 if (!allocate_at_bottom &&
5585 ((priv->cur_page->pack == GTK_PACK_START && top_y >= anchor) ||
5586 (priv->cur_page->pack == GTK_PACK_END && top_y < anchor)))
5588 top_y = priv->drag_window_y = anchor;
5589 anchor += priv->cur_page->allocation.height - tab_overlap;
5595 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5597 priv->drag_window_x = child_allocation.x;
5598 priv->drag_window_y = top_y;
5602 if (allocate_at_bottom)
5603 anchor -= child_allocation.height;
5605 if (priv->operation == DRAG_OPERATION_REORDER && page->pack == priv->cur_page->pack)
5607 if (!allocate_at_bottom &&
5609 top_y <= anchor + child_allocation.height / 2)
5610 anchor += priv->cur_page->allocation.height - tab_overlap;
5611 else if (allocate_at_bottom &&
5612 bottom_y >= anchor + child_allocation.height / 2 &&
5613 bottom_y <= anchor + child_allocation.height)
5614 anchor -= priv->cur_page->allocation.height - tab_overlap;
5617 child_allocation.y = anchor;
5623 page->allocation = child_allocation;
5625 if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
5626 (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
5628 /* needs to be allocated at 0,0
5629 * to be shown in the drag window */
5630 page->allocation.x = 0;
5631 page->allocation.y = 0;
5634 if (page != priv->cur_page)
5639 page->allocation.y += ythickness;
5641 case GTK_POS_BOTTOM:
5642 page->allocation.height = MAX (1, page->allocation.height - ythickness);
5645 page->allocation.x += xthickness;
5648 page->allocation.width = MAX (1, page->allocation.width - xthickness);
5653 /* calculate whether to leave a gap based on reorder operation or not */
5657 case GTK_POS_BOTTOM:
5658 if (priv->operation != DRAG_OPERATION_REORDER ||
5659 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
5661 if (priv->operation == DRAG_OPERATION_REORDER)
5663 if (page->pack == priv->cur_page->pack &&
5664 !allocate_at_bottom &&
5665 left_x > anchor + child_allocation.width / 2 &&
5666 left_x <= anchor + child_allocation.width)
5667 anchor += priv->cur_page->allocation.width - tab_overlap;
5668 else if (page->pack == priv->cur_page->pack &&
5669 allocate_at_bottom &&
5670 right_x >= anchor &&
5671 right_x <= anchor + child_allocation.width / 2)
5672 anchor -= priv->cur_page->allocation.width - tab_overlap;
5675 if (!allocate_at_bottom)
5676 anchor += child_allocation.width - tab_overlap;
5678 anchor += tab_overlap;
5684 if (priv->operation != DRAG_OPERATION_REORDER ||
5685 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
5687 if (priv->operation == DRAG_OPERATION_REORDER)
5689 if (page->pack == priv->cur_page->pack &&
5690 !allocate_at_bottom &&
5691 top_y >= anchor + child_allocation.height / 2 &&
5692 top_y <= anchor + child_allocation.height)
5693 anchor += priv->cur_page->allocation.height - tab_overlap;
5694 else if (page->pack == priv->cur_page->pack &&
5695 allocate_at_bottom &&
5696 bottom_y >= anchor &&
5697 bottom_y <= anchor + child_allocation.height / 2)
5698 anchor -= priv->cur_page->allocation.height - tab_overlap;
5701 if (!allocate_at_bottom)
5702 anchor += child_allocation.height - tab_overlap;
5704 anchor += tab_overlap;
5710 /* set child visible */
5711 if (page->tab_label)
5712 gtk_widget_set_child_visible (page->tab_label, TRUE);
5715 /* Don't move the current tab past the last position during tabs reordering */
5717 priv->operation == DRAG_OPERATION_REORDER &&
5718 ((direction == STEP_NEXT && priv->cur_page->pack == GTK_PACK_START) ||
5719 ((direction == STEP_PREV || packing_changed) && priv->cur_page->pack == GTK_PACK_END)))
5724 case GTK_POS_BOTTOM:
5725 if (allocate_at_bottom)
5726 anchor -= priv->cur_page->allocation.width;
5728 if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
5729 (allocate_at_bottom && priv->drag_window_x < anchor))
5730 priv->drag_window_x = anchor;
5734 if (allocate_at_bottom)
5735 anchor -= priv->cur_page->allocation.height;
5737 if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
5738 (allocate_at_bottom && priv->drag_window_y < anchor))
5739 priv->drag_window_y = anchor;
5746 gtk_notebook_pages_allocate (GtkNotebook *notebook)
5748 GtkNotebookPrivate *priv = notebook->priv;
5749 GList *children = NULL;
5750 GList *last_child = NULL;
5751 gboolean showarrow = FALSE;
5752 gint tab_space, min, max, remaining_space;
5754 gboolean tab_allocations_changed = FALSE;
5756 if (!priv->show_tabs || !priv->children || !priv->cur_page)
5759 min = max = tab_space = remaining_space = 0;
5762 gtk_notebook_tab_space (notebook, &showarrow,
5763 &min, &max, &tab_space);
5765 gtk_notebook_calculate_shown_tabs (notebook, showarrow,
5766 min, max, tab_space, &last_child,
5767 &expanded_tabs, &remaining_space);
5769 children = priv->first_tab;
5770 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5771 showarrow, STEP_NEXT,
5772 &remaining_space, &expanded_tabs, min, max);
5773 if (children && children != last_child)
5775 children = priv->children;
5776 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5777 showarrow, STEP_PREV,
5778 &remaining_space, &expanded_tabs, min, max);
5781 children = priv->children;
5785 if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
5786 tab_allocations_changed = TRUE;
5787 children = children->next;
5790 if (!priv->first_tab)
5791 priv->first_tab = priv->children;
5793 if (tab_allocations_changed)
5794 gtk_notebook_redraw_tabs (notebook);
5798 gtk_notebook_page_allocate (GtkNotebook *notebook,
5799 GtkNotebookPage *page)
5801 GtkWidget *widget = GTK_WIDGET (notebook);
5802 GtkNotebookPrivate *priv = notebook->priv;
5803 GtkAllocation child_allocation, label_allocation;
5804 GtkRequisition tab_requisition;
5811 gint tab_pos = get_effective_tab_pos (notebook);
5812 gboolean tab_allocation_changed;
5813 gboolean was_visible = page->tab_allocated_visible;
5815 if (!page->tab_label ||
5816 !gtk_widget_get_visible (page->tab_label) ||
5817 !gtk_widget_get_child_visible (page->tab_label))
5819 page->tab_allocated_visible = FALSE;
5823 style = gtk_widget_get_style (widget);
5824 xthickness = style->xthickness;
5825 ythickness = style->ythickness;
5827 gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
5828 gtk_widget_style_get (widget,
5829 "focus-line-width", &focus_width,
5830 "tab-curvature", &tab_curvature,
5835 case GTK_POS_BOTTOM:
5836 padding = tab_curvature + focus_width + priv->tab_hborder;
5839 child_allocation.x = xthickness + focus_width + priv->tab_hborder;
5840 child_allocation.width = MAX (1, page->allocation.width - 2 * child_allocation.x);
5841 child_allocation.x += page->allocation.x;
5845 child_allocation.x = page->allocation.x +
5846 (page->allocation.width - tab_requisition.width) / 2;
5848 child_allocation.width = tab_requisition.width;
5851 child_allocation.y = priv->tab_vborder + focus_width + page->allocation.y;
5853 if (tab_pos == GTK_POS_TOP)
5854 child_allocation.y += ythickness;
5856 child_allocation.height = MAX (1, (page->allocation.height - ythickness -
5857 2 * (priv->tab_vborder + focus_width)));
5861 padding = tab_curvature + focus_width + priv->tab_vborder;
5864 child_allocation.y = ythickness + padding;
5865 child_allocation.height = MAX (1, (page->allocation.height -
5866 2 * child_allocation.y));
5867 child_allocation.y += page->allocation.y;
5871 child_allocation.y = page->allocation.y +
5872 (page->allocation.height - tab_requisition.height) / 2;
5874 child_allocation.height = tab_requisition.height;
5877 child_allocation.x = priv->tab_hborder + focus_width + page->allocation.x;
5879 if (tab_pos == GTK_POS_LEFT)
5880 child_allocation.x += xthickness;
5882 child_allocation.width = MAX (1, (page->allocation.width - xthickness -
5883 2 * (priv->tab_hborder + focus_width)));
5887 gtk_widget_get_allocation (page->tab_label, &label_allocation);
5888 tab_allocation_changed = (child_allocation.x != label_allocation.x ||
5889 child_allocation.y != label_allocation.y ||
5890 child_allocation.width != label_allocation.width ||
5891 child_allocation.height != label_allocation.height);
5893 gtk_widget_size_allocate (page->tab_label, &child_allocation);
5897 page->tab_allocated_visible = TRUE;
5898 tab_allocation_changed = TRUE;
5901 return tab_allocation_changed;
5905 gtk_notebook_calc_tabs (GtkNotebook *notebook,
5911 GtkNotebookPage *page = NULL;
5913 GList *last_list = NULL;
5914 GList *last_calculated_child = NULL;
5916 gint tab_pos = get_effective_tab_pos (notebook);
5917 guint real_direction;
5923 pack = GTK_NOTEBOOK_PAGE (start)->pack;
5924 if (pack == GTK_PACK_END)
5925 real_direction = (direction == STEP_PREV) ? STEP_NEXT : STEP_PREV;
5927 real_direction = direction;
5934 case GTK_POS_BOTTOM:
5937 page = children->data;
5938 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5939 gtk_widget_get_visible (page->child))
5941 if (page->pack == pack)
5943 *tab_space -= page->requisition.width;
5944 if (*tab_space < 0 || children == *end)
5948 *tab_space = - (*tab_space +
5949 page->requisition.width);
5951 if (*tab_space == 0 && direction == STEP_PREV)
5952 children = last_calculated_child;
5959 last_calculated_child = children;
5961 last_list = children;
5963 if (real_direction == STEP_NEXT)
5964 children = children->next;
5966 children = children->prev;
5973 page = children->data;
5974 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5975 gtk_widget_get_visible (page->child))
5977 if (page->pack == pack)
5979 *tab_space -= page->requisition.height;
5980 if (*tab_space < 0 || children == *end)
5984 *tab_space = - (*tab_space +
5985 page->requisition.height);
5987 if (*tab_space == 0 && direction == STEP_PREV)
5988 children = last_calculated_child;
5995 last_calculated_child = children;
5997 last_list = children;
5999 if (real_direction == STEP_NEXT)
6000 children = children->next;
6002 children = children->prev;
6006 if (real_direction == STEP_PREV)
6008 pack = (pack == GTK_PACK_END) ? GTK_PACK_START : GTK_PACK_END;
6009 real_direction = STEP_PREV;
6010 children = last_list;
6015 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6017 GtkNotebookPrivate *priv = notebook->priv;
6020 for (list = priv->children; list != NULL; list = list->next)
6022 GtkNotebookPage *page = list->data;
6024 if (page->tab_label)
6026 if (page == priv->cur_page)
6027 gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
6029 gtk_widget_set_state (page->tab_label, GTK_STATE_ACTIVE);
6034 /* Private GtkNotebook Page Switch Methods:
6036 * gtk_notebook_real_switch_page
6039 gtk_notebook_real_switch_page (GtkNotebook *notebook,
6043 GtkNotebookPrivate *priv = notebook->priv;
6044 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6045 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6046 gboolean child_has_focus;
6048 if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6051 /* save the value here, changing visibility changes focus */
6052 child_has_focus = priv->child_has_focus;
6055 gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6057 priv->cur_page = page;
6059 if (!priv->focus_tab ||
6060 priv->focus_tab->data != (gpointer) priv->cur_page)
6062 g_list_find (priv->children, priv->cur_page);
6064 gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6066 /* If the focus was on the previous page, move it to the first
6067 * element on the new page, if possible, or if not, to the
6070 if (child_has_focus)
6072 if (priv->cur_page->last_focus_child &&
6073 gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6074 gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6076 if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6077 gtk_widget_grab_focus (GTK_WIDGET (notebook));
6080 gtk_notebook_update_tab_states (notebook);
6081 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6082 g_object_notify (G_OBJECT (notebook), "page");
6085 /* Private GtkNotebook Page Switch Functions:
6087 * gtk_notebook_switch_page
6088 * gtk_notebook_page_select
6089 * gtk_notebook_switch_focus_tab
6090 * gtk_notebook_menu_switch_page
6093 gtk_notebook_switch_page (GtkNotebook *notebook,
6094 GtkNotebookPage *page)
6096 GtkNotebookPrivate *priv = notebook->priv;
6099 if (priv->cur_page == page)
6102 page_num = g_list_index (priv->children, page);
6104 g_signal_emit (notebook,
6105 notebook_signals[SWITCH_PAGE],
6112 gtk_notebook_page_select (GtkNotebook *notebook,
6113 gboolean move_focus)
6115 GtkNotebookPrivate *priv = notebook->priv;
6116 GtkNotebookPage *page;
6117 GtkDirectionType dir = GTK_DIR_DOWN; /* Quiet GCC */
6118 gint tab_pos = get_effective_tab_pos (notebook);
6120 if (!priv->focus_tab)
6123 page = priv->focus_tab->data;
6124 gtk_notebook_switch_page (notebook, page);
6133 case GTK_POS_BOTTOM:
6137 dir = GTK_DIR_RIGHT;
6144 if (gtk_widget_child_focus (page->child, dir))
6151 gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
6154 GtkNotebookPrivate *priv = notebook->priv;
6156 GtkNotebookPage *page;
6158 if (priv->focus_tab == new_child)
6161 old_child = priv->focus_tab;
6162 priv->focus_tab = new_child;
6164 if (priv->scrollable)
6165 gtk_notebook_redraw_arrows (notebook);
6167 if (!priv->show_tabs || !priv->focus_tab)
6170 page = priv->focus_tab->data;
6171 if (gtk_widget_get_mapped (page->tab_label))
6172 gtk_notebook_redraw_tabs (notebook);
6174 gtk_notebook_pages_allocate (notebook);
6176 gtk_notebook_switch_page (notebook, page);
6180 gtk_notebook_menu_switch_page (GtkWidget *widget,
6181 GtkNotebookPage *page)
6183 GtkNotebookPrivate *priv;
6184 GtkNotebook *notebook;
6189 parent = gtk_widget_get_parent (widget);
6190 notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6191 priv = notebook->priv;
6193 if (priv->cur_page == page)
6197 children = priv->children;
6198 while (children && children->data != page)
6200 children = children->next;
6204 g_signal_emit (notebook,
6205 notebook_signals[SWITCH_PAGE],
6211 /* Private GtkNotebook Menu Functions:
6213 * gtk_notebook_menu_item_create
6214 * gtk_notebook_menu_label_unparent
6215 * gtk_notebook_menu_detacher
6218 gtk_notebook_menu_item_create (GtkNotebook *notebook,
6221 GtkNotebookPrivate *priv = notebook->priv;
6222 GtkNotebookPage *page;
6223 GtkWidget *menu_item;
6226 if (page->default_menu)
6228 if (GTK_IS_LABEL (page->tab_label))
6229 page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6231 page->menu_label = gtk_label_new ("");
6232 gtk_misc_set_alignment (GTK_MISC (page->menu_label), 0.0, 0.5);
6235 gtk_widget_show (page->menu_label);
6236 menu_item = gtk_menu_item_new ();
6237 gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6238 gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6239 gtk_notebook_real_page_position (notebook, list));
6240 g_signal_connect (menu_item, "activate",
6241 G_CALLBACK (gtk_notebook_menu_switch_page), page);
6242 if (gtk_widget_get_visible (page->child))
6243 gtk_widget_show (menu_item);
6247 gtk_notebook_menu_label_unparent (GtkWidget *widget,
6250 gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6251 _gtk_bin_set_child (GTK_BIN (widget), NULL);
6255 gtk_notebook_menu_detacher (GtkWidget *widget,
6258 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6259 GtkNotebookPrivate *priv = notebook->priv;
6261 g_return_if_fail (priv->menu == (GtkWidget*) menu);
6266 /* Public GtkNotebook Page Insert/Remove Methods :
6268 * gtk_notebook_append_page
6269 * gtk_notebook_append_page_menu
6270 * gtk_notebook_prepend_page
6271 * gtk_notebook_prepend_page_menu
6272 * gtk_notebook_insert_page
6273 * gtk_notebook_insert_page_menu
6274 * gtk_notebook_remove_page
6277 * gtk_notebook_append_page:
6278 * @notebook: a #GtkNotebook
6279 * @child: the #GtkWidget to use as the contents of the page.
6280 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6281 * or %NULL to use the default label, 'page N'.
6283 * Appends a page to @notebook.
6285 * Return value: the index (starting from 0) of the appended
6286 * page in the notebook, or -1 if function fails
6289 gtk_notebook_append_page (GtkNotebook *notebook,
6291 GtkWidget *tab_label)
6293 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6294 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6295 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6297 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6301 * gtk_notebook_append_page_menu:
6302 * @notebook: a #GtkNotebook
6303 * @child: the #GtkWidget to use as the contents of the page.
6304 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6305 * or %NULL to use the default label, 'page N'.
6306 * @menu_label: (allow-none): the widget to use as a label for the page-switch
6307 * menu, if that is enabled. If %NULL, and @tab_label
6308 * is a #GtkLabel or %NULL, then the menu label will be
6309 * a newly created label with the same text as @tab_label;
6310 * If @tab_label is not a #GtkLabel, @menu_label must be
6311 * specified if the page-switch menu is to be used.
6313 * Appends a page to @notebook, specifying the widget to use as the
6314 * label in the popup menu.
6316 * Return value: the index (starting from 0) of the appended
6317 * page in the notebook, or -1 if function fails
6320 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6322 GtkWidget *tab_label,
6323 GtkWidget *menu_label)
6325 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6326 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6327 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6328 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6330 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6334 * gtk_notebook_prepend_page:
6335 * @notebook: a #GtkNotebook
6336 * @child: the #GtkWidget to use as the contents of the page.
6337 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6338 * or %NULL to use the default label, 'page N'.
6340 * Prepends a page to @notebook.
6342 * Return value: the index (starting from 0) of the prepended
6343 * page in the notebook, or -1 if function fails
6346 gtk_notebook_prepend_page (GtkNotebook *notebook,
6348 GtkWidget *tab_label)
6350 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6351 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6352 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6354 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6358 * gtk_notebook_prepend_page_menu:
6359 * @notebook: a #GtkNotebook
6360 * @child: the #GtkWidget to use as the contents of the page.
6361 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6362 * or %NULL to use the default label, 'page N'.
6363 * @menu_label: (allow-none): the widget to use as a label for the page-switch
6364 * menu, if that is enabled. If %NULL, and @tab_label
6365 * is a #GtkLabel or %NULL, then the menu label will be
6366 * a newly created label with the same text as @tab_label;
6367 * If @tab_label is not a #GtkLabel, @menu_label must be
6368 * specified if the page-switch menu is to be used.
6370 * Prepends a page to @notebook, specifying the widget to use as the
6371 * label in the popup menu.
6373 * Return value: the index (starting from 0) of the prepended
6374 * page in the notebook, or -1 if function fails
6377 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6379 GtkWidget *tab_label,
6380 GtkWidget *menu_label)
6382 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6383 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6384 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6385 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6387 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6391 * gtk_notebook_insert_page:
6392 * @notebook: a #GtkNotebook
6393 * @child: the #GtkWidget to use as the contents of the page.
6394 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6395 * or %NULL to use the default label, 'page N'.
6396 * @position: the index (starting at 0) at which to insert the page,
6397 * or -1 to append the page after all other pages.
6399 * Insert a page into @notebook at the given position.
6401 * Return value: the index (starting from 0) of the inserted
6402 * page in the notebook, or -1 if function fails
6405 gtk_notebook_insert_page (GtkNotebook *notebook,
6407 GtkWidget *tab_label,
6410 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6411 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6412 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6414 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6419 gtk_notebook_page_compare_tab (gconstpointer a,
6422 return (((GtkNotebookPage *) a)->tab_label != b);
6426 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6430 GtkNotebook *notebook = GTK_NOTEBOOK (data);
6431 GtkNotebookPrivate *priv = notebook->priv;
6434 list = g_list_find_custom (priv->children, child,
6435 gtk_notebook_page_compare_tab);
6438 GtkNotebookPage *page = list->data;
6440 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
6441 gtk_notebook_switch_page (notebook, page);
6442 focus_tabs_in (notebook);
6449 * gtk_notebook_insert_page_menu:
6450 * @notebook: a #GtkNotebook
6451 * @child: the #GtkWidget to use as the contents of the page.
6452 * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6453 * or %NULL to use the default label, 'page N'.
6454 * @menu_label: (allow-none): the widget to use as a label for the page-switch
6455 * menu, if that is enabled. If %NULL, and @tab_label
6456 * is a #GtkLabel or %NULL, then the menu label will be
6457 * a newly created label with the same text as @tab_label;
6458 * If @tab_label is not a #GtkLabel, @menu_label must be
6459 * specified if the page-switch menu is to be used.
6460 * @position: the index (starting at 0) at which to insert the page,
6461 * or -1 to append the page after all other pages.
6463 * Insert a page into @notebook at the given position, specifying
6464 * the widget to use as the label in the popup menu.
6466 * Return value: the index (starting from 0) of the inserted
6467 * page in the notebook
6470 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
6472 GtkWidget *tab_label,
6473 GtkWidget *menu_label,
6476 GtkNotebookClass *class;
6478 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6479 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6480 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6481 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6483 class = GTK_NOTEBOOK_GET_CLASS (notebook);
6485 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
6489 * gtk_notebook_remove_page:
6490 * @notebook: a #GtkNotebook.
6491 * @page_num: the index of a notebook page, starting
6492 * from 0. If -1, the last page will
6495 * Removes a page from the notebook given its index
6499 gtk_notebook_remove_page (GtkNotebook *notebook,
6502 GtkNotebookPrivate *priv;
6505 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6507 priv = notebook->priv;
6510 list = g_list_nth (priv->children, page_num);
6512 list = g_list_last (priv->children);
6515 gtk_container_remove (GTK_CONTAINER (notebook),
6516 ((GtkNotebookPage *) list->data)->child);
6519 /* Public GtkNotebook Page Switch Methods :
6520 * gtk_notebook_get_current_page
6521 * gtk_notebook_page_num
6522 * gtk_notebook_set_current_page
6523 * gtk_notebook_next_page
6524 * gtk_notebook_prev_page
6527 * gtk_notebook_get_current_page:
6528 * @notebook: a #GtkNotebook
6530 * Returns the page number of the current page.
6532 * Return value: the index (starting from 0) of the current
6533 * page in the notebook. If the notebook has no pages, then
6534 * -1 will be returned.
6537 gtk_notebook_get_current_page (GtkNotebook *notebook)
6539 GtkNotebookPrivate *priv;
6541 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6543 priv = notebook->priv;
6545 if (!priv->cur_page)
6548 return g_list_index (priv->children, priv->cur_page);
6552 * gtk_notebook_get_nth_page:
6553 * @notebook: a #GtkNotebook
6554 * @page_num: the index of a page in the notebook, or -1
6555 * to get the last page.
6557 * Returns the child widget contained in page number @page_num.
6559 * Return value: (transfer none): the child widget, or %NULL if @page_num is
6563 gtk_notebook_get_nth_page (GtkNotebook *notebook,
6566 GtkNotebookPrivate *priv;
6567 GtkNotebookPage *page;
6570 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6572 priv = notebook->priv;
6575 list = g_list_nth (priv->children, page_num);
6577 list = g_list_last (priv->children);
6589 * gtk_notebook_get_n_pages:
6590 * @notebook: a #GtkNotebook
6592 * Gets the number of pages in a notebook.
6594 * Return value: the number of pages in the notebook.
6599 gtk_notebook_get_n_pages (GtkNotebook *notebook)
6601 GtkNotebookPrivate *priv;
6603 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
6605 priv = notebook->priv;
6607 return g_list_length (priv->children);
6611 * gtk_notebook_page_num:
6612 * @notebook: a #GtkNotebook
6613 * @child: a #GtkWidget
6615 * Finds the index of the page which contains the given child
6618 * Return value: the index of the page containing @child, or
6619 * -1 if @child is not in the notebook.
6622 gtk_notebook_page_num (GtkNotebook *notebook,
6625 GtkNotebookPrivate *priv;
6629 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6631 priv = notebook->priv;
6634 children = priv->children;
6637 GtkNotebookPage *page = children->data;
6639 if (page->child == child)
6642 children = children->next;
6650 * gtk_notebook_set_current_page:
6651 * @notebook: a #GtkNotebook
6652 * @page_num: index of the page to switch to, starting from 0.
6653 * If negative, the last page will be used. If greater
6654 * than the number of pages in the notebook, nothing
6657 * Switches to the page number @page_num.
6659 * Note that due to historical reasons, GtkNotebook refuses
6660 * to switch to a page unless the child widget is visible.
6661 * Therefore, it is recommended to show child widgets before
6662 * adding them to a notebook.
6665 gtk_notebook_set_current_page (GtkNotebook *notebook,
6668 GtkNotebookPrivate *priv;
6671 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6673 priv = notebook->priv;
6676 page_num = g_list_length (priv->children) - 1;
6678 list = g_list_nth (priv->children, page_num);
6680 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6684 * gtk_notebook_next_page:
6685 * @notebook: a #GtkNotebook
6687 * Switches to the next page. Nothing happens if the current page is
6691 gtk_notebook_next_page (GtkNotebook *notebook)
6693 GtkNotebookPrivate *priv;
6696 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6698 priv = notebook->priv;
6700 list = g_list_find (priv->children, priv->cur_page);
6704 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
6708 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6712 * gtk_notebook_prev_page:
6713 * @notebook: a #GtkNotebook
6715 * Switches to the previous page. Nothing happens if the current page
6716 * is the first page.
6719 gtk_notebook_prev_page (GtkNotebook *notebook)
6721 GtkNotebookPrivate *priv;
6724 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6726 priv = notebook->priv;
6728 list = g_list_find (priv->children, priv->cur_page);
6732 list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
6736 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6739 /* Public GtkNotebook/Tab Style Functions
6741 * gtk_notebook_set_show_border
6742 * gtk_notebook_get_show_border
6743 * gtk_notebook_set_show_tabs
6744 * gtk_notebook_get_show_tabs
6745 * gtk_notebook_set_tab_pos
6746 * gtk_notebook_get_tab_pos
6747 * gtk_notebook_set_scrollable
6748 * gtk_notebook_get_scrollable
6749 * gtk_notebook_get_tab_hborder
6750 * gtk_notebook_get_tab_vborder
6753 * gtk_notebook_set_show_border:
6754 * @notebook: a #GtkNotebook
6755 * @show_border: %TRUE if a bevel should be drawn around the notebook.
6757 * Sets whether a bevel will be drawn around the notebook pages.
6758 * This only has a visual effect when the tabs are not shown.
6759 * See gtk_notebook_set_show_tabs().
6762 gtk_notebook_set_show_border (GtkNotebook *notebook,
6763 gboolean show_border)
6765 GtkNotebookPrivate *priv;
6767 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6769 priv = notebook->priv;
6771 if (priv->show_border != show_border)
6773 priv->show_border = show_border;
6775 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6776 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6778 g_object_notify (G_OBJECT (notebook), "show-border");
6783 * gtk_notebook_get_show_border:
6784 * @notebook: a #GtkNotebook
6786 * Returns whether a bevel will be drawn around the notebook pages. See
6787 * gtk_notebook_set_show_border().
6789 * Return value: %TRUE if the bevel is drawn
6792 gtk_notebook_get_show_border (GtkNotebook *notebook)
6794 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6796 return notebook->priv->show_border;
6800 * gtk_notebook_set_show_tabs:
6801 * @notebook: a #GtkNotebook
6802 * @show_tabs: %TRUE if the tabs should be shown.
6804 * Sets whether to show the tabs for the notebook or not.
6807 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
6810 GtkNotebookPrivate *priv;
6811 GtkNotebookPage *page;
6815 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6817 priv = notebook->priv;
6819 show_tabs = show_tabs != FALSE;
6821 if (priv->show_tabs == show_tabs)
6824 priv->show_tabs = show_tabs;
6825 children = priv->children;
6829 gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
6833 page = children->data;
6834 children = children->next;
6835 if (page->default_tab)
6837 gtk_widget_destroy (page->tab_label);
6838 page->tab_label = NULL;
6841 gtk_widget_hide (page->tab_label);
6846 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
6847 gtk_notebook_update_labels (notebook);
6850 for (i = 0; i < N_ACTION_WIDGETS; i++)
6852 if (priv->action_widget[i])
6853 gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
6856 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6858 g_object_notify (G_OBJECT (notebook), "show-tabs");
6862 * gtk_notebook_get_show_tabs:
6863 * @notebook: a #GtkNotebook
6865 * Returns whether the tabs of the notebook are shown. See
6866 * gtk_notebook_set_show_tabs().
6868 * Return value: %TRUE if the tabs are shown
6871 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
6873 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6875 return notebook->priv->show_tabs;
6879 * gtk_notebook_set_tab_pos:
6880 * @notebook: a #GtkNotebook.
6881 * @pos: the edge to draw the tabs at.
6883 * Sets the edge at which the tabs for switching pages in the
6884 * notebook are drawn.
6887 gtk_notebook_set_tab_pos (GtkNotebook *notebook,
6888 GtkPositionType pos)
6890 GtkNotebookPrivate *priv;
6892 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6894 priv = notebook->priv;
6896 if (priv->tab_pos != pos)
6898 priv->tab_pos = pos;
6899 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6900 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6903 g_object_notify (G_OBJECT (notebook), "tab-pos");
6907 * gtk_notebook_get_tab_pos:
6908 * @notebook: a #GtkNotebook
6910 * Gets the edge at which the tabs for switching pages in the
6911 * notebook are drawn.
6913 * Return value: the edge at which the tabs are drawn
6916 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
6918 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
6920 return notebook->priv->tab_pos;
6924 * gtk_notebook_set_scrollable:
6925 * @notebook: a #GtkNotebook
6926 * @scrollable: %TRUE if scroll arrows should be added
6928 * Sets whether the tab label area will have arrows for scrolling if
6929 * there are too many tabs to fit in the area.
6932 gtk_notebook_set_scrollable (GtkNotebook *notebook,
6933 gboolean scrollable)
6935 GtkNotebookPrivate *priv;
6937 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6939 priv = notebook->priv;
6941 scrollable = (scrollable != FALSE);
6943 if (scrollable != priv->scrollable)
6945 priv->scrollable = scrollable;
6947 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6948 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6950 g_object_notify (G_OBJECT (notebook), "scrollable");
6955 * gtk_notebook_get_scrollable:
6956 * @notebook: a #GtkNotebook
6958 * Returns whether the tab label area has arrows for scrolling. See
6959 * gtk_notebook_set_scrollable().
6961 * Return value: %TRUE if arrows for scrolling are present
6964 gtk_notebook_get_scrollable (GtkNotebook *notebook)
6966 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6968 return notebook->priv->scrollable;
6972 * gtk_notebook_get_tab_hborder:
6973 * @notebook: a #GtkNotebook
6975 * Returns the horizontal width of a tab border.
6977 * Return value: horizontal width of a tab border
6982 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
6984 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6986 return notebook->priv->tab_hborder;
6990 * gtk_notebook_get_tab_vborder:
6991 * @notebook: a #GtkNotebook
6993 * Returns the vertical width of a tab border.
6995 * Return value: vertical width of a tab border
7000 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7002 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7004 return notebook->priv->tab_vborder;
7008 /* Public GtkNotebook Popup Menu Methods:
7010 * gtk_notebook_popup_enable
7011 * gtk_notebook_popup_disable
7016 * gtk_notebook_popup_enable:
7017 * @notebook: a #GtkNotebook
7019 * Enables the popup menu: if the user clicks with the right mouse button on
7020 * the tab labels, a menu with all the pages will be popped up.
7023 gtk_notebook_popup_enable (GtkNotebook *notebook)
7025 GtkNotebookPrivate *priv;
7028 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7030 priv = notebook->priv;
7035 priv->menu = gtk_menu_new ();
7036 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7038 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7039 gtk_notebook_menu_item_create (notebook, list);
7041 gtk_notebook_update_labels (notebook);
7042 gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7043 GTK_WIDGET (notebook),
7044 gtk_notebook_menu_detacher);
7046 g_object_notify (G_OBJECT (notebook), "enable-popup");
7050 * gtk_notebook_popup_disable:
7051 * @notebook: a #GtkNotebook
7053 * Disables the popup menu.
7056 gtk_notebook_popup_disable (GtkNotebook *notebook)
7058 GtkNotebookPrivate *priv;
7060 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7062 priv = notebook->priv;
7067 gtk_container_foreach (GTK_CONTAINER (priv->menu),
7068 (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7069 gtk_widget_destroy (priv->menu);
7071 g_object_notify (G_OBJECT (notebook), "enable-popup");
7074 /* Public GtkNotebook Page Properties Functions:
7076 * gtk_notebook_get_tab_label
7077 * gtk_notebook_set_tab_label
7078 * gtk_notebook_set_tab_label_text
7079 * gtk_notebook_get_menu_label
7080 * gtk_notebook_set_menu_label
7081 * gtk_notebook_set_menu_label_text
7082 * gtk_notebook_get_tab_reorderable
7083 * gtk_notebook_set_tab_reorderable
7084 * gtk_notebook_get_tab_detachable
7085 * gtk_notebook_set_tab_detachable
7089 * gtk_notebook_get_tab_label:
7090 * @notebook: a #GtkNotebook
7093 * Returns the tab label widget for the page @child. %NULL is returned
7094 * if @child is not in @notebook or if no tab label has specifically
7095 * been set for @child.
7097 * Return value: (transfer none): the tab label
7100 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7105 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7106 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7108 list = CHECK_FIND_CHILD (notebook, child);
7112 if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7115 return GTK_NOTEBOOK_PAGE (list)->tab_label;
7119 * gtk_notebook_set_tab_label:
7120 * @notebook: a #GtkNotebook
7122 * @tab_label: (allow-none): the tab label widget to use, or %NULL for default tab
7125 * Changes the tab label for @child. If %NULL is specified
7126 * for @tab_label, then the page will have the label 'page N'.
7129 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7131 GtkWidget *tab_label)
7133 GtkNotebookPrivate *priv;
7134 GtkNotebookPage *page;
7137 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7138 g_return_if_fail (GTK_IS_WIDGET (child));
7140 priv = notebook->priv;
7142 list = CHECK_FIND_CHILD (notebook, child);
7146 /* a NULL pointer indicates a default_tab setting, otherwise
7147 * we need to set the associated label
7151 if (page->tab_label == tab_label)
7155 gtk_notebook_remove_tab_label (notebook, page);
7159 page->default_tab = FALSE;
7160 page->tab_label = tab_label;
7161 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7165 page->default_tab = TRUE;
7166 page->tab_label = NULL;
7168 if (priv->show_tabs)
7172 g_snprintf (string, sizeof(string), _("Page %u"),
7173 gtk_notebook_real_page_position (notebook, list));
7174 page->tab_label = gtk_label_new (string);
7175 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7179 if (page->tab_label)
7180 page->mnemonic_activate_signal =
7181 g_signal_connect (page->tab_label,
7182 "mnemonic-activate",
7183 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7186 if (priv->show_tabs && gtk_widget_get_visible (child))
7188 gtk_widget_show (page->tab_label);
7189 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7192 gtk_notebook_update_tab_states (notebook);
7193 gtk_widget_child_notify (child, "tab-label");
7197 * gtk_notebook_set_tab_label_text:
7198 * @notebook: a #GtkNotebook
7200 * @tab_text: the label text
7202 * Creates a new label and sets it as the tab label for the page
7203 * containing @child.
7206 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7208 const gchar *tab_text)
7210 GtkWidget *tab_label = NULL;
7212 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7215 tab_label = gtk_label_new (tab_text);
7216 gtk_notebook_set_tab_label (notebook, child, tab_label);
7217 gtk_widget_child_notify (child, "tab-label");
7221 * gtk_notebook_get_tab_label_text:
7222 * @notebook: a #GtkNotebook
7223 * @child: a widget contained in a page of @notebook
7225 * Retrieves the text of the tab label for the page containing
7228 * Return value: the text of the tab label, or %NULL if the
7229 * tab label widget is not a #GtkLabel. The
7230 * string is owned by the widget and must not
7233 G_CONST_RETURN gchar *
7234 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7237 GtkWidget *tab_label;
7239 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7240 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7242 tab_label = gtk_notebook_get_tab_label (notebook, child);
7244 if (GTK_IS_LABEL (tab_label))
7245 return gtk_label_get_text (GTK_LABEL (tab_label));
7251 * gtk_notebook_get_menu_label:
7252 * @notebook: a #GtkNotebook
7253 * @child: a widget contained in a page of @notebook
7255 * Retrieves the menu label widget of the page containing @child.
7257 * Return value: (transfer none): the menu label, or %NULL if the
7258 * notebook page does not have a menu label other than the
7259 * default (the tab label).
7262 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7267 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7268 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7270 list = CHECK_FIND_CHILD (notebook, child);
7274 if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7277 return GTK_NOTEBOOK_PAGE (list)->menu_label;
7281 * gtk_notebook_set_menu_label:
7282 * @notebook: a #GtkNotebook
7283 * @child: the child widget
7284 * @menu_label: (allow-none): the menu label, or NULL for default
7286 * Changes the menu label for the page containing @child.
7289 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7291 GtkWidget *menu_label)
7293 GtkNotebookPrivate *priv;
7294 GtkNotebookPage *page;
7297 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7298 g_return_if_fail (GTK_IS_WIDGET (child));
7300 priv = notebook->priv;
7302 list = CHECK_FIND_CHILD (notebook, child);
7307 if (page->menu_label)
7310 gtk_container_remove (GTK_CONTAINER (priv->menu),
7311 gtk_widget_get_parent (page->menu_label));
7313 if (!page->default_menu)
7314 g_object_unref (page->menu_label);
7319 page->menu_label = menu_label;
7320 g_object_ref_sink (page->menu_label);
7321 page->default_menu = FALSE;
7324 page->default_menu = TRUE;
7327 gtk_notebook_menu_item_create (notebook, list);
7328 gtk_widget_child_notify (child, "menu-label");
7332 * gtk_notebook_set_menu_label_text:
7333 * @notebook: a #GtkNotebook
7334 * @child: the child widget
7335 * @menu_text: the label text
7337 * Creates a new label and sets it as the menu label of @child.
7340 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7342 const gchar *menu_text)
7344 GtkWidget *menu_label = NULL;
7346 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7350 menu_label = gtk_label_new (menu_text);
7351 gtk_misc_set_alignment (GTK_MISC (menu_label), 0.0, 0.5);
7353 gtk_notebook_set_menu_label (notebook, child, menu_label);
7354 gtk_widget_child_notify (child, "menu-label");
7358 * gtk_notebook_get_menu_label_text:
7359 * @notebook: a #GtkNotebook
7360 * @child: the child widget of a page of the notebook.
7362 * Retrieves the text of the menu label for the page containing
7365 * Return value: the text of the tab label, or %NULL if the
7366 * widget does not have a menu label other than
7367 * the default menu label, or the menu label widget
7368 * is not a #GtkLabel. The string is owned by
7369 * the widget and must not be freed.
7371 G_CONST_RETURN gchar *
7372 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7375 GtkWidget *menu_label;
7377 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7378 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7380 menu_label = gtk_notebook_get_menu_label (notebook, child);
7382 if (GTK_IS_LABEL (menu_label))
7383 return gtk_label_get_text (GTK_LABEL (menu_label));
7388 /* Helper function called when pages are reordered
7391 gtk_notebook_child_reordered (GtkNotebook *notebook,
7392 GtkNotebookPage *page)
7394 GtkNotebookPrivate *priv = notebook->priv;
7398 GtkWidget *menu_item;
7400 menu_item = gtk_widget_get_parent (page->menu_label);
7401 gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7402 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7403 gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7406 gtk_notebook_update_tab_states (notebook);
7407 gtk_notebook_update_labels (notebook);
7411 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7415 GtkPackType pack_type)
7417 GtkNotebookPrivate *priv;
7418 GtkNotebookPage *page;
7421 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7422 g_return_if_fail (GTK_IS_WIDGET (child));
7424 priv = notebook->priv;
7426 list = CHECK_FIND_CHILD (notebook, child);
7431 expand = expand != FALSE;
7432 fill = fill != FALSE;
7433 if (page->pack == pack_type && page->expand == expand && page->fill == fill)
7436 gtk_widget_freeze_child_notify (child);
7437 page->expand = expand;
7438 gtk_widget_child_notify (child, "tab-expand");
7440 gtk_widget_child_notify (child, "tab-fill");
7441 if (page->pack != pack_type)
7443 page->pack = pack_type;
7444 gtk_notebook_child_reordered (notebook, page);
7446 gtk_widget_child_notify (child, "tab-pack");
7447 gtk_widget_child_notify (child, "position");
7448 if (priv->show_tabs)
7449 gtk_notebook_pages_allocate (notebook);
7450 gtk_widget_thaw_child_notify (child);
7454 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
7458 GtkPackType *pack_type)
7462 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7463 g_return_if_fail (GTK_IS_WIDGET (child));
7465 list = CHECK_FIND_CHILD (notebook, child);
7470 *expand = GTK_NOTEBOOK_PAGE (list)->expand;
7472 *fill = GTK_NOTEBOOK_PAGE (list)->fill;
7474 *pack_type = GTK_NOTEBOOK_PAGE (list)->pack;
7478 * gtk_notebook_reorder_child:
7479 * @notebook: a #GtkNotebook
7480 * @child: the child to move
7481 * @position: the new position, or -1 to move to the end
7483 * Reorders the page containing @child, so that it appears in position
7484 * @position. If @position is greater than or equal to the number of
7485 * children in the list or negative, @child will be moved to the end
7489 gtk_notebook_reorder_child (GtkNotebook *notebook,
7493 GtkNotebookPrivate *priv;
7494 GList *list, *new_list;
7495 GtkNotebookPage *page;
7499 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7500 g_return_if_fail (GTK_IS_WIDGET (child));
7502 priv = notebook->priv;
7504 list = CHECK_FIND_CHILD (notebook, child);
7508 max_pos = g_list_length (priv->children) - 1;
7509 if (position < 0 || position > max_pos)
7512 old_pos = g_list_position (priv->children, list);
7514 if (old_pos == position)
7518 priv->children = g_list_delete_link (priv->children, list);
7520 priv->children = g_list_insert (priv->children, page, position);
7521 new_list = g_list_nth (priv->children, position);
7523 /* Fix up GList references in GtkNotebook structure */
7524 if (priv->first_tab == list)
7525 priv->first_tab = new_list;
7526 if (priv->focus_tab == list)
7527 priv->focus_tab = new_list;
7529 gtk_widget_freeze_child_notify (child);
7531 /* Move around the menu items if necessary */
7532 gtk_notebook_child_reordered (notebook, page);
7533 gtk_widget_child_notify (child, "tab-pack");
7534 gtk_widget_child_notify (child, "position");
7536 if (priv->show_tabs)
7537 gtk_notebook_pages_allocate (notebook);
7539 gtk_widget_thaw_child_notify (child);
7541 g_signal_emit (notebook,
7542 notebook_signals[PAGE_REORDERED],
7549 * gtk_notebook_set_group_name:
7550 * @notebook: a #GtkNotebook
7551 * @name: (allow-none): the name of the notebook group, or %NULL to unset it
7553 * Sets a group name for @notebook.
7555 * Notebooks with the same name will be able to exchange tabs
7556 * via drag and drop. A notebook with a %NULL group name will
7557 * not be able to exchange tabs with any other notebook.
7562 gtk_notebook_set_group_name (GtkNotebook *notebook,
7563 const gchar *group_name)
7565 GtkNotebookPrivate *priv;
7568 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7570 priv = notebook->priv;
7572 group = g_quark_from_string (group_name);
7574 if (priv->group != group)
7576 priv->group = group;
7577 g_object_notify (G_OBJECT (notebook), "group-name");
7582 * gtk_notebook_get_group_name:
7583 * @notebook: a #GtkNotebook
7585 * Gets the current group name for @notebook.
7587 * Return Value: (transfer none): the group name,
7588 * or %NULL if none is set.
7593 gtk_notebook_get_group_name (GtkNotebook *notebook)
7595 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7597 return g_quark_to_string (notebook->priv->group);
7601 * gtk_notebook_get_tab_reorderable:
7602 * @notebook: a #GtkNotebook
7603 * @child: a child #GtkWidget
7605 * Gets whether the tab can be reordered via drag and drop or not.
7607 * Return Value: %TRUE if the tab is reorderable.
7612 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
7617 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7618 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7620 list = CHECK_FIND_CHILD (notebook, child);
7624 return GTK_NOTEBOOK_PAGE (list)->reorderable;
7628 * gtk_notebook_set_tab_reorderable:
7629 * @notebook: a #GtkNotebook
7630 * @child: a child #GtkWidget
7631 * @reorderable: whether the tab is reorderable or not.
7633 * Sets whether the notebook tab can be reordered
7634 * via drag and drop or not.
7639 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
7641 gboolean reorderable)
7645 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7646 g_return_if_fail (GTK_IS_WIDGET (child));
7648 list = CHECK_FIND_CHILD (notebook, child);
7652 if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
7654 GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
7655 gtk_widget_child_notify (child, "reorderable");
7660 * gtk_notebook_get_tab_detachable:
7661 * @notebook: a #GtkNotebook
7662 * @child: a child #GtkWidget
7664 * Returns whether the tab contents can be detached from @notebook.
7666 * Return Value: TRUE if the tab is detachable.
7671 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
7676 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7677 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7679 list = CHECK_FIND_CHILD (notebook, child);
7683 return GTK_NOTEBOOK_PAGE (list)->detachable;
7687 * gtk_notebook_set_tab_detachable:
7688 * @notebook: a #GtkNotebook
7689 * @child: a child #GtkWidget
7690 * @detachable: whether the tab is detachable or not
7692 * Sets whether the tab can be detached from @notebook to another
7693 * notebook or widget.
7695 * Note that 2 notebooks must share a common group identificator
7696 * (see gtk_notebook_set_group()) to allow automatic tabs
7697 * interchange between them.
7699 * If you want a widget to interact with a notebook through DnD
7700 * (i.e.: accept dragged tabs from it) it must be set as a drop
7701 * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
7702 * will fill the selection with a GtkWidget** pointing to the child
7703 * widget that corresponds to the dropped tab.
7706 * on_drop_zone_drag_data_received (GtkWidget *widget,
7707 * GdkDragContext *context,
7710 * GtkSelectionData *selection_data,
7713 * gpointer user_data)
7715 * GtkWidget *notebook;
7716 * GtkWidget **child;
7718 * notebook = gtk_drag_get_source_widget (context);
7719 * child = (void*) selection_data->data;
7721 * process_widget (*child);
7722 * gtk_container_remove (GTK_CONTAINER (notebook), *child);
7726 * If you want a notebook to accept drags from other widgets,
7727 * you will have to set your own DnD code to do it.
7732 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
7734 gboolean detachable)
7738 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7739 g_return_if_fail (GTK_IS_WIDGET (child));
7741 list = CHECK_FIND_CHILD (notebook, child);
7745 if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
7747 GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
7748 gtk_widget_child_notify (child, "detachable");
7753 * gtk_notebook_get_action_widget:
7754 * @notebook: a #GtkNotebook
7755 * @pack_type: pack type of the action widget to receive
7757 * Gets one of the action widgets. See gtk_notebook_set_action_widget().
7759 * Returns: (transfer none): The action widget with the given @pack_type
7760 * or %NULL when this action widget has not been set
7765 gtk_notebook_get_action_widget (GtkNotebook *notebook,
7766 GtkPackType pack_type)
7768 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7770 return notebook->priv->action_widget[pack_type];
7774 * gtk_notebook_set_action_widget:
7775 * @notebook: a #GtkNotebook
7776 * @widget: a #GtkWidget
7777 * @pack_type: pack type of the action widget
7779 * Sets @widget as one of the action widgets. Depending on the pack type
7780 * the widget will be placed before or after the tabs. You can use
7781 * a #GtkBox if you need to pack more than one widget on the same side.
7783 * Note that action widgets are "internal" children of the notebook and thus
7784 * not included in the list returned from gtk_container_foreach().
7789 gtk_notebook_set_action_widget (GtkNotebook *notebook,
7791 GtkPackType pack_type)
7793 GtkNotebookPrivate *priv;
7795 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7796 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
7797 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
7799 priv = notebook->priv;
7801 if (priv->action_widget[pack_type])
7802 gtk_widget_unparent (priv->action_widget[pack_type]);
7804 priv->action_widget[pack_type] = widget;
7808 gtk_widget_set_child_visible (widget, priv->show_tabs);
7809 gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
7812 gtk_widget_queue_resize (GTK_WIDGET (notebook));