1 /* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
2 /* GTK - The GIMP Toolkit
3 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
21 * file for a list of people on the GTK+ Team. See the ChangeLog
22 * files for a list of changes. These files are distributed with
23 * GTK+ at ftp:ftp.gtk.org/pub/gtk/.
31 #include "gtknotebook.h"
35 #include "gtkmenuitem.h"
38 #include "gtkmarshalers.h"
39 #include "gtkbindings.h"
40 #include "gtkprivate.h"
42 #include "gtkbuildable.h"
43 #include "gtktypebuiltins.h"
44 #include "gtkwidgetpath.h"
45 #include "gtkwidgetprivate.h"
46 #include "a11y/gtknotebookaccessible.h"
51 * @Short_description: A tabbed notebook container
53 * @See_also: #GtkContainer
55 * The #GtkNotebook widget is a #GtkContainer whose children are pages that
56 * can be switched between using tab labels along one edge.
58 * There are many configuration options for GtkNotebook. Among other
59 * things, you can choose on which edge the tabs appear
60 * (see gtk_notebook_set_tab_pos()), whether, if there are too many
61 * tabs to fit the notebook should be made bigger or scrolling
62 * arrows added (see gtk_notebook_set_scrollable()), and whether there
63 * will be a popup menu allowing the users to switch pages.
64 * (see gtk_notebook_popup_enable(), gtk_notebook_popup_disable())
66 * <refsect2 id="GtkNotebook-BUILDER-UI">
67 * <title>GtkNotebook as GtkBuildable</title>
69 * The GtkNotebook implementation of the #GtkBuildable interface
70 * supports placing children into tabs by specifying "tab" as the
71 * "type" attribute of a <child> element. Note that the content
72 * of the tab must be created before the tab can be filled.
73 * A tab child can be specified without specifying a <child>
76 * To add a child widget in the notebooks action area, specify
77 * "action-start" or "action-end" as the "type" attribute of the <child>
81 * <title>A UI definition fragment with GtkNotebook</title>
82 * <programlisting><![CDATA[
83 * <object class="GtkNotebook">
85 * <object class="GtkLabel" id="notebook-content">
86 * <property name="label">Content</property>
90 * <object class="GtkLabel" id="notebook-tab">
91 * <property name="label">Tab</property>
95 * ]]></programlisting>
101 #define SCROLL_DELAY_FACTOR 5
102 #define SCROLL_THRESHOLD 12
103 #define DND_THRESHOLD_MULTIPLIER 4
104 #define FRAMES_PER_SECOND 45
105 #define MSECS_BETWEEN_UPDATES (1000 / FRAMES_PER_SECOND)
107 typedef struct _GtkNotebookPage GtkNotebookPage;
112 DRAG_OPERATION_REORDER,
113 DRAG_OPERATION_DETACH
114 } GtkNotebookDragOperation;
122 struct _GtkNotebookPrivate
124 GtkNotebookDragOperation operation;
125 GtkNotebookPage *cur_page;
126 GtkNotebookPage *detached_tab;
127 GtkTargetList *source_targets;
128 GtkWidget *action_widget[N_ACTION_WIDGETS];
129 GtkWidget *dnd_window;
132 GdkWindow *drag_window;
133 GdkWindow *event_window;
136 GList *first_tab; /* The first tab visible (for scrolling notebooks) */
152 guint switch_tab_timer;
159 guint child_has_focus : 1;
160 guint click_child : 3;
161 guint during_detach : 1;
162 guint during_reorder : 1;
163 guint focus_out : 1; /* Flag used by ::move-focus-out implementation */
164 guint has_scrolled : 1;
166 guint need_timer : 1;
167 guint show_border : 1;
169 guint scrollable : 1;
172 guint has_before_previous : 1;
173 guint has_before_next : 1;
174 guint has_after_previous : 1;
175 guint has_after_next : 1;
211 } GtkNotebookPointerPosition;
213 #define ARROW_IS_LEFT(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
214 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
229 CHILD_PROP_TAB_LABEL,
230 CHILD_PROP_MENU_LABEL,
232 CHILD_PROP_TAB_EXPAND,
234 CHILD_PROP_REORDERABLE,
235 CHILD_PROP_DETACHABLE
238 #define GTK_NOTEBOOK_PAGE(_glist_) ((GtkNotebookPage *)((GList *)(_glist_))->data)
240 /* some useful defines for calculating coords */
241 #define PAGE_LEFT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x)
242 #define PAGE_RIGHT_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
243 #define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
244 #define PAGE_TOP_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y)
245 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
246 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
247 #define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent (((GtkNotebookPage *) (_page_))->tab_label) == ((GtkWidget *) (_notebook_)))
249 struct _GtkNotebookPage
252 GtkWidget *tab_label;
253 GtkWidget *menu_label;
254 GtkWidget *last_focus_child; /* Last descendant of the page that had focus */
256 guint default_menu : 1; /* If true, we create the menu label ourself */
257 guint default_tab : 1; /* If true, we create the tab label ourself */
260 guint reorderable : 1;
261 guint detachable : 1;
263 /* if true, the tab label was visible on last allocation; we track this so
264 * that we know to redraw the tab area if a tab label was hidden then shown
265 * without changing position */
266 guint tab_allocated_visible : 1;
268 GtkRequisition requisition;
269 GtkAllocation allocation;
271 gulong mnemonic_activate_signal;
272 gulong notify_visible_handler;
275 static const GtkTargetEntry notebook_targets [] = {
276 { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
279 #ifdef G_DISABLE_CHECKS
280 #define CHECK_FIND_CHILD(notebook, child) \
281 gtk_notebook_find_child (notebook, child, G_STRLOC)
283 #define CHECK_FIND_CHILD(notebook, child) \
284 gtk_notebook_find_child (notebook, child, NULL)
287 /*** GtkNotebook Methods ***/
288 static gboolean gtk_notebook_select_page (GtkNotebook *notebook,
289 gboolean move_focus);
290 static gboolean gtk_notebook_focus_tab (GtkNotebook *notebook,
291 GtkNotebookTab type);
292 static gboolean gtk_notebook_change_current_page (GtkNotebook *notebook,
294 static void gtk_notebook_move_focus_out (GtkNotebook *notebook,
295 GtkDirectionType direction_type);
296 static gboolean gtk_notebook_reorder_tab (GtkNotebook *notebook,
297 GtkDirectionType direction_type,
298 gboolean move_to_last);
299 static void gtk_notebook_remove_tab_label (GtkNotebook *notebook,
300 GtkNotebookPage *page);
301 static void gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
305 static void gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
310 /*** GObject Methods ***/
311 static void gtk_notebook_set_property (GObject *object,
315 static void gtk_notebook_get_property (GObject *object,
320 /*** GtkWidget Methods ***/
321 static void gtk_notebook_destroy (GtkWidget *widget);
322 static void gtk_notebook_map (GtkWidget *widget);
323 static void gtk_notebook_unmap (GtkWidget *widget);
324 static void gtk_notebook_realize (GtkWidget *widget);
325 static void gtk_notebook_unrealize (GtkWidget *widget);
326 static void gtk_notebook_get_preferred_width (GtkWidget *widget,
329 static void gtk_notebook_get_preferred_height(GtkWidget *widget,
332 static void gtk_notebook_get_preferred_width_for_height
337 static void gtk_notebook_get_preferred_height_for_width
342 static void gtk_notebook_size_allocate (GtkWidget *widget,
343 GtkAllocation *allocation);
344 static gint gtk_notebook_draw (GtkWidget *widget,
346 static gint gtk_notebook_button_press (GtkWidget *widget,
347 GdkEventButton *event);
348 static gint gtk_notebook_button_release (GtkWidget *widget,
349 GdkEventButton *event);
350 static gboolean gtk_notebook_popup_menu (GtkWidget *widget);
351 static gint gtk_notebook_leave_notify (GtkWidget *widget,
352 GdkEventCrossing *event);
353 static gint gtk_notebook_motion_notify (GtkWidget *widget,
354 GdkEventMotion *event);
355 static gint gtk_notebook_focus_in (GtkWidget *widget,
356 GdkEventFocus *event);
357 static gint gtk_notebook_focus_out (GtkWidget *widget,
358 GdkEventFocus *event);
359 static void gtk_notebook_grab_notify (GtkWidget *widget,
360 gboolean was_grabbed);
361 static void gtk_notebook_state_flags_changed (GtkWidget *widget,
362 GtkStateFlags previous_state);
363 static gint gtk_notebook_focus (GtkWidget *widget,
364 GtkDirectionType direction);
365 static void gtk_notebook_style_updated (GtkWidget *widget);
367 /*** Drag and drop Methods ***/
368 static void gtk_notebook_drag_begin (GtkWidget *widget,
369 GdkDragContext *context);
370 static void gtk_notebook_drag_end (GtkWidget *widget,
371 GdkDragContext *context);
372 static gboolean gtk_notebook_drag_failed (GtkWidget *widget,
373 GdkDragContext *context,
374 GtkDragResult result);
375 static gboolean gtk_notebook_drag_motion (GtkWidget *widget,
376 GdkDragContext *context,
380 static void gtk_notebook_drag_leave (GtkWidget *widget,
381 GdkDragContext *context,
383 static gboolean gtk_notebook_drag_drop (GtkWidget *widget,
384 GdkDragContext *context,
388 static void gtk_notebook_drag_data_get (GtkWidget *widget,
389 GdkDragContext *context,
390 GtkSelectionData *data,
393 static void gtk_notebook_drag_data_received (GtkWidget *widget,
394 GdkDragContext *context,
397 GtkSelectionData *data,
401 /*** GtkContainer Methods ***/
402 static void gtk_notebook_set_child_property (GtkContainer *container,
407 static void gtk_notebook_get_child_property (GtkContainer *container,
412 static void gtk_notebook_add (GtkContainer *container,
414 static void gtk_notebook_remove (GtkContainer *container,
416 static void gtk_notebook_set_focus_child (GtkContainer *container,
418 static GType gtk_notebook_child_type (GtkContainer *container);
419 static void gtk_notebook_forall (GtkContainer *container,
420 gboolean include_internals,
421 GtkCallback callback,
422 gpointer callback_data);
423 static GtkWidgetPath * gtk_notebook_get_path_for_child (GtkContainer *container,
426 /*** GtkNotebook Methods ***/
427 static gint gtk_notebook_real_insert_page (GtkNotebook *notebook,
429 GtkWidget *tab_label,
430 GtkWidget *menu_label,
433 static GtkNotebook *gtk_notebook_create_window (GtkNotebook *notebook,
438 /*** GtkNotebook Private Functions ***/
439 static void gtk_notebook_redraw_tabs (GtkNotebook *notebook);
440 static void gtk_notebook_redraw_tabs_junction (GtkNotebook *notebook);
441 static void gtk_notebook_redraw_arrows (GtkNotebook *notebook);
442 static void gtk_notebook_real_remove (GtkNotebook *notebook,
444 static void gtk_notebook_update_labels (GtkNotebook *notebook);
445 static gint gtk_notebook_timer (GtkNotebook *notebook);
446 static void gtk_notebook_set_scroll_timer (GtkNotebook *notebook);
447 static gint gtk_notebook_page_compare (gconstpointer a,
449 static GList* gtk_notebook_find_child (GtkNotebook *notebook,
451 const gchar *function);
452 static GList * gtk_notebook_search_page (GtkNotebook *notebook,
455 gboolean find_visible);
456 static void gtk_notebook_child_reordered (GtkNotebook *notebook,
457 GtkNotebookPage *page);
459 /*** GtkNotebook Drawing Functions ***/
460 static void gtk_notebook_paint (GtkWidget *widget,
462 static void gtk_notebook_draw_tab (GtkNotebook *notebook,
463 GtkNotebookPage *page,
466 static void gtk_notebook_draw_arrow (GtkNotebook *notebook,
468 GtkNotebookArrow arrow);
470 /*** GtkNotebook Size Allocate Functions ***/
471 static void gtk_notebook_pages_allocate (GtkNotebook *notebook);
472 static gboolean gtk_notebook_page_allocate (GtkNotebook *notebook,
473 GtkNotebookPage *page);
474 static void gtk_notebook_calc_tabs (GtkNotebook *notebook,
480 /*** GtkNotebook Page Switch Methods ***/
481 static void gtk_notebook_real_switch_page (GtkNotebook *notebook,
485 /*** GtkNotebook Page Switch Functions ***/
486 static void gtk_notebook_switch_page (GtkNotebook *notebook,
487 GtkNotebookPage *page);
488 static gint gtk_notebook_page_select (GtkNotebook *notebook,
489 gboolean move_focus);
490 static void gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
492 static void gtk_notebook_menu_switch_page (GtkWidget *widget,
493 GtkNotebookPage *page);
495 /*** GtkNotebook Menu Functions ***/
496 static void gtk_notebook_menu_item_create (GtkNotebook *notebook,
498 static void gtk_notebook_menu_label_unparent (GtkWidget *widget,
500 static void gtk_notebook_menu_detacher (GtkWidget *widget,
503 /*** GtkNotebook Private Setters ***/
504 static void gtk_notebook_update_tab_states (GtkNotebook *notebook);
505 static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
509 static gboolean focus_tabs_in (GtkNotebook *notebook);
510 static gboolean focus_child_in (GtkNotebook *notebook,
511 GtkDirectionType direction);
513 static void stop_scrolling (GtkNotebook *notebook);
514 static void do_detach_tab (GtkNotebook *from,
521 static void gtk_notebook_buildable_init (GtkBuildableIface *iface);
522 static void gtk_notebook_buildable_add_child (GtkBuildable *buildable,
527 static guint notebook_signals[LAST_SIGNAL] = { 0 };
529 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
530 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
531 gtk_notebook_buildable_init))
534 add_tab_bindings (GtkBindingSet *binding_set,
535 GdkModifierType modifiers,
536 GtkDirectionType direction)
538 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
540 GTK_TYPE_DIRECTION_TYPE, direction);
541 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
543 GTK_TYPE_DIRECTION_TYPE, direction);
547 add_arrow_bindings (GtkBindingSet *binding_set,
549 GtkDirectionType direction)
551 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
553 gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
555 GTK_TYPE_DIRECTION_TYPE, direction);
556 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
558 GTK_TYPE_DIRECTION_TYPE, direction);
562 add_reorder_bindings (GtkBindingSet *binding_set,
564 GtkDirectionType direction,
565 gboolean move_to_last)
567 guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
569 gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
571 GTK_TYPE_DIRECTION_TYPE, direction,
572 G_TYPE_BOOLEAN, move_to_last);
573 gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
575 GTK_TYPE_DIRECTION_TYPE, direction,
576 G_TYPE_BOOLEAN, move_to_last);
580 gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
582 const GValue *handler_return,
585 gboolean continue_emission;
588 object = g_value_get_object (handler_return);
589 g_value_set_object (return_accu, object);
590 continue_emission = !object;
592 return continue_emission;
596 gtk_notebook_compute_expand (GtkWidget *widget,
600 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
601 GtkNotebookPrivate *priv = notebook->priv;
605 GtkNotebookPage *page;
610 for (list = priv->children; list; list = list->next)
615 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_HORIZONTAL);
618 gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);
620 if (hexpand & vexpand)
624 *hexpand_p = hexpand;
625 *vexpand_p = vexpand;
629 gtk_notebook_class_init (GtkNotebookClass *class)
631 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
632 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
633 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
634 GtkBindingSet *binding_set;
636 gobject_class->set_property = gtk_notebook_set_property;
637 gobject_class->get_property = gtk_notebook_get_property;
639 widget_class->destroy = gtk_notebook_destroy;
640 widget_class->map = gtk_notebook_map;
641 widget_class->unmap = gtk_notebook_unmap;
642 widget_class->realize = gtk_notebook_realize;
643 widget_class->unrealize = gtk_notebook_unrealize;
644 widget_class->get_preferred_width = gtk_notebook_get_preferred_width;
645 widget_class->get_preferred_height = gtk_notebook_get_preferred_height;
646 widget_class->get_preferred_width_for_height = gtk_notebook_get_preferred_width_for_height;
647 widget_class->get_preferred_height_for_width = gtk_notebook_get_preferred_height_for_width;
648 widget_class->size_allocate = gtk_notebook_size_allocate;
649 widget_class->draw = gtk_notebook_draw;
650 widget_class->button_press_event = gtk_notebook_button_press;
651 widget_class->button_release_event = gtk_notebook_button_release;
652 widget_class->popup_menu = gtk_notebook_popup_menu;
653 widget_class->leave_notify_event = gtk_notebook_leave_notify;
654 widget_class->motion_notify_event = gtk_notebook_motion_notify;
655 widget_class->grab_notify = gtk_notebook_grab_notify;
656 widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
657 widget_class->focus_in_event = gtk_notebook_focus_in;
658 widget_class->focus_out_event = gtk_notebook_focus_out;
659 widget_class->focus = gtk_notebook_focus;
660 widget_class->style_updated = gtk_notebook_style_updated;
661 widget_class->drag_begin = gtk_notebook_drag_begin;
662 widget_class->drag_end = gtk_notebook_drag_end;
663 widget_class->drag_motion = gtk_notebook_drag_motion;
664 widget_class->drag_leave = gtk_notebook_drag_leave;
665 widget_class->drag_drop = gtk_notebook_drag_drop;
666 widget_class->drag_data_get = gtk_notebook_drag_data_get;
667 widget_class->drag_data_received = gtk_notebook_drag_data_received;
668 widget_class->drag_failed = gtk_notebook_drag_failed;
669 widget_class->compute_expand = gtk_notebook_compute_expand;
671 container_class->add = gtk_notebook_add;
672 container_class->remove = gtk_notebook_remove;
673 container_class->forall = gtk_notebook_forall;
674 container_class->set_focus_child = gtk_notebook_set_focus_child;
675 container_class->get_child_property = gtk_notebook_get_child_property;
676 container_class->set_child_property = gtk_notebook_set_child_property;
677 container_class->child_type = gtk_notebook_child_type;
678 container_class->get_path_for_child = gtk_notebook_get_path_for_child;
680 class->switch_page = gtk_notebook_real_switch_page;
681 class->insert_page = gtk_notebook_real_insert_page;
683 class->focus_tab = gtk_notebook_focus_tab;
684 class->select_page = gtk_notebook_select_page;
685 class->change_current_page = gtk_notebook_change_current_page;
686 class->move_focus_out = gtk_notebook_move_focus_out;
687 class->reorder_tab = gtk_notebook_reorder_tab;
688 class->create_window = gtk_notebook_create_window;
690 g_object_class_install_property (gobject_class,
692 g_param_spec_int ("page",
694 P_("The index of the current page"),
698 GTK_PARAM_READWRITE));
699 g_object_class_install_property (gobject_class,
701 g_param_spec_enum ("tab-pos",
703 P_("Which side of the notebook holds the tabs"),
704 GTK_TYPE_POSITION_TYPE,
706 GTK_PARAM_READWRITE));
707 g_object_class_install_property (gobject_class,
709 g_param_spec_boolean ("show-tabs",
711 P_("Whether tabs should be shown"),
713 GTK_PARAM_READWRITE));
714 g_object_class_install_property (gobject_class,
716 g_param_spec_boolean ("show-border",
718 P_("Whether the border should be shown"),
720 GTK_PARAM_READWRITE));
721 g_object_class_install_property (gobject_class,
723 g_param_spec_boolean ("scrollable",
725 P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
727 GTK_PARAM_READWRITE));
728 g_object_class_install_property (gobject_class,
730 g_param_spec_boolean ("enable-popup",
732 P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
734 GTK_PARAM_READWRITE));
737 * GtkNotebook:group-name:
739 * Group name for tab drag and drop.
743 g_object_class_install_property (gobject_class,
745 g_param_spec_string ("group-name",
747 P_("Group name for tab drag and drop"),
749 GTK_PARAM_READWRITE));
751 gtk_container_class_install_child_property (container_class,
752 CHILD_PROP_TAB_LABEL,
753 g_param_spec_string ("tab-label",
755 P_("The string displayed on the child's tab label"),
757 GTK_PARAM_READWRITE));
758 gtk_container_class_install_child_property (container_class,
759 CHILD_PROP_MENU_LABEL,
760 g_param_spec_string ("menu-label",
762 P_("The string displayed in the child's menu entry"),
764 GTK_PARAM_READWRITE));
765 gtk_container_class_install_child_property (container_class,
767 g_param_spec_int ("position",
769 P_("The index of the child in the parent"),
771 GTK_PARAM_READWRITE));
772 gtk_container_class_install_child_property (container_class,
773 CHILD_PROP_TAB_EXPAND,
774 g_param_spec_boolean ("tab-expand",
776 P_("Whether to expand the child's tab"),
778 GTK_PARAM_READWRITE));
779 gtk_container_class_install_child_property (container_class,
781 g_param_spec_boolean ("tab-fill",
783 P_("Whether the child's tab should fill the allocated area"),
785 GTK_PARAM_READWRITE));
787 gtk_container_class_install_child_property (container_class,
788 CHILD_PROP_REORDERABLE,
789 g_param_spec_boolean ("reorderable",
790 P_("Tab reorderable"),
791 P_("Whether the tab is reorderable by user action"),
793 GTK_PARAM_READWRITE));
794 gtk_container_class_install_child_property (container_class,
795 CHILD_PROP_DETACHABLE,
796 g_param_spec_boolean ("detachable",
797 P_("Tab detachable"),
798 P_("Whether the tab is detachable"),
800 GTK_PARAM_READWRITE));
803 * GtkNotebook:has-secondary-backward-stepper:
805 * The "has-secondary-backward-stepper" property determines whether
806 * a second backward arrow button is displayed on the opposite end
811 gtk_widget_class_install_style_property (widget_class,
812 g_param_spec_boolean ("has-secondary-backward-stepper",
813 P_("Secondary backward stepper"),
814 P_("Display a second backward arrow button on the opposite end of the tab area"),
816 GTK_PARAM_READABLE));
819 * GtkNotebook:has-secondary-forward-stepper:
821 * The "has-secondary-forward-stepper" property determines whether
822 * a second forward arrow button is displayed on the opposite end
827 gtk_widget_class_install_style_property (widget_class,
828 g_param_spec_boolean ("has-secondary-forward-stepper",
829 P_("Secondary forward stepper"),
830 P_("Display a second forward arrow button on the opposite end of the tab area"),
832 GTK_PARAM_READABLE));
835 * GtkNotebook:has-backward-stepper:
837 * The "has-backward-stepper" property determines whether
838 * the standard backward arrow button is displayed.
842 gtk_widget_class_install_style_property (widget_class,
843 g_param_spec_boolean ("has-backward-stepper",
844 P_("Backward stepper"),
845 P_("Display the standard backward arrow button"),
847 GTK_PARAM_READABLE));
850 * GtkNotebook:has-forward-stepper:
852 * The "has-forward-stepper" property determines whether
853 * the standard forward arrow button is displayed.
857 gtk_widget_class_install_style_property (widget_class,
858 g_param_spec_boolean ("has-forward-stepper",
859 P_("Forward stepper"),
860 P_("Display the standard forward arrow button"),
862 GTK_PARAM_READABLE));
865 * GtkNotebook:tab-overlap:
867 * The "tab-overlap" property defines size of tab overlap
872 gtk_widget_class_install_style_property (widget_class,
873 g_param_spec_int ("tab-overlap",
875 P_("Size of tab overlap area"),
879 GTK_PARAM_READABLE));
882 * GtkNotebook:tab-curvature:
884 * The "tab-curvature" property defines size of tab curvature.
888 gtk_widget_class_install_style_property (widget_class,
889 g_param_spec_int ("tab-curvature",
891 P_("Size of tab curvature"),
895 GTK_PARAM_READABLE));
898 * GtkNotebook:arrow-spacing:
900 * The "arrow-spacing" property defines the spacing between the scroll
901 * arrows and the tabs.
905 gtk_widget_class_install_style_property (widget_class,
906 g_param_spec_int ("arrow-spacing",
908 P_("Scroll arrow spacing"),
912 GTK_PARAM_READABLE));
915 * GtkNotebook:initial-gap:
917 * The "initial-gap" property defines the minimum size for the initial
918 * gap between the first tab.
922 gtk_widget_class_install_style_property (widget_class,
923 g_param_spec_int ("initial-gap",
925 P_("Initial gap before the first tab"),
929 GTK_PARAM_READABLE));
932 * GtkNotebook::switch-page:
933 * @notebook: the object which received the signal.
934 * @page: the new current page
935 * @page_num: the index of the page
937 * Emitted when the user or a function changes the current page.
939 notebook_signals[SWITCH_PAGE] =
940 g_signal_new (I_("switch-page"),
941 G_TYPE_FROM_CLASS (gobject_class),
943 G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
945 _gtk_marshal_VOID__OBJECT_UINT,
949 notebook_signals[FOCUS_TAB] =
950 g_signal_new (I_("focus-tab"),
951 G_TYPE_FROM_CLASS (gobject_class),
952 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
953 G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
955 _gtk_marshal_BOOLEAN__ENUM,
957 GTK_TYPE_NOTEBOOK_TAB);
958 notebook_signals[SELECT_PAGE] =
959 g_signal_new (I_("select-page"),
960 G_TYPE_FROM_CLASS (gobject_class),
961 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
962 G_STRUCT_OFFSET (GtkNotebookClass, select_page),
964 _gtk_marshal_BOOLEAN__BOOLEAN,
967 notebook_signals[CHANGE_CURRENT_PAGE] =
968 g_signal_new (I_("change-current-page"),
969 G_TYPE_FROM_CLASS (gobject_class),
970 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
971 G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
973 _gtk_marshal_BOOLEAN__INT,
976 notebook_signals[MOVE_FOCUS_OUT] =
977 g_signal_new (I_("move-focus-out"),
978 G_TYPE_FROM_CLASS (gobject_class),
979 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
980 G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
982 _gtk_marshal_VOID__ENUM,
984 GTK_TYPE_DIRECTION_TYPE);
985 notebook_signals[REORDER_TAB] =
986 g_signal_new (I_("reorder-tab"),
987 G_TYPE_FROM_CLASS (gobject_class),
988 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
989 G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
991 _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
993 GTK_TYPE_DIRECTION_TYPE,
996 * GtkNotebook::page-reordered:
997 * @notebook: the #GtkNotebook
998 * @child: the child #GtkWidget affected
999 * @page_num: the new page number for @child
1001 * the ::page-reordered signal is emitted in the notebook
1002 * right after a page has been reordered.
1006 notebook_signals[PAGE_REORDERED] =
1007 g_signal_new (I_("page-reordered"),
1008 G_TYPE_FROM_CLASS (gobject_class),
1010 G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1012 _gtk_marshal_VOID__OBJECT_UINT,
1017 * GtkNotebook::page-removed:
1018 * @notebook: the #GtkNotebook
1019 * @child: the child #GtkWidget affected
1020 * @page_num: the @child page number
1022 * the ::page-removed signal is emitted in the notebook
1023 * right after a page is removed from the notebook.
1027 notebook_signals[PAGE_REMOVED] =
1028 g_signal_new (I_("page-removed"),
1029 G_TYPE_FROM_CLASS (gobject_class),
1031 G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1033 _gtk_marshal_VOID__OBJECT_UINT,
1038 * GtkNotebook::page-added:
1039 * @notebook: the #GtkNotebook
1040 * @child: the child #GtkWidget affected
1041 * @page_num: the new page number for @child
1043 * the ::page-added signal is emitted in the notebook
1044 * right after a page is added to the notebook.
1048 notebook_signals[PAGE_ADDED] =
1049 g_signal_new (I_("page-added"),
1050 G_TYPE_FROM_CLASS (gobject_class),
1052 G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1054 _gtk_marshal_VOID__OBJECT_UINT,
1060 * GtkNotebook::create-window:
1061 * @notebook: the #GtkNotebook emitting the signal
1062 * @page: the tab of @notebook that is being detached
1063 * @x: the X coordinate where the drop happens
1064 * @y: the Y coordinate where the drop happens
1066 * The ::create-window signal is emitted when a detachable
1067 * tab is dropped on the root window.
1069 * A handler for this signal can create a window containing
1070 * a notebook where the tab will be attached. It is also
1071 * responsible for moving/resizing the window and adding the
1072 * necessary properties to the notebook (e.g. the
1073 * #GtkNotebook:group-name ).
1075 * Returns: (transfer none): a #GtkNotebook that @page should be
1076 * added to, or %NULL.
1080 notebook_signals[CREATE_WINDOW] =
1081 g_signal_new (I_("create-window"),
1082 G_TYPE_FROM_CLASS (gobject_class),
1084 G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1085 gtk_object_handled_accumulator, NULL,
1086 _gtk_marshal_OBJECT__OBJECT_INT_INT,
1087 GTK_TYPE_NOTEBOOK, 3,
1088 GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
1090 binding_set = gtk_binding_set_by_class (class);
1091 gtk_binding_entry_add_signal (binding_set,
1094 G_TYPE_BOOLEAN, FALSE);
1095 gtk_binding_entry_add_signal (binding_set,
1096 GDK_KEY_KP_Space, 0,
1098 G_TYPE_BOOLEAN, FALSE);
1100 gtk_binding_entry_add_signal (binding_set,
1103 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1104 gtk_binding_entry_add_signal (binding_set,
1107 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1108 gtk_binding_entry_add_signal (binding_set,
1111 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1112 gtk_binding_entry_add_signal (binding_set,
1115 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1117 gtk_binding_entry_add_signal (binding_set,
1118 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1119 "change-current-page", 1,
1121 gtk_binding_entry_add_signal (binding_set,
1122 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1123 "change-current-page", 1,
1126 gtk_binding_entry_add_signal (binding_set,
1127 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1128 "change-current-page", 1,
1130 gtk_binding_entry_add_signal (binding_set,
1131 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1132 "change-current-page", 1,
1135 add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1136 add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1137 add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1138 add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1140 add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1141 add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1142 add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1143 add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1144 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1145 add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1146 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1147 add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1149 add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1150 add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1152 g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1154 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_NOTEBOOK_ACCESSIBLE);
1158 gtk_notebook_init (GtkNotebook *notebook)
1160 GtkNotebookPrivate *priv;
1161 GtkStyleContext *context;
1163 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1164 gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1166 notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1168 GtkNotebookPrivate);
1169 priv = notebook->priv;
1171 priv->cur_page = NULL;
1172 priv->children = NULL;
1173 priv->first_tab = NULL;
1174 priv->focus_tab = NULL;
1175 priv->event_window = NULL;
1178 priv->show_tabs = TRUE;
1179 priv->show_border = TRUE;
1180 priv->tab_pos = GTK_POS_TOP;
1181 priv->scrollable = FALSE;
1183 priv->click_child = 0;
1185 priv->need_timer = 0;
1186 priv->child_has_focus = FALSE;
1187 priv->focus_out = FALSE;
1189 priv->has_before_previous = 1;
1190 priv->has_before_next = 0;
1191 priv->has_after_previous = 0;
1192 priv->has_after_next = 1;
1195 priv->pressed_button = -1;
1196 priv->dnd_timer = 0;
1197 priv->switch_tab_timer = 0;
1198 priv->source_targets = gtk_target_list_new (notebook_targets,
1199 G_N_ELEMENTS (notebook_targets));
1200 priv->operation = DRAG_OPERATION_NONE;
1201 priv->detached_tab = NULL;
1202 priv->during_detach = FALSE;
1203 priv->has_scrolled = FALSE;
1205 gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1206 notebook_targets, G_N_ELEMENTS (notebook_targets),
1209 gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1211 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
1212 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
1216 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1218 iface->add_child = gtk_notebook_buildable_add_child;
1222 gtk_notebook_buildable_add_child (GtkBuildable *buildable,
1223 GtkBuilder *builder,
1227 GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1229 if (type && strcmp (type, "tab") == 0)
1233 page = gtk_notebook_get_nth_page (notebook, -1);
1234 /* To set the tab label widget, we must have already a child
1235 * inside the tab container. */
1236 g_assert (page != NULL);
1237 /* warn when Glade tries to overwrite label */
1238 if (gtk_notebook_get_tab_label (notebook, page))
1239 g_warning ("Overriding tab label for notebook");
1240 gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1242 else if (type && strcmp (type, "action-start") == 0)
1244 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1246 else if (type && strcmp (type, "action-end") == 0)
1248 gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1251 gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1253 GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1257 gtk_notebook_select_page (GtkNotebook *notebook,
1258 gboolean move_focus)
1260 GtkNotebookPrivate *priv = notebook->priv;
1262 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1264 gtk_notebook_page_select (notebook, move_focus);
1272 gtk_notebook_focus_tab (GtkNotebook *notebook,
1273 GtkNotebookTab type)
1275 GtkNotebookPrivate *priv = notebook->priv;
1278 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1282 case GTK_NOTEBOOK_TAB_FIRST:
1283 list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1285 gtk_notebook_switch_focus_tab (notebook, list);
1287 case GTK_NOTEBOOK_TAB_LAST:
1288 list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1290 gtk_notebook_switch_focus_tab (notebook, list);
1301 gtk_notebook_change_current_page (GtkNotebook *notebook,
1304 GtkNotebookPrivate *priv = notebook->priv;
1305 GList *current = NULL;
1307 if (!priv->show_tabs)
1311 current = g_list_find (priv->children, priv->cur_page);
1315 current = gtk_notebook_search_page (notebook, current,
1316 offset < 0 ? STEP_PREV : STEP_NEXT,
1321 gboolean wrap_around;
1323 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1324 "gtk-keynav-wrap-around", &wrap_around,
1328 current = gtk_notebook_search_page (notebook, NULL,
1329 offset < 0 ? STEP_PREV : STEP_NEXT,
1335 offset += offset < 0 ? 1 : -1;
1339 gtk_notebook_switch_page (notebook, current->data);
1341 gtk_widget_error_bell (GTK_WIDGET (notebook));
1346 static GtkDirectionType
1347 get_effective_direction (GtkNotebook *notebook,
1348 GtkDirectionType direction)
1350 GtkNotebookPrivate *priv = notebook->priv;
1352 /* Remap the directions into the effective direction it would be for a
1353 * GTK_POS_TOP notebook
1356 #define D(rest) GTK_DIR_##rest
1358 static const GtkDirectionType translate_direction[2][4][6] = {
1359 /* LEFT */ {{ D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1360 /* RIGHT */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1361 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(LEFT), D(RIGHT) },
1362 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(LEFT), D(RIGHT) }},
1363 /* LEFT */ {{ D(TAB_BACKWARD), D(TAB_FORWARD), D(LEFT), D(RIGHT), D(DOWN), D(UP) },
1364 /* RIGHT */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP), D(DOWN) },
1365 /* TOP */ { D(TAB_FORWARD), D(TAB_BACKWARD), D(UP), D(DOWN), D(RIGHT), D(LEFT) },
1366 /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD), D(DOWN), D(UP), D(RIGHT), D(LEFT) }},
1371 int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1373 return translate_direction[text_dir][priv->tab_pos][direction];
1377 get_effective_tab_pos (GtkNotebook *notebook)
1379 GtkNotebookPrivate *priv = notebook->priv;
1381 if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1383 switch (priv->tab_pos)
1386 return GTK_POS_RIGHT;
1388 return GTK_POS_LEFT;
1393 return priv->tab_pos;
1397 get_tab_gap_pos (GtkNotebook *notebook)
1399 gint tab_pos = get_effective_tab_pos (notebook);
1400 gint gap_side = GTK_POS_BOTTOM;
1405 gap_side = GTK_POS_BOTTOM;
1407 case GTK_POS_BOTTOM:
1408 gap_side = GTK_POS_TOP;
1411 gap_side = GTK_POS_RIGHT;
1414 gap_side = GTK_POS_LEFT;
1422 gtk_notebook_move_focus_out (GtkNotebook *notebook,
1423 GtkDirectionType direction_type)
1425 GtkNotebookPrivate *priv = notebook->priv;
1426 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1427 GtkWidget *toplevel;
1429 if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1430 if (focus_tabs_in (notebook))
1432 if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1433 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1436 /* At this point, we know we should be focusing out of the notebook entirely. We
1437 * do this by setting a flag, then propagating the focus motion to the notebook.
1439 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1440 if (!gtk_widget_is_toplevel (toplevel))
1443 g_object_ref (notebook);
1445 priv->focus_out = TRUE;
1446 g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1447 priv->focus_out = FALSE;
1449 g_object_unref (notebook);
1453 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1455 GtkNotebookPrivate *priv = notebook->priv;
1458 if (position == tab)
1459 return g_list_position (priv->children, tab);
1461 /* check that we aren't inserting the tab in the
1462 * same relative position, taking packing into account */
1463 elem = (position) ? position->prev : g_list_last (priv->children);
1466 return g_list_position (priv->children, tab);
1468 /* now actually reorder the tab */
1469 if (priv->first_tab == tab)
1470 priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1473 priv->children = g_list_remove_link (priv->children, tab);
1476 elem = g_list_last (priv->children);
1479 elem = position->prev;
1480 position->prev = tab;
1486 priv->children = tab;
1489 tab->next = position;
1491 return g_list_position (priv->children, tab);
1495 gtk_notebook_reorder_tab (GtkNotebook *notebook,
1496 GtkDirectionType direction_type,
1497 gboolean move_to_last)
1499 GtkNotebookPrivate *priv = notebook->priv;
1500 GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1501 GList *last, *child;
1504 if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1507 if (!priv->cur_page ||
1508 !priv->cur_page->reorderable)
1511 if (effective_direction != GTK_DIR_LEFT &&
1512 effective_direction != GTK_DIR_RIGHT)
1517 child = priv->focus_tab;
1522 child = gtk_notebook_search_page (notebook, last,
1523 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1531 child = gtk_notebook_search_page (notebook, priv->focus_tab,
1532 (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1535 if (!child || child->data == priv->cur_page)
1538 if (effective_direction == GTK_DIR_RIGHT)
1539 page_num = reorder_tab (notebook, child->next, priv->focus_tab);
1541 page_num = reorder_tab (notebook, child, priv->focus_tab);
1543 gtk_notebook_pages_allocate (notebook);
1545 g_signal_emit (notebook,
1546 notebook_signals[PAGE_REORDERED],
1548 ((GtkNotebookPage *) priv->focus_tab->data)->child,
1557 * Creates a new #GtkNotebook widget with no pages.
1559 * Return value: the newly created #GtkNotebook
1562 gtk_notebook_new (void)
1564 return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1567 /* Private GObject Methods :
1569 * gtk_notebook_set_property
1570 * gtk_notebook_get_property
1573 gtk_notebook_set_property (GObject *object,
1575 const GValue *value,
1578 GtkNotebook *notebook;
1580 notebook = GTK_NOTEBOOK (object);
1584 case PROP_SHOW_TABS:
1585 gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1587 case PROP_SHOW_BORDER:
1588 gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1590 case PROP_SCROLLABLE:
1591 gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1593 case PROP_ENABLE_POPUP:
1594 if (g_value_get_boolean (value))
1595 gtk_notebook_popup_enable (notebook);
1597 gtk_notebook_popup_disable (notebook);
1600 gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1603 gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1605 case PROP_GROUP_NAME:
1606 gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1609 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1615 gtk_notebook_get_property (GObject *object,
1620 GtkNotebook *notebook = GTK_NOTEBOOK (object);
1621 GtkNotebookPrivate *priv = notebook->priv;
1625 case PROP_SHOW_TABS:
1626 g_value_set_boolean (value, priv->show_tabs);
1628 case PROP_SHOW_BORDER:
1629 g_value_set_boolean (value, priv->show_border);
1631 case PROP_SCROLLABLE:
1632 g_value_set_boolean (value, priv->scrollable);
1634 case PROP_ENABLE_POPUP:
1635 g_value_set_boolean (value, priv->menu != NULL);
1638 g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1641 g_value_set_enum (value, priv->tab_pos);
1643 case PROP_GROUP_NAME:
1644 g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1647 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1652 /* Private GtkWidget Methods :
1654 * gtk_notebook_destroy
1656 * gtk_notebook_unmap
1657 * gtk_notebook_realize
1658 * gtk_notebook_size_request
1659 * gtk_notebook_size_allocate
1661 * gtk_notebook_scroll
1662 * gtk_notebook_button_press
1663 * gtk_notebook_button_release
1664 * gtk_notebook_popup_menu
1665 * gtk_notebook_leave_notify
1666 * gtk_notebook_motion_notify
1667 * gtk_notebook_focus_in
1668 * gtk_notebook_focus_out
1669 * gtk_notebook_style_updated
1670 * gtk_notebook_drag_begin
1671 * gtk_notebook_drag_end
1672 * gtk_notebook_drag_failed
1673 * gtk_notebook_drag_motion
1674 * gtk_notebook_drag_drop
1675 * gtk_notebook_drag_data_get
1676 * gtk_notebook_drag_data_received
1679 remove_switch_tab_timer (GtkNotebook *notebook)
1681 GtkNotebookPrivate *priv = notebook->priv;
1683 if (priv->switch_tab_timer)
1685 g_source_remove (priv->switch_tab_timer);
1686 priv->switch_tab_timer = 0;
1691 gtk_notebook_destroy (GtkWidget *widget)
1693 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1694 GtkNotebookPrivate *priv = notebook->priv;
1696 if (priv->action_widget[GTK_PACK_START])
1698 gtk_widget_unparent (priv->action_widget[GTK_PACK_START]);
1699 priv->action_widget[GTK_PACK_START] = NULL;
1702 if (priv->action_widget[GTK_PACK_END])
1704 gtk_widget_unparent (priv->action_widget[GTK_PACK_END]);
1705 priv->action_widget[GTK_PACK_END] = NULL;
1709 gtk_notebook_popup_disable (notebook);
1711 if (priv->source_targets)
1713 gtk_target_list_unref (priv->source_targets);
1714 priv->source_targets = NULL;
1717 remove_switch_tab_timer (notebook);
1719 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->destroy (widget);
1723 gtk_notebook_get_event_window_position (GtkNotebook *notebook,
1724 GdkRectangle *rectangle)
1726 GtkNotebookPrivate *priv = notebook->priv;
1727 GtkAllocation allocation, action_allocation;
1728 GtkWidget *widget = GTK_WIDGET (notebook);
1729 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1730 GtkNotebookPage *visible_page = NULL;
1732 gint tab_pos = get_effective_tab_pos (notebook);
1736 for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1738 GtkNotebookPage *page = tmp_list->data;
1739 if (gtk_widget_get_visible (page->child))
1741 visible_page = page;
1746 if (priv->show_tabs && visible_page)
1750 gtk_widget_get_allocation (widget, &allocation);
1752 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1753 rectangle->x = allocation.x + border_width;
1754 rectangle->y = allocation.y + border_width;
1759 case GTK_POS_BOTTOM:
1760 rectangle->width = allocation.width - 2 * border_width;
1761 rectangle->height = visible_page->requisition.height;
1762 if (tab_pos == GTK_POS_BOTTOM)
1763 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1765 for (i = 0; i < N_ACTION_WIDGETS; i++)
1767 if (priv->action_widget[i] &&
1768 gtk_widget_get_visible (priv->action_widget[i]))
1770 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1772 rectangle->width -= action_allocation.width;
1773 if ((!is_rtl && i == ACTION_WIDGET_START) ||
1774 (is_rtl && i == ACTION_WIDGET_END))
1775 rectangle->x += action_allocation.width;
1781 rectangle->width = visible_page->requisition.width;
1782 rectangle->height = allocation.height - 2 * border_width;
1783 if (tab_pos == GTK_POS_RIGHT)
1784 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1786 for (i = 0; i < N_ACTION_WIDGETS; i++)
1788 if (priv->action_widget[i] &&
1789 gtk_widget_get_visible (priv->action_widget[i]))
1791 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1793 rectangle->height -= action_allocation.height;
1795 if (i == ACTION_WIDGET_START)
1796 rectangle->y += action_allocation.height;
1809 rectangle->x = rectangle->y = 0;
1810 rectangle->width = rectangle->height = 10;
1818 gtk_notebook_map (GtkWidget *widget)
1820 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1821 GtkNotebookPrivate *priv = notebook->priv;
1822 GtkNotebookPage *page;
1826 gtk_widget_set_mapped (widget, TRUE);
1828 if (priv->cur_page &&
1829 gtk_widget_get_visible (priv->cur_page->child) &&
1830 !gtk_widget_get_mapped (priv->cur_page->child))
1831 gtk_widget_map (priv->cur_page->child);
1833 for (i = 0; i < N_ACTION_WIDGETS; i++)
1835 if (priv->action_widget[i] &&
1836 gtk_widget_get_visible (priv->action_widget[i]) &&
1837 gtk_widget_get_child_visible (priv->action_widget[i]) &&
1838 !gtk_widget_get_mapped (priv->action_widget[i]))
1839 gtk_widget_map (priv->action_widget[i]);
1842 if (priv->scrollable)
1843 gtk_notebook_pages_allocate (notebook);
1846 children = priv->children;
1850 page = children->data;
1851 children = children->next;
1853 if (page->tab_label &&
1854 gtk_widget_get_visible (page->tab_label) &&
1855 !gtk_widget_get_mapped (page->tab_label))
1856 gtk_widget_map (page->tab_label);
1860 if (gtk_notebook_get_event_window_position (notebook, NULL))
1861 gdk_window_show_unraised (priv->event_window);
1865 gtk_notebook_unmap (GtkWidget *widget)
1867 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1868 GtkNotebookPrivate *priv = notebook->priv;
1870 stop_scrolling (notebook);
1872 gtk_widget_set_mapped (widget, FALSE);
1874 gdk_window_hide (priv->event_window);
1876 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1880 gtk_notebook_realize (GtkWidget *widget)
1882 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1883 GtkNotebookPrivate *priv = notebook->priv;
1885 GdkWindowAttr attributes;
1886 gint attributes_mask;
1887 GdkRectangle event_window_pos;
1889 gtk_widget_set_realized (widget, TRUE);
1891 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1893 window = gtk_widget_get_parent_window (widget);
1894 gtk_widget_set_window (widget, window);
1895 g_object_ref (window);
1897 attributes.window_type = GDK_WINDOW_CHILD;
1898 attributes.x = event_window_pos.x;
1899 attributes.y = event_window_pos.y;
1900 attributes.width = event_window_pos.width;
1901 attributes.height = event_window_pos.height;
1902 attributes.wclass = GDK_INPUT_ONLY;
1903 attributes.event_mask = gtk_widget_get_events (widget);
1904 attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1905 GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1906 GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1907 attributes_mask = GDK_WA_X | GDK_WA_Y;
1909 priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1910 &attributes, attributes_mask);
1911 gtk_widget_register_window (widget, priv->event_window);
1915 gtk_notebook_unrealize (GtkWidget *widget)
1917 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1918 GtkNotebookPrivate *priv = notebook->priv;
1920 gtk_widget_unregister_window (widget, priv->event_window);
1921 gdk_window_destroy (priv->event_window);
1922 priv->event_window = NULL;
1924 if (priv->drag_window)
1926 gtk_widget_unregister_window (widget, priv->drag_window);
1927 gdk_window_destroy (priv->drag_window);
1928 priv->drag_window = NULL;
1931 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1934 static GtkRegionFlags
1935 _gtk_notebook_get_tab_flags (GtkNotebook *notebook,
1936 GtkNotebookPage *page)
1938 GtkNotebookPrivate *priv = notebook->priv;
1939 gint i = 0, page_num = -1;
1940 GtkRegionFlags flags = 0;
1941 gboolean is_last = FALSE;
1944 for (pages = priv->children; pages; pages = pages->next)
1946 GtkNotebookPage *p = pages->data;
1948 if (!p->tab_label || !gtk_widget_get_visible (p->tab_label))
1953 /* No need to keep counting tabs after it */
1957 is_last = pages->next == NULL;
1965 if ((page_num) % 2 == 0)
1966 flags |= GTK_REGION_EVEN;
1968 flags |= GTK_REGION_ODD;
1971 flags |= GTK_REGION_FIRST;
1974 flags |= GTK_REGION_LAST;
1979 static GtkStateFlags
1980 notebook_tab_prepare_style_context (GtkNotebook *notebook,
1981 GtkNotebookPage *page,
1982 GtkStyleContext *context,
1985 gint tab_pos = get_effective_tab_pos (notebook);
1986 GtkRegionFlags flags = 0;
1987 GtkStateFlags state = gtk_style_context_get_state (context);
1990 page == notebook->priv->cur_page)
1991 state |= GTK_STATE_FLAG_ACTIVE;
1993 gtk_style_context_set_state (context, state);
1995 if (use_flags && (page != NULL))
1996 flags = _gtk_notebook_get_tab_flags (notebook, page);
1998 gtk_style_context_add_region (context, GTK_STYLE_REGION_TAB, flags);
2003 gtk_style_context_add_class (context, GTK_STYLE_CLASS_TOP);
2005 case GTK_POS_BOTTOM:
2006 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BOTTOM);
2009 gtk_style_context_add_class (context, GTK_STYLE_CLASS_LEFT);
2012 gtk_style_context_add_class (context, GTK_STYLE_CLASS_RIGHT);
2022 gtk_notebook_get_preferred_tabs_size (GtkNotebook *notebook,
2023 GtkRequisition *requisition)
2025 GtkNotebookPrivate *priv;
2028 gint tab_height = 0;
2032 gint action_width = 0;
2033 gint action_height = 0;
2034 guint vis_pages = 0;
2036 GtkNotebookPage *page;
2037 GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
2038 GtkRequisition child_requisition;
2039 GtkStyleContext *context;
2046 gint scroll_arrow_hlength;
2047 gint scroll_arrow_vlength;
2049 priv = notebook->priv;
2050 widget = GTK_WIDGET (notebook);
2051 context = gtk_widget_get_style_context (widget);
2052 gtk_widget_style_get (widget,
2053 "focus-line-width", &focus_width,
2054 "focus-padding", &focus_pad,
2055 "initial-gap", &initial_gap,
2056 "tab-overlap", &tab_overlap,
2057 "tab-curvature", &tab_curvature,
2058 "arrow-spacing", &arrow_spacing,
2059 "scroll-arrow-hlength", &scroll_arrow_hlength,
2060 "scroll-arrow-vlength", &scroll_arrow_vlength,
2063 for (children = priv->children; children;
2064 children = children->next)
2066 page = children->data;
2068 if (gtk_widget_get_visible (page->child))
2070 GtkBorder tab_padding;
2071 GtkStateFlags state;
2075 if (!gtk_widget_get_visible (page->tab_label))
2076 gtk_widget_show (page->tab_label);
2078 gtk_widget_get_preferred_size (page->tab_label,
2079 &child_requisition, NULL);
2081 /* Get border/padding for tab */
2082 gtk_style_context_save (context);
2083 state = notebook_tab_prepare_style_context (notebook, page, context, TRUE);
2084 gtk_style_context_get_padding (context, state, &tab_padding);
2085 gtk_style_context_restore (context);
2087 page->requisition.width = child_requisition.width +
2088 tab_padding.left + tab_padding.right + 2 * (focus_width + focus_pad);
2090 page->requisition.height = child_requisition.height +
2091 tab_padding.top + tab_padding.bottom + 2 * (focus_width + focus_pad);
2093 switch (priv->tab_pos)
2096 case GTK_POS_BOTTOM:
2097 tab_height = MAX (tab_height, page->requisition.height);
2098 tab_max = MAX (tab_max, page->requisition.width);
2102 tab_width = MAX (tab_width, page->requisition.width);
2103 tab_max = MAX (tab_max, page->requisition.height);
2107 else if (gtk_widget_get_visible (page->tab_label))
2108 gtk_widget_hide (page->tab_label);
2111 children = priv->children;
2115 for (i = 0; i < N_ACTION_WIDGETS; i++)
2117 if (priv->action_widget[i])
2119 gtk_widget_get_preferred_size (priv->action_widget[i],
2120 &action_widget_requisition[i], NULL);
2124 switch (priv->tab_pos)
2127 case GTK_POS_BOTTOM:
2128 if (tab_height == 0)
2131 if (priv->scrollable)
2132 tab_height = MAX (tab_height, scroll_arrow_hlength);
2134 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
2135 tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
2137 padding = 2 * tab_curvature - tab_overlap;
2141 page = children->data;
2142 children = children->next;
2144 if (!gtk_widget_get_visible (page->child))
2147 page->requisition.width += padding;
2149 tab_width += page->requisition.width;
2150 page->requisition.height = tab_height;
2153 if (priv->scrollable)
2154 tab_width = MIN (tab_width,
2155 tab_max + 2 * (scroll_arrow_hlength + arrow_spacing));
2157 action_width += action_widget_requisition[ACTION_WIDGET_START].width;
2158 action_width += action_widget_requisition[ACTION_WIDGET_END].width;
2159 requisition->width = tab_width + tab_overlap + action_width + initial_gap;
2161 requisition->height = tab_height;
2168 if (priv->scrollable)
2169 tab_width = MAX (tab_width, arrow_spacing + 2 * scroll_arrow_vlength);
2171 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2172 tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2174 padding = 2 * tab_curvature - tab_overlap;
2179 page = children->data;
2180 children = children->next;
2182 if (!gtk_widget_get_visible (page->child))
2185 page->requisition.width = tab_width;
2187 page->requisition.height += padding;
2189 tab_height += page->requisition.height;
2192 if (priv->scrollable)
2193 tab_height = MIN (tab_height,
2194 tab_max + (2 * scroll_arrow_vlength + arrow_spacing));
2195 action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2196 action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2198 requisition->height = tab_height + tab_overlap + action_height + initial_gap;
2200 requisition->height = MAX (requisition->height, tab_max + tab_overlap);
2202 requisition->width = tab_width;
2205 g_assert_not_reached ();
2206 requisition->width = 0;
2207 requisition->height = 0;
2212 requisition->width = 0;
2213 requisition->height = 0;
2218 get_preferred_size_for_size (GtkWidget *widget,
2219 GtkOrientation orientation,
2224 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2226 gtk_widget_get_preferred_width (widget, minimum, natural);
2228 gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural);
2231 gtk_widget_get_preferred_height (widget, minimum, natural);
2233 gtk_widget_get_preferred_height_for_width (widget, size, minimum, natural);
2237 get_padding_and_border (GtkNotebook *notebook,
2240 GtkStyleContext *context;
2242 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
2243 gtk_style_context_get_padding (context, 0, border);
2245 if (notebook->priv->show_border || notebook->priv->show_tabs)
2249 gtk_style_context_get_border (context, 0, &tmp);
2250 border->top += tmp.top;
2251 border->right += tmp.right;
2252 border->bottom += tmp.bottom;
2253 border->left += tmp.left;
2258 gtk_notebook_size_request (GtkWidget *widget,
2259 GtkOrientation orientation,
2264 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2265 GtkNotebookPrivate *priv = notebook->priv;
2266 GtkNotebookPage *page;
2268 gint child_minimum, child_natural;
2269 gboolean switch_page = FALSE;
2276 for (children = priv->children, vis_pages = 0; children;
2277 children = children->next)
2280 page = children->data;
2282 if (gtk_widget_get_visible (page->child))
2285 get_preferred_size_for_size (page->child,
2291 *minimum = MAX (*minimum, child_minimum);
2292 *natural = MAX (*natural, child_natural);
2294 if (priv->menu && page->menu_label)
2296 parent = gtk_widget_get_parent (page->menu_label);
2297 if (parent && !gtk_widget_get_visible (parent))
2298 gtk_widget_show (parent);
2303 if (page == priv->cur_page)
2306 if (priv->menu && page->menu_label)
2308 parent = gtk_widget_get_parent (page->menu_label);
2309 if (parent && gtk_widget_get_visible (parent))
2310 gtk_widget_hide (parent);
2315 if (priv->show_border || priv->show_tabs)
2317 GtkBorder notebook_padding;
2319 get_padding_and_border (notebook, ¬ebook_padding);
2321 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2323 *minimum += notebook_padding.left + notebook_padding.right;
2324 *natural += notebook_padding.left + notebook_padding.right;
2328 *minimum += notebook_padding.top + notebook_padding.bottom;
2329 *natural += notebook_padding.top + notebook_padding.bottom;
2332 if (priv->show_tabs)
2334 GtkRequisition tabs_requisition = { 0, 0 };
2336 gtk_notebook_get_preferred_tabs_size (notebook, &tabs_requisition);
2337 if (orientation == GTK_ORIENTATION_HORIZONTAL)
2339 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
2341 *minimum = MAX (*minimum, tabs_requisition.width);
2342 *natural = MAX (*minimum, *natural);
2346 *minimum += tabs_requisition.width;
2347 *natural += tabs_requisition.width;
2352 if (priv->tab_pos == GTK_POS_LEFT || priv->tab_pos == GTK_POS_RIGHT)
2354 *minimum = MAX (*minimum, tabs_requisition.height);
2355 *natural = MAX (*minimum, *natural);
2359 *minimum += tabs_requisition.height;
2360 *natural += tabs_requisition.height;
2366 for (children = priv->children; children;
2367 children = children->next)
2369 page = children->data;
2371 if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2372 gtk_widget_hide (page->tab_label);
2377 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2379 *minimum += border_width * 2;
2380 *natural += border_width * 2;
2386 for (children = priv->children; children;
2387 children = children->next)
2389 page = children->data;
2390 if (gtk_widget_get_visible (page->child))
2392 gtk_notebook_switch_page (notebook, page);
2397 else if (gtk_widget_get_visible (widget))
2399 *minimum = border_width * 2;
2402 if (vis_pages && !priv->cur_page)
2404 children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2407 priv->first_tab = children;
2408 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2414 gtk_notebook_get_preferred_width_for_height (GtkWidget *widget,
2419 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural);
2423 gtk_notebook_get_preferred_height_for_width (GtkWidget *widget,
2428 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural);
2432 gtk_notebook_get_preferred_width (GtkWidget *widget,
2436 gtk_notebook_size_request (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural);
2440 gtk_notebook_get_preferred_height (GtkWidget *widget,
2444 gtk_notebook_size_request (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural);
2448 gtk_notebook_size_allocate (GtkWidget *widget,
2449 GtkAllocation *allocation)
2451 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2452 GtkNotebookPrivate *priv = notebook->priv;
2453 gint tab_pos = get_effective_tab_pos (notebook);
2457 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2459 gtk_widget_set_allocation (widget, allocation);
2461 if (gtk_widget_get_realized (widget))
2463 GdkRectangle position;
2465 if (gtk_notebook_get_event_window_position (notebook, &position))
2467 gdk_window_move_resize (priv->event_window,
2468 position.x, position.y,
2469 position.width, position.height);
2470 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2471 gdk_window_show_unraised (priv->event_window);
2474 gdk_window_hide (priv->event_window);
2479 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2480 GtkNotebookPage *page;
2481 GtkAllocation child_allocation;
2485 child_allocation.x = allocation->x + border_width;
2486 child_allocation.y = allocation->y + border_width;
2487 child_allocation.width = MAX (1, allocation->width - border_width * 2);
2488 child_allocation.height = MAX (1, allocation->height - border_width * 2);
2490 if (priv->show_tabs || priv->show_border)
2494 get_padding_and_border (notebook, &padding);
2496 child_allocation.x += padding.left;
2497 child_allocation.y += padding.top;
2498 child_allocation.width = MAX (1, child_allocation.width - padding.left - padding.right);
2499 child_allocation.height = MAX (1, child_allocation.height - padding.top - padding.bottom);
2501 if (priv->show_tabs && priv->children && priv->cur_page)
2506 child_allocation.y += priv->cur_page->requisition.height;
2508 case GTK_POS_BOTTOM:
2509 child_allocation.height =
2510 MAX (1, child_allocation.height -
2511 priv->cur_page->requisition.height);
2514 child_allocation.x += priv->cur_page->requisition.width;
2517 child_allocation.width =
2518 MAX (1, child_allocation.width -
2519 priv->cur_page->requisition.width);
2523 for (i = 0; i < N_ACTION_WIDGETS; i++)
2525 GtkAllocation widget_allocation;
2526 GtkRequisition requisition;
2528 if (!priv->action_widget[i])
2531 widget_allocation.x = allocation->x + border_width;
2532 widget_allocation.y = allocation->y + border_width;
2533 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2535 gtk_widget_get_preferred_size (priv->action_widget[i],
2536 &requisition, NULL);
2540 case GTK_POS_BOTTOM:
2541 widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2544 widget_allocation.width = requisition.width;
2545 widget_allocation.height = priv->cur_page->requisition.height - padding.top;
2547 if ((i == ACTION_WIDGET_START && is_rtl) ||
2548 (i == ACTION_WIDGET_END && !is_rtl))
2549 widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2550 if (tab_pos == GTK_POS_TOP) /* no fall through */
2551 widget_allocation.y += 2 * focus_width;
2554 widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2557 widget_allocation.height = requisition.height;
2558 widget_allocation.width = priv->cur_page->requisition.width - padding.left;
2560 if (i == ACTION_WIDGET_END)
2561 widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2562 if (tab_pos == GTK_POS_LEFT) /* no fall through */
2563 widget_allocation.x += 2 * focus_width;
2567 gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2572 children = priv->children;
2575 page = children->data;
2576 children = children->next;
2578 if (gtk_widget_get_visible (page->child))
2579 gtk_widget_size_allocate (page->child, &child_allocation);
2582 gtk_notebook_pages_allocate (notebook);
2587 gtk_notebook_draw (GtkWidget *widget,
2590 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2591 GtkNotebookPrivate *priv = notebook->priv;
2592 GtkAllocation allocation;
2596 gtk_widget_get_allocation (widget, &allocation);
2598 window = gtk_widget_get_window (widget);
2599 if (gtk_cairo_should_draw_window (cr, window))
2603 cairo_translate (cr, -allocation.x, -allocation.y);
2604 gtk_notebook_paint (widget, cr);
2608 if (priv->show_tabs)
2610 GtkNotebookPage *page;
2613 for (pages = priv->children; pages; pages = pages->next)
2615 page = GTK_NOTEBOOK_PAGE (pages);
2617 if (gtk_widget_get_parent (page->tab_label) == widget)
2618 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2619 page->tab_label, cr);
2623 if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2624 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2625 priv->cur_page->child,
2627 if (priv->show_tabs)
2629 for (i = 0; i < N_ACTION_WIDGETS; i++)
2631 if (priv->action_widget[i])
2632 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2633 priv->action_widget[i], cr);
2638 if (priv->operation == DRAG_OPERATION_REORDER &&
2639 gtk_cairo_should_draw_window (cr, priv->drag_window))
2642 gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2644 gtk_notebook_draw_tab (notebook,
2650 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2651 priv->cur_page->tab_label, cr);
2658 gtk_notebook_show_arrows (GtkNotebook *notebook)
2660 GtkNotebookPrivate *priv = notebook->priv;
2661 gboolean show_arrow = FALSE;
2664 if (!priv->scrollable)
2667 children = priv->children;
2670 GtkNotebookPage *page = children->data;
2672 if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2675 children = children->next;
2682 gtk_notebook_get_arrow_rect (GtkNotebook *notebook,
2683 GdkRectangle *rectangle,
2684 GtkNotebookArrow arrow)
2686 GtkNotebookPrivate *priv = notebook->priv;
2687 GdkRectangle event_window_pos;
2688 gboolean before = ARROW_IS_BEFORE (arrow);
2689 gboolean left = ARROW_IS_LEFT (arrow);
2691 if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2693 gint scroll_arrow_hlength;
2694 gint scroll_arrow_vlength;
2696 gtk_widget_style_get (GTK_WIDGET (notebook),
2697 "scroll-arrow-hlength", &scroll_arrow_hlength,
2698 "scroll-arrow-vlength", &scroll_arrow_vlength,
2701 switch (priv->tab_pos)
2705 rectangle->width = scroll_arrow_vlength;
2706 rectangle->height = scroll_arrow_vlength;
2708 if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2709 (!before && (priv->has_after_previous != priv->has_after_next)))
2710 rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2712 rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2714 rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2715 rectangle->y = event_window_pos.y;
2717 rectangle->y += event_window_pos.height - rectangle->height;
2721 case GTK_POS_BOTTOM:
2722 rectangle->width = scroll_arrow_hlength;
2723 rectangle->height = scroll_arrow_hlength;
2727 if (left || !priv->has_before_previous)
2728 rectangle->x = event_window_pos.x;
2730 rectangle->x = event_window_pos.x + rectangle->width;
2734 if (!left || !priv->has_after_next)
2735 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2737 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2739 rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2745 static GtkNotebookArrow
2746 gtk_notebook_get_arrow (GtkNotebook *notebook,
2750 GtkNotebookPrivate *priv = notebook->priv;
2751 GdkRectangle arrow_rect;
2752 GdkRectangle event_window_pos;
2755 GtkNotebookArrow arrow[4];
2757 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2758 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2759 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2760 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2762 if (gtk_notebook_show_arrows (notebook))
2764 gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2765 for (i = 0; i < 4; i++)
2767 if (arrow[i] == ARROW_NONE)
2770 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2772 x0 = x - arrow_rect.x;
2773 y0 = y - arrow_rect.y;
2775 if (y0 >= 0 && y0 < arrow_rect.height &&
2776 x0 >= 0 && x0 < arrow_rect.width)
2785 gtk_notebook_do_arrow (GtkNotebook *notebook,
2786 GtkNotebookArrow arrow)
2788 GtkNotebookPrivate *priv = notebook->priv;
2789 GtkWidget *widget = GTK_WIDGET (notebook);
2790 gboolean is_rtl, left;
2792 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2793 left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2794 (!ARROW_IS_LEFT (arrow) && is_rtl);
2796 if (!priv->focus_tab ||
2797 gtk_notebook_search_page (notebook, priv->focus_tab,
2798 left ? STEP_PREV : STEP_NEXT,
2801 gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2802 gtk_widget_grab_focus (widget);
2807 gtk_notebook_arrow_button_press (GtkNotebook *notebook,
2808 GtkNotebookArrow arrow,
2811 GtkNotebookPrivate *priv = notebook->priv;
2812 GtkWidget *widget = GTK_WIDGET (notebook);
2813 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2814 gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) ||
2815 (!ARROW_IS_LEFT (arrow) && is_rtl);
2817 if (!gtk_widget_has_focus (widget))
2818 gtk_widget_grab_focus (widget);
2820 priv->button = button;
2821 priv->click_child = arrow;
2823 if (button == GDK_BUTTON_PRIMARY)
2825 gtk_notebook_do_arrow (notebook, arrow);
2826 gtk_notebook_set_scroll_timer (notebook);
2828 else if (button == GDK_BUTTON_MIDDLE)
2829 gtk_notebook_page_select (notebook, TRUE);
2830 else if (button == GDK_BUTTON_SECONDARY)
2831 gtk_notebook_switch_focus_tab (notebook,
2832 gtk_notebook_search_page (notebook,
2834 left ? STEP_NEXT : STEP_PREV,
2836 gtk_notebook_redraw_arrows (notebook);
2842 get_widget_coordinates (GtkWidget *widget,
2847 GdkWindow *window = ((GdkEventAny *)event)->window;
2850 if (!gdk_event_get_coords (event, &tx, &ty))
2853 while (window && window != gtk_widget_get_window (widget))
2855 gint window_x, window_y;
2857 gdk_window_get_position (window, &window_x, &window_y);
2861 window = gdk_window_get_parent (window);
2876 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2878 GtkNotebookPrivate *priv = notebook->priv;
2879 GtkNotebookPage *page;
2882 children = priv->children;
2885 page = children->data;
2887 if (gtk_widget_get_visible (page->child) &&
2888 page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2889 (x >= page->allocation.x) &&
2890 (y >= page->allocation.y) &&
2891 (x <= (page->allocation.x + page->allocation.width)) &&
2892 (y <= (page->allocation.y + page->allocation.height)))
2895 children = children->next;
2902 gtk_notebook_button_press (GtkWidget *widget,
2903 GdkEventButton *event)
2905 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2906 GtkNotebookPrivate *priv = notebook->priv;
2907 GtkNotebookPage *page;
2909 GtkNotebookArrow arrow;
2912 if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2916 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2919 arrow = gtk_notebook_get_arrow (notebook, x, y);
2921 return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2923 if (priv->menu && gdk_event_triggers_context_menu ((GdkEvent *) event))
2925 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2926 NULL, NULL, 3, event->time);
2930 if (event->button != GDK_BUTTON_PRIMARY)
2933 priv->button = event->button;
2935 if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2937 gboolean page_changed, was_focus;
2940 page_changed = page != priv->cur_page;
2941 was_focus = gtk_widget_is_focus (widget);
2943 gtk_notebook_switch_focus_tab (notebook, tab);
2944 gtk_widget_grab_focus (widget);
2946 if (page_changed && !was_focus)
2947 gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2949 /* save press to possibly begin a drag */
2950 if (page->reorderable || page->detachable)
2952 priv->during_detach = FALSE;
2953 priv->during_reorder = FALSE;
2954 priv->pressed_button = event->button;
2959 priv->drag_begin_x = priv->mouse_x;
2960 priv->drag_begin_y = priv->mouse_y;
2961 priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2962 priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2970 popup_position_func (GtkMenu *menu,
2976 GtkNotebook *notebook = data;
2977 GtkNotebookPrivate *priv = notebook->priv;
2978 GtkAllocation allocation;
2980 GtkRequisition requisition;
2982 if (priv->focus_tab)
2984 GtkNotebookPage *page;
2986 page = priv->focus_tab->data;
2987 w = page->tab_label;
2991 w = GTK_WIDGET (notebook);
2994 gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2996 gtk_widget_get_allocation (w, &allocation);
2997 gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2998 &requisition, NULL);
3000 if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
3001 *x += allocation.x + allocation.width - requisition.width;
3005 *y += allocation.y + allocation.height;
3011 gtk_notebook_popup_menu (GtkWidget *widget)
3013 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3014 GtkNotebookPrivate *priv = notebook->priv;
3018 gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
3019 popup_position_func, notebook,
3020 0, gtk_get_current_event_time ());
3021 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
3029 stop_scrolling (GtkNotebook *notebook)
3031 GtkNotebookPrivate *priv = notebook->priv;
3035 g_source_remove (priv->timer);
3037 priv->need_timer = FALSE;
3039 priv->click_child = 0;
3041 gtk_notebook_redraw_arrows (notebook);
3045 get_drop_position (GtkNotebook *notebook)
3047 GtkNotebookPrivate *priv = notebook->priv;
3048 GList *children, *last_child;
3049 GtkNotebookPage *page;
3056 is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
3057 children = priv->children;
3062 page = children->data;
3064 if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
3065 gtk_widget_get_visible (page->child) &&
3067 gtk_widget_get_mapped (page->tab_label))
3069 switch (priv->tab_pos)
3072 case GTK_POS_BOTTOM:
3075 if (PAGE_MIDDLE_X (page) > x)
3080 if (PAGE_MIDDLE_X (page) < x)
3087 if (PAGE_MIDDLE_Y (page) > y)
3093 last_child = children->next;
3096 children = children->next;
3103 show_drag_window (GtkNotebook *notebook,
3104 GtkNotebookPrivate *priv,
3105 GtkNotebookPage *page,
3108 GtkWidget *widget = GTK_WIDGET (notebook);
3110 if (!priv->drag_window)
3112 GdkWindowAttr attributes;
3113 guint attributes_mask;
3114 GdkRGBA transparent = {0, 0, 0, 0};
3116 attributes.x = page->allocation.x;
3117 attributes.y = page->allocation.y;
3118 attributes.width = page->allocation.width;
3119 attributes.height = page->allocation.height;
3120 attributes.window_type = GDK_WINDOW_CHILD;
3121 attributes.wclass = GDK_INPUT_OUTPUT;
3122 attributes.visual = gtk_widget_get_visual (widget);
3123 attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
3124 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
3126 priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
3129 gtk_widget_register_window (widget, priv->drag_window);
3130 gdk_window_set_background_rgba (priv->drag_window, &transparent);
3133 g_object_ref (page->tab_label);
3134 gtk_widget_unparent (page->tab_label);
3135 gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
3136 gtk_widget_set_parent (page->tab_label, widget);
3137 g_object_unref (page->tab_label);
3139 gdk_window_show (priv->drag_window);
3141 /* the grab will dissapear when the window is hidden */
3142 gdk_device_grab (device, priv->drag_window,
3143 GDK_OWNERSHIP_WINDOW, FALSE,
3144 GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
3145 NULL, GDK_CURRENT_TIME);
3148 /* This function undoes the reparenting that happens both when drag_window
3149 * is shown for reordering and when the DnD icon is shown for detaching
3152 hide_drag_window (GtkNotebook *notebook,
3153 GtkNotebookPrivate *priv,
3154 GtkNotebookPage *page)
3156 GtkWidget *widget = GTK_WIDGET (notebook);
3157 GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
3159 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
3160 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
3162 g_object_ref (page->tab_label);
3164 if (GTK_IS_WINDOW (parent)) /* parent widget is the drag window */
3165 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
3167 gtk_widget_unparent (page->tab_label);
3169 gtk_widget_set_parent (page->tab_label, widget);
3170 g_object_unref (page->tab_label);
3173 if (priv->drag_window &&
3174 gdk_window_is_visible (priv->drag_window))
3175 gdk_window_hide (priv->drag_window);
3179 gtk_notebook_stop_reorder (GtkNotebook *notebook)
3181 GtkNotebookPrivate *priv = notebook->priv;
3182 GtkNotebookPage *page;
3184 if (priv->operation == DRAG_OPERATION_DETACH)
3185 page = priv->detached_tab;
3187 page = priv->cur_page;
3189 if (!page || !page->tab_label)
3192 priv->pressed_button = -1;
3194 if (page->reorderable || page->detachable)
3196 if (priv->during_reorder)
3198 gint old_page_num, page_num, i;
3201 element = get_drop_position (notebook);
3202 old_page_num = g_list_position (priv->children, priv->focus_tab);
3203 page_num = reorder_tab (notebook, element, priv->focus_tab);
3204 gtk_notebook_child_reordered (notebook, page);
3206 if (priv->has_scrolled || old_page_num != page_num)
3208 for (element = priv->children, i = 0; element; element = element->next, i++)
3210 if (MIN (old_page_num, page_num) <= i && i <= MAX (old_page_num, page_num))
3211 gtk_widget_child_notify (((GtkNotebookPage *) element->data)->child, "position");
3213 g_signal_emit (notebook,
3214 notebook_signals[PAGE_REORDERED], 0,
3215 page->child, page_num);
3218 priv->has_scrolled = FALSE;
3219 priv->during_reorder = FALSE;
3222 hide_drag_window (notebook, priv, page);
3224 priv->operation = DRAG_OPERATION_NONE;
3225 gtk_notebook_pages_allocate (notebook);
3227 if (priv->dnd_timer)
3229 g_source_remove (priv->dnd_timer);
3230 priv->dnd_timer = 0;
3236 gtk_notebook_button_release (GtkWidget *widget,
3237 GdkEventButton *event)
3239 GtkNotebook *notebook;
3240 GtkNotebookPrivate *priv;
3241 GtkNotebookPage *page;
3243 if (event->type != GDK_BUTTON_RELEASE)
3246 notebook = GTK_NOTEBOOK (widget);
3247 priv = notebook->priv;
3249 page = priv->cur_page;
3251 if (!priv->during_detach &&
3252 page->reorderable &&
3253 event->button == priv->pressed_button)
3254 gtk_notebook_stop_reorder (notebook);
3256 if (event->button == priv->button)
3258 stop_scrolling (notebook);
3266 gtk_notebook_leave_notify (GtkWidget *widget,
3267 GdkEventCrossing *event)
3269 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3270 GtkNotebookPrivate *priv = notebook->priv;
3273 if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
3279 gtk_notebook_redraw_arrows (notebook);
3285 static GtkNotebookPointerPosition
3286 get_pointer_position (GtkNotebook *notebook)
3288 GtkNotebookPrivate *priv = notebook->priv;
3289 GtkWidget *widget = GTK_WIDGET (notebook);
3290 gint wx, wy, width, height;
3293 if (!priv->scrollable)
3294 return POINTER_BETWEEN;
3296 gdk_window_get_position (priv->event_window, &wx, &wy);
3297 width = gdk_window_get_width (priv->event_window);
3298 height = gdk_window_get_height (priv->event_window);
3300 if (priv->tab_pos == GTK_POS_TOP ||
3301 priv->tab_pos == GTK_POS_BOTTOM)
3305 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3306 x = priv->mouse_x - wx;
3308 if (x > width - SCROLL_THRESHOLD)
3309 return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
3310 else if (x < SCROLL_THRESHOLD)
3311 return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
3313 return POINTER_BETWEEN;
3319 y = priv->mouse_y - wy;
3320 if (y > height - SCROLL_THRESHOLD)
3321 return POINTER_AFTER;
3322 else if (y < SCROLL_THRESHOLD)
3323 return POINTER_BEFORE;
3325 return POINTER_BETWEEN;
3330 scroll_notebook_timer (gpointer data)
3332 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3333 GtkNotebookPrivate *priv = notebook->priv;
3334 GtkNotebookPointerPosition pointer_position;
3335 GList *element, *first_tab;
3337 pointer_position = get_pointer_position (notebook);
3339 element = get_drop_position (notebook);
3340 reorder_tab (notebook, element, priv->focus_tab);
3341 first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3342 (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3346 priv->first_tab = first_tab;
3347 gtk_notebook_pages_allocate (notebook);
3349 gdk_window_move_resize (priv->drag_window,
3350 priv->drag_window_x,
3351 priv->drag_window_y,
3352 priv->cur_page->allocation.width,
3353 priv->cur_page->allocation.height);
3354 gdk_window_raise (priv->drag_window);
3361 check_threshold (GtkNotebook *notebook,
3365 GtkNotebookPrivate *priv = notebook->priv;
3367 GdkRectangle rectangle = { 0, }; /* shut up gcc */
3368 GtkSettings *settings;
3370 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3371 g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3373 /* we want a large threshold */
3374 dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3376 gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3377 rectangle.width = gdk_window_get_width (priv->event_window);
3378 rectangle.height = gdk_window_get_height (priv->event_window);
3380 rectangle.x -= dnd_threshold;
3381 rectangle.width += 2 * dnd_threshold;
3382 rectangle.y -= dnd_threshold;
3383 rectangle.height += 2 * dnd_threshold;
3385 return (current_x < rectangle.x ||
3386 current_x > rectangle.x + rectangle.width ||
3387 current_y < rectangle.y ||
3388 current_y > rectangle.y + rectangle.height);
3392 gtk_notebook_motion_notify (GtkWidget *widget,
3393 GdkEventMotion *event)
3395 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3396 GtkNotebookPrivate *priv = notebook->priv;
3397 GtkNotebookPage *page;
3398 GtkNotebookArrow arrow;
3399 GtkNotebookPointerPosition pointer_position;
3400 GtkSettings *settings;
3404 page = priv->cur_page;
3409 if (!(event->state & GDK_BUTTON1_MASK) &&
3410 priv->pressed_button != -1)
3412 gtk_notebook_stop_reorder (notebook);
3413 stop_scrolling (notebook);
3416 if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3419 priv->timestamp = event->time;
3421 /* While animating the move, event->x is relative to the flying tab
3422 * (priv->drag_window has a pointer grab), but we need coordinates relative to
3423 * the notebook widget.
3425 gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3426 priv->mouse_x = event->x_root - x_win;
3427 priv->mouse_y = event->y_root - y_win;
3429 arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3430 if (arrow != priv->in_child)
3432 priv->in_child = arrow;
3433 gtk_notebook_redraw_arrows (notebook);
3436 if (priv->pressed_button == -1)
3439 if (page->detachable &&
3440 check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3442 priv->detached_tab = priv->cur_page;
3443 priv->during_detach = TRUE;
3445 gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3446 priv->pressed_button, (GdkEvent*) event);
3450 if (page->reorderable &&
3451 (priv->during_reorder ||
3452 gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3454 priv->during_reorder = TRUE;
3455 pointer_position = get_pointer_position (notebook);
3457 if (event->window == priv->drag_window &&
3458 pointer_position != POINTER_BETWEEN &&
3459 gtk_notebook_show_arrows (notebook))
3462 if (!priv->dnd_timer)
3464 priv->has_scrolled = TRUE;
3465 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3466 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3468 priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3469 scroll_notebook_timer,
3470 (gpointer) notebook);
3475 if (priv->dnd_timer)
3477 g_source_remove (priv->dnd_timer);
3478 priv->dnd_timer = 0;
3482 if (event->window == priv->drag_window ||
3483 priv->operation != DRAG_OPERATION_REORDER)
3485 /* the drag operation is beginning, create the window */
3486 if (priv->operation != DRAG_OPERATION_REORDER)
3488 priv->operation = DRAG_OPERATION_REORDER;
3489 show_drag_window (notebook, priv, page, event->device);
3492 gtk_notebook_pages_allocate (notebook);
3493 gdk_window_move_resize (priv->drag_window,
3494 priv->drag_window_x,
3495 priv->drag_window_y,
3496 page->allocation.width,
3497 page->allocation.height);
3499 gtk_notebook_redraw_tabs_junction (notebook);
3507 gtk_notebook_grab_notify (GtkWidget *widget,
3508 gboolean was_grabbed)
3510 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3514 gtk_notebook_stop_reorder (notebook);
3515 stop_scrolling (notebook);
3520 gtk_notebook_state_flags_changed (GtkWidget *widget,
3521 GtkStateFlags previous_state)
3523 if (!gtk_widget_is_sensitive (widget))
3524 stop_scrolling (GTK_NOTEBOOK (widget));
3528 gtk_notebook_focus_in (GtkWidget *widget,
3529 GdkEventFocus *event)
3531 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3537 gtk_notebook_focus_out (GtkWidget *widget,
3538 GdkEventFocus *event)
3540 gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3546 gtk_notebook_style_updated (GtkWidget *widget)
3548 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3549 GtkNotebookPrivate *priv = notebook->priv;
3551 gboolean has_before_previous;
3552 gboolean has_before_next;
3553 gboolean has_after_previous;
3554 gboolean has_after_next;
3556 gtk_widget_style_get (widget,
3557 "has-backward-stepper", &has_before_previous,
3558 "has-secondary-forward-stepper", &has_before_next,
3559 "has-secondary-backward-stepper", &has_after_previous,
3560 "has-forward-stepper", &has_after_next,
3563 priv->has_before_previous = has_before_previous;
3564 priv->has_before_next = has_before_next;
3565 priv->has_after_previous = has_after_previous;
3566 priv->has_after_next = has_after_next;
3568 GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_updated (widget);
3572 on_drag_icon_draw (GtkWidget *widget,
3576 GtkWidget *notebook, *child;
3577 GtkRequisition requisition;
3578 GtkStyleContext *context;
3581 notebook = GTK_WIDGET (data);
3582 child = gtk_bin_get_child (GTK_BIN (widget));
3583 context = gtk_widget_get_style_context (widget);
3585 gtk_style_context_save (context);
3586 notebook_tab_prepare_style_context (GTK_NOTEBOOK (notebook), NULL, context, FALSE);
3588 gtk_widget_get_preferred_size (widget,
3589 &requisition, NULL);
3590 gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3592 gtk_render_extension (context, cr, 0, 0,
3593 requisition.width, requisition.height,
3597 gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3599 gtk_style_context_restore (context);
3605 gtk_notebook_drag_begin (GtkWidget *widget,
3606 GdkDragContext *context)
3608 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3609 GtkNotebookPrivate *priv = notebook->priv;
3610 GtkWidget *tab_label;
3612 if (priv->dnd_timer)
3614 g_source_remove (priv->dnd_timer);
3615 priv->dnd_timer = 0;
3618 priv->operation = DRAG_OPERATION_DETACH;
3619 gtk_notebook_pages_allocate (notebook);
3621 tab_label = priv->detached_tab->tab_label;
3623 hide_drag_window (notebook, priv, priv->cur_page);
3624 g_object_ref (tab_label);
3625 gtk_widget_unparent (tab_label);
3627 priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3628 gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3629 gtk_widget_get_screen (widget));
3630 gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3631 gtk_widget_set_size_request (priv->dnd_window,
3632 priv->detached_tab->allocation.width,
3633 priv->detached_tab->allocation.height);
3634 g_object_unref (tab_label);
3636 g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3637 G_CALLBACK (on_drag_icon_draw), notebook);
3639 gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3643 gtk_notebook_drag_end (GtkWidget *widget,
3644 GdkDragContext *context)
3646 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3647 GtkNotebookPrivate *priv = notebook->priv;
3649 gtk_notebook_stop_reorder (notebook);
3651 if (priv->detached_tab)
3652 gtk_notebook_switch_page (notebook, priv->detached_tab);
3654 _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3655 gtk_widget_destroy (priv->dnd_window);
3656 priv->dnd_window = NULL;
3658 priv->operation = DRAG_OPERATION_NONE;
3661 static GtkNotebook *
3662 gtk_notebook_create_window (GtkNotebook *notebook,
3671 gtk_notebook_drag_failed (GtkWidget *widget,
3672 GdkDragContext *context,
3673 GtkDragResult result)
3675 if (result == GTK_DRAG_RESULT_NO_TARGET)
3677 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3678 GtkNotebookPrivate *priv = notebook->priv;
3679 GtkNotebook *dest_notebook = NULL;
3682 gdk_device_get_position (gdk_drag_context_get_device (context),
3685 g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3686 priv->detached_tab->child, x, y, &dest_notebook);
3689 do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3698 gtk_notebook_switch_tab_timeout (gpointer data)
3700 GtkNotebook *notebook = GTK_NOTEBOOK (data);
3701 GtkNotebookPrivate *priv = notebook->priv;
3704 priv->switch_tab_timer = 0;
3706 switch_tab = priv->switch_tab;
3707 priv->switch_tab = NULL;
3711 /* FIXME: hack, we don't want the
3712 * focus to move fom the source widget
3714 priv->child_has_focus = FALSE;
3715 gtk_notebook_switch_focus_tab (notebook, switch_tab);
3722 gtk_notebook_drag_motion (GtkWidget *widget,
3723 GdkDragContext *context,
3728 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3729 GtkNotebookPrivate *priv = notebook->priv;
3730 GtkAllocation allocation;
3731 GdkRectangle position;
3732 GtkSettings *settings;
3733 GtkNotebookArrow arrow;
3735 GdkAtom target, tab_target;
3737 gboolean retval = FALSE;
3739 gtk_widget_get_allocation (widget, &allocation);
3741 arrow = gtk_notebook_get_arrow (notebook,
3746 priv->click_child = arrow;
3747 gtk_notebook_set_scroll_timer (notebook);
3748 gdk_drag_status (context, 0, time);
3754 stop_scrolling (notebook);
3755 target = gtk_drag_dest_find_target (widget, context, NULL);
3756 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3758 if (target == tab_target)
3760 GQuark group, source_group;
3761 GtkNotebook *source;
3762 GtkWidget *source_child;
3766 source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3767 source_child = source->priv->cur_page->child;
3769 group = notebook->priv->group;
3770 source_group = source->priv->group;
3772 if (group != 0 && group == source_group &&
3773 !(widget == source_child ||
3774 gtk_widget_is_ancestor (widget, source_child)))
3776 gdk_drag_status (context, GDK_ACTION_MOVE, time);
3781 /* it's a tab, but doesn't share
3782 * ID with this notebook */
3783 gdk_drag_status (context, 0, time);
3790 if (gtk_notebook_get_event_window_position (notebook, &position) &&
3791 x >= position.x && x <= position.x + position.width &&
3792 y >= position.y && y <= position.y + position.height &&
3793 (tab = get_tab_at_pos (notebook, x, y)))
3800 if (tab != priv->switch_tab)
3801 remove_switch_tab_timer (notebook);
3803 priv->switch_tab = tab;
3805 if (!priv->switch_tab_timer)
3807 settings = gtk_widget_get_settings (widget);
3809 g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3810 priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3811 gtk_notebook_switch_tab_timeout,
3817 remove_switch_tab_timer (notebook);
3825 gtk_notebook_drag_leave (GtkWidget *widget,
3826 GdkDragContext *context,
3829 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3831 remove_switch_tab_timer (notebook);
3832 stop_scrolling (notebook);
3836 gtk_notebook_drag_drop (GtkWidget *widget,
3837 GdkDragContext *context,
3842 GdkAtom target, tab_target;
3844 target = gtk_drag_dest_find_target (widget, context, NULL);
3845 tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3847 if (target == tab_target)
3849 gtk_drag_get_data (widget, context, target, time);
3857 do_detach_tab (GtkNotebook *from,
3863 GtkNotebookPrivate *to_priv = to->priv;
3864 GtkAllocation to_allocation;
3865 GtkWidget *tab_label, *menu_label;
3866 gboolean tab_expand, tab_fill, reorderable, detachable;
3870 menu_label = gtk_notebook_get_menu_label (from, child);
3873 g_object_ref (menu_label);
3875 tab_label = gtk_notebook_get_tab_label (from, child);
3878 g_object_ref (tab_label);
3880 g_object_ref (child);
3882 gtk_container_child_get (GTK_CONTAINER (from),
3884 "tab-expand", &tab_expand,
3885 "tab-fill", &tab_fill,
3886 "reorderable", &reorderable,
3887 "detachable", &detachable,
3890 gtk_container_remove (GTK_CONTAINER (from), child);
3892 gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3893 to_priv->mouse_x = x + to_allocation.x;
3894 to_priv->mouse_y = y + to_allocation.y;
3896 element = get_drop_position (to);
3897 page_num = g_list_position (to_priv->children, element);
3898 gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3900 gtk_container_child_set (GTK_CONTAINER (to), child,
3901 "tab-expand", tab_expand,
3902 "tab-fill", tab_fill,
3903 "reorderable", reorderable,
3904 "detachable", detachable,
3907 g_object_unref (child);
3910 g_object_unref (tab_label);
3913 g_object_unref (menu_label);
3915 gtk_notebook_set_current_page (to, page_num);
3919 gtk_notebook_drag_data_get (GtkWidget *widget,
3920 GdkDragContext *context,
3921 GtkSelectionData *data,
3927 target = gtk_selection_data_get_target (data);
3928 if (target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3930 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3931 GtkNotebookPrivate *priv = notebook->priv;
3933 gtk_selection_data_set (data,
3936 (void*) &priv->detached_tab->child,
3942 gtk_notebook_drag_data_received (GtkWidget *widget,
3943 GdkDragContext *context,
3946 GtkSelectionData *data,
3950 GtkNotebook *notebook;
3951 GtkWidget *source_widget;
3954 notebook = GTK_NOTEBOOK (widget);
3955 source_widget = gtk_drag_get_source_widget (context);
3957 if (source_widget &&
3958 gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3960 child = (void*) gtk_selection_data_get_data (data);
3962 do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3963 gtk_drag_finish (context, TRUE, FALSE, time);
3966 gtk_drag_finish (context, FALSE, FALSE, time);
3969 /* Private GtkContainer Methods :
3971 * gtk_notebook_set_child_arg
3972 * gtk_notebook_get_child_arg
3974 * gtk_notebook_remove
3975 * gtk_notebook_focus
3976 * gtk_notebook_set_focus_child
3977 * gtk_notebook_child_type
3978 * gtk_notebook_forall
3981 gtk_notebook_set_child_property (GtkContainer *container,
3984 const GValue *value,
3990 /* not finding child's page is valid for menus or labels */
3991 if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3994 switch (property_id)
3996 case CHILD_PROP_TAB_LABEL:
3997 /* a NULL pointer indicates a default_tab setting, otherwise
3998 * we need to set the associated label
4000 gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
4001 g_value_get_string (value));
4003 case CHILD_PROP_MENU_LABEL:
4004 gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
4005 g_value_get_string (value));
4007 case CHILD_PROP_POSITION:
4008 gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
4009 g_value_get_int (value));
4011 case CHILD_PROP_TAB_EXPAND:
4012 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4014 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
4015 g_value_get_boolean (value),
4018 case CHILD_PROP_TAB_FILL:
4019 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4021 gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
4023 g_value_get_boolean (value));
4025 case CHILD_PROP_REORDERABLE:
4026 gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
4027 g_value_get_boolean (value));
4029 case CHILD_PROP_DETACHABLE:
4030 gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
4031 g_value_get_boolean (value));
4034 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
4040 gtk_notebook_get_child_property (GtkContainer *container,
4046 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4047 GtkNotebookPrivate *priv = notebook->priv;
4053 /* not finding child's page is valid for menus or labels */
4054 list = gtk_notebook_find_child (notebook, child, NULL);
4057 /* nothing to set on labels or menus */
4058 g_param_value_set_default (pspec, value);
4062 switch (property_id)
4064 case CHILD_PROP_TAB_LABEL:
4065 label = gtk_notebook_get_tab_label (notebook, child);
4067 if (GTK_IS_LABEL (label))
4068 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4070 g_value_set_string (value, NULL);
4072 case CHILD_PROP_MENU_LABEL:
4073 label = gtk_notebook_get_menu_label (notebook, child);
4075 if (GTK_IS_LABEL (label))
4076 g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
4078 g_value_set_string (value, NULL);
4080 case CHILD_PROP_POSITION:
4081 g_value_set_int (value, g_list_position (priv->children, list));
4083 case CHILD_PROP_TAB_EXPAND:
4084 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4086 g_value_set_boolean (value, expand);
4088 case CHILD_PROP_TAB_FILL:
4089 gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
4091 g_value_set_boolean (value, fill);
4093 case CHILD_PROP_REORDERABLE:
4094 g_value_set_boolean (value,
4095 gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
4097 case CHILD_PROP_DETACHABLE:
4098 g_value_set_boolean (value,
4099 gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
4102 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
4108 gtk_notebook_add (GtkContainer *container,
4111 gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget,
4116 gtk_notebook_remove (GtkContainer *container,
4119 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4120 GtkNotebookPrivate *priv = notebook->priv;
4121 GtkNotebookPage *page;
4122 GList *children, *list;
4125 children = priv->children;
4128 page = children->data;
4130 if (page->child == widget)
4134 children = children->next;
4137 if (children == NULL)
4140 g_object_ref (widget);
4142 list = children->next;
4143 gtk_notebook_real_remove (notebook, children);
4147 gtk_widget_child_notify (((GtkNotebookPage *)list->data)->child, "position");
4151 g_signal_emit (notebook,
4152 notebook_signals[PAGE_REMOVED],
4157 g_object_unref (widget);
4161 focus_tabs_in (GtkNotebook *notebook)
4163 GtkNotebookPrivate *priv = notebook->priv;
4165 if (priv->show_tabs && priv->cur_page)
4167 gtk_widget_grab_focus (GTK_WIDGET (notebook));
4168 gtk_notebook_set_focus_child (GTK_CONTAINER (notebook), NULL);
4169 gtk_notebook_switch_focus_tab (notebook,
4170 g_list_find (priv->children,
4180 focus_tabs_move (GtkNotebook *notebook,
4181 GtkDirectionType direction,
4182 gint search_direction)
4184 GtkNotebookPrivate *priv = notebook->priv;
4187 new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
4188 search_direction, TRUE);
4191 gboolean wrap_around;
4193 g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
4194 "gtk-keynav-wrap-around", &wrap_around,
4198 new_page = gtk_notebook_search_page (notebook, NULL,
4199 search_direction, TRUE);
4203 gtk_notebook_switch_focus_tab (notebook, new_page);
4205 gtk_widget_error_bell (GTK_WIDGET (notebook));
4211 focus_child_in (GtkNotebook *notebook,
4212 GtkDirectionType direction)
4214 GtkNotebookPrivate *priv = notebook->priv;
4217 return gtk_widget_child_focus (priv->cur_page->child, direction);
4223 focus_action_in (GtkNotebook *notebook,
4225 GtkDirectionType direction)
4227 GtkNotebookPrivate *priv = notebook->priv;
4229 if (priv->action_widget[action] &&
4230 gtk_widget_get_visible (priv->action_widget[action]))
4231 return gtk_widget_child_focus (priv->action_widget[action], direction);
4236 /* Focus in the notebook can either be on the pages, or on
4237 * the tabs or on the action_widgets.
4240 gtk_notebook_focus (GtkWidget *widget,
4241 GtkDirectionType direction)
4243 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
4244 GtkNotebookPrivate *priv = notebook->priv;
4245 GtkWidget *old_focus_child;
4246 GtkDirectionType effective_direction;
4250 gboolean widget_is_focus;
4251 GtkContainer *container;
4253 container = GTK_CONTAINER (widget);
4255 if (priv->tab_pos == GTK_POS_TOP ||
4256 priv->tab_pos == GTK_POS_LEFT)
4258 first_action = ACTION_WIDGET_START;
4259 last_action = ACTION_WIDGET_END;
4263 first_action = ACTION_WIDGET_END;
4264 last_action = ACTION_WIDGET_START;
4267 if (priv->focus_out)
4269 priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
4273 widget_is_focus = gtk_widget_is_focus (widget);
4274 old_focus_child = gtk_container_get_focus_child (container);
4276 effective_direction = get_effective_direction (notebook, direction);
4278 if (old_focus_child) /* Focus on page child or action widget */
4280 if (gtk_widget_child_focus (old_focus_child, direction))
4283 if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
4285 switch (effective_direction)
4288 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4290 return focus_tabs_in (notebook);
4298 case GTK_DIR_TAB_FORWARD:
4299 if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
4300 focus_child_in (notebook, direction))
4302 return focus_tabs_in (notebook);
4303 case GTK_DIR_TAB_BACKWARD:
4306 g_assert_not_reached ();
4310 else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
4312 switch (effective_direction)
4315 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4319 return focus_tabs_in (notebook);
4325 case GTK_DIR_TAB_FORWARD:
4327 case GTK_DIR_TAB_BACKWARD:
4328 if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
4329 focus_child_in (notebook, direction))
4331 return focus_tabs_in (notebook);
4333 g_assert_not_reached ();
4339 switch (effective_direction)
4341 case GTK_DIR_TAB_BACKWARD:
4343 /* Focus onto the tabs */
4344 return focus_tabs_in (notebook);
4349 case GTK_DIR_TAB_FORWARD:
4350 return focus_action_in (notebook, last_action, direction);
4354 else if (widget_is_focus) /* Focus was on tabs */
4356 switch (effective_direction)
4358 case GTK_DIR_TAB_BACKWARD:
4359 return focus_action_in (notebook, first_action, direction);
4362 case GTK_DIR_TAB_FORWARD:
4363 if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4365 return focus_action_in (notebook, last_action, direction);
4367 /* We use TAB_FORWARD rather than direction so that we focus a more
4368 * predictable widget for the user; users may be using arrow focusing
4369 * in this situation even if they don't usually use arrow focusing.
4371 return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4373 return focus_tabs_move (notebook, direction, STEP_PREV);
4375 return focus_tabs_move (notebook, direction, STEP_NEXT);
4378 else /* Focus was not on widget */
4380 switch (effective_direction)
4382 case GTK_DIR_TAB_FORWARD:
4384 if (focus_action_in (notebook, first_action, direction))
4386 if (focus_tabs_in (notebook))
4388 if (focus_action_in (notebook, last_action, direction))
4390 if (focus_child_in (notebook, direction))
4393 case GTK_DIR_TAB_BACKWARD:
4394 if (focus_action_in (notebook, last_action, direction))
4396 if (focus_child_in (notebook, direction))
4398 if (focus_tabs_in (notebook))
4400 if (focus_action_in (notebook, first_action, direction))
4405 return focus_child_in (notebook, direction);
4409 g_assert_not_reached ();
4414 gtk_notebook_set_focus_child (GtkContainer *container,
4417 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4418 GtkNotebookPrivate *priv = notebook->priv;
4419 GtkWidget *page_child;
4420 GtkWidget *toplevel;
4422 /* If the old focus widget was within a page of the notebook,
4423 * (child may either be NULL or not in this case), record it
4424 * for future use if we switch to the page with a mnemonic.
4427 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4428 if (toplevel && gtk_widget_is_toplevel (toplevel))
4430 page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4433 if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4435 GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4438 GtkNotebookPage *page = list->data;
4440 if (page->last_focus_child)
4441 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4443 page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4444 g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4450 page_child = gtk_widget_get_parent (page_child);
4456 g_return_if_fail (GTK_IS_WIDGET (child));
4458 priv->child_has_focus = TRUE;
4459 if (!priv->focus_tab)
4462 GtkNotebookPage *page;
4464 children = priv->children;
4467 page = children->data;
4468 if (page->child == child || page->tab_label == child)
4469 gtk_notebook_switch_focus_tab (notebook, children);
4470 children = children->next;
4475 priv->child_has_focus = FALSE;
4477 GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4481 gtk_notebook_forall (GtkContainer *container,
4482 gboolean include_internals,
4483 GtkCallback callback,
4484 gpointer callback_data)
4486 GtkNotebook *notebook = GTK_NOTEBOOK (container);
4487 GtkNotebookPrivate *priv = notebook->priv;
4491 children = priv->children;
4494 GtkNotebookPage *page;
4496 page = children->data;
4497 children = children->next;
4498 (* callback) (page->child, callback_data);
4500 if (include_internals)
4502 if (page->tab_label)
4503 (* callback) (page->tab_label, callback_data);
4507 if (include_internals) {
4508 for (i = 0; i < N_ACTION_WIDGETS; i++)
4510 if (priv->action_widget[i])
4511 (* callback) (priv->action_widget[i], callback_data);
4516 static GtkWidgetPath *
4517 gtk_notebook_get_path_for_child (GtkContainer *container,
4520 GtkNotebookPrivate *priv;
4521 GtkNotebook *notebook;
4522 GtkNotebookPage *page;
4523 GtkWidgetPath *path;
4526 path = GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->get_path_for_child (container, widget);
4528 notebook = GTK_NOTEBOOK (container);
4529 priv = notebook->priv;
4531 for (c = priv->children; c; c = c->next)
4535 if (page->tab_label == widget)
4539 /* Widget is not a tab label */
4543 gtk_widget_path_iter_add_region (path,
4544 gtk_widget_path_length (path) - 2,
4545 GTK_STYLE_REGION_TAB,
4546 _gtk_notebook_get_tab_flags (notebook, page));
4552 gtk_notebook_child_type (GtkContainer *container)
4554 return GTK_TYPE_WIDGET;
4557 /* Private GtkNotebook Methods:
4559 * gtk_notebook_real_insert_page
4562 page_visible_cb (GtkWidget *page,
4566 GtkNotebook *notebook = GTK_NOTEBOOK (data);
4567 GtkNotebookPrivate *priv = notebook->priv;
4571 if (priv->cur_page &&
4572 priv->cur_page->child == page &&
4573 !gtk_widget_get_visible (page))
4575 list = g_list_find (priv->children, priv->cur_page);
4578 next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4580 next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4584 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4589 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4591 GtkWidget *tab_label,
4592 GtkWidget *menu_label,
4595 GtkNotebookPrivate *priv = notebook->priv;
4596 GtkNotebookPage *page;
4600 gtk_widget_freeze_child_notify (child);
4602 page = g_slice_new0 (GtkNotebookPage);
4603 page->child = child;
4605 nchildren = g_list_length (priv->children);
4606 if ((position < 0) || (position > nchildren))
4607 position = nchildren;
4609 priv->children = g_list_insert (priv->children, page, position);
4613 page->default_tab = TRUE;
4615 page->tab_label = tab_label;
4616 page->menu_label = menu_label;
4617 page->expand = FALSE;
4621 page->default_menu = TRUE;
4623 g_object_ref_sink (page->menu_label);
4626 gtk_notebook_menu_item_create (notebook,
4627 g_list_find (priv->children, page));
4629 /* child visible will be turned on by switch_page below */
4630 if (priv->cur_page != page)
4631 gtk_widget_set_child_visible (child, FALSE);
4633 gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4635 gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4637 gtk_notebook_update_labels (notebook);
4639 if (!priv->first_tab)
4640 priv->first_tab = priv->children;
4644 if (priv->show_tabs && gtk_widget_get_visible (child))
4645 gtk_widget_show (tab_label);
4647 gtk_widget_hide (tab_label);
4649 page->mnemonic_activate_signal =
4650 g_signal_connect (tab_label,
4651 "mnemonic-activate",
4652 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4656 page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4657 G_CALLBACK (page_visible_cb), notebook);
4659 g_signal_emit (notebook,
4660 notebook_signals[PAGE_ADDED],
4665 if (!priv->cur_page)
4667 gtk_notebook_switch_page (notebook, page);
4668 /* focus_tab is set in the switch_page method */
4669 gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4672 gtk_notebook_update_tab_states (notebook);
4674 if (priv->scrollable)
4675 gtk_notebook_redraw_arrows (notebook);
4677 gtk_widget_child_notify (child, "tab-expand");
4678 gtk_widget_child_notify (child, "tab-fill");
4679 gtk_widget_child_notify (child, "tab-label");
4680 gtk_widget_child_notify (child, "menu-label");
4682 list = g_list_nth (priv->children, position);
4685 gtk_widget_child_notify (((GtkNotebookPage *)list->data)->child, "position");
4689 gtk_widget_thaw_child_notify (child);
4691 /* The page-added handler might have reordered the pages, re-get the position */
4692 return gtk_notebook_page_num (notebook, child);
4695 /* Private GtkNotebook Functions:
4697 * gtk_notebook_redraw_tabs
4698 * gtk_notebook_real_remove
4699 * gtk_notebook_update_labels
4700 * gtk_notebook_timer
4701 * gtk_notebook_set_scroll_timer
4702 * gtk_notebook_page_compare
4703 * gtk_notebook_search_page
4706 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4708 GtkNotebookPrivate *priv = notebook->priv;
4709 GtkAllocation allocation;
4711 GtkNotebookPage *page;
4712 GdkRectangle redraw_rect;
4714 gint tab_pos = get_effective_tab_pos (notebook);
4717 widget = GTK_WIDGET (notebook);
4718 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4720 if (!gtk_widget_get_mapped (widget) || !priv->cur_page)
4723 page = priv->cur_page;
4725 redraw_rect.x = border;
4726 redraw_rect.y = border;
4728 gtk_widget_get_allocation (widget, &allocation);
4730 get_padding_and_border (notebook, &padding);
4734 case GTK_POS_BOTTOM:
4735 redraw_rect.y = allocation.height - border -
4736 page->allocation.height - padding.bottom;
4739 redraw_rect.width = allocation.width - 2 * border;
4740 redraw_rect.height = page->allocation.height + padding.top;
4744 redraw_rect.x = allocation.width - border -
4745 page->allocation.width - padding.right;
4749 redraw_rect.width = page->allocation.width + padding.left;
4750 redraw_rect.height = allocation.height - 2 * border;
4755 redraw_rect.x += allocation.x;
4756 redraw_rect.y += allocation.y;
4758 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4759 &redraw_rect, TRUE);
4763 gtk_notebook_redraw_tabs_junction (GtkNotebook *notebook)
4765 GtkNotebookPrivate *priv = notebook->priv;
4766 GtkAllocation allocation;
4768 GtkNotebookPage *page;
4769 GdkRectangle redraw_rect;
4771 gint tab_pos = get_effective_tab_pos (notebook);
4774 widget = GTK_WIDGET (notebook);
4775 border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4777 if (!gtk_widget_get_mapped (widget) || !priv->cur_page)
4780 page = priv->cur_page;
4782 redraw_rect.x = border;
4783 redraw_rect.y = border;
4785 gtk_widget_get_allocation (widget, &allocation);
4787 get_padding_and_border (notebook, &padding);
4792 case GTK_POS_BOTTOM:
4793 redraw_rect.width = allocation.width - 2 * border;
4794 if (tab_pos == GTK_POS_TOP)
4796 redraw_rect.y = border + page->allocation.y +
4797 page->allocation.height;
4798 redraw_rect.height = padding.top;
4802 redraw_rect.y = allocation.height - border -
4803 page->allocation.height - padding.bottom;
4804 redraw_rect.height = padding.bottom;
4809 redraw_rect.height = allocation.height - 2 * border;
4811 if (tab_pos == GTK_POS_LEFT)
4813 redraw_rect.x = border + page->allocation.x + page->allocation.width;
4814 redraw_rect.width = padding.left;
4818 redraw_rect.x = allocation.width - border -
4819 page->allocation.width - padding.right;
4820 redraw_rect.width = padding.right;
4825 redraw_rect.x += allocation.x;
4826 redraw_rect.y += allocation.y;
4828 gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4829 &redraw_rect, TRUE);
4833 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4835 GtkNotebookPrivate *priv = notebook->priv;
4837 if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4838 gtk_notebook_show_arrows (notebook))
4842 GtkNotebookArrow arrow[4];
4844 arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4845 arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4846 arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4847 arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4849 for (i = 0; i < 4; i++)
4851 if (arrow[i] == ARROW_NONE)
4854 gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4855 gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4862 gtk_notebook_timer (GtkNotebook *notebook)
4864 GtkNotebookPrivate *priv = notebook->priv;
4865 gboolean retval = FALSE;
4869 gtk_notebook_do_arrow (notebook, priv->click_child);
4871 if (priv->need_timer)
4873 GtkSettings *settings;
4876 settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4877 g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4879 priv->need_timer = FALSE;
4880 priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4881 (GSourceFunc) gtk_notebook_timer,
4882 (gpointer) notebook);
4892 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4894 GtkNotebookPrivate *priv = notebook->priv;
4895 GtkWidget *widget = GTK_WIDGET (notebook);
4899 GtkSettings *settings = gtk_widget_get_settings (widget);
4902 g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4904 priv->timer = gdk_threads_add_timeout (timeout,
4905 (GSourceFunc) gtk_notebook_timer,
4906 (gpointer) notebook);
4907 priv->need_timer = TRUE;
4912 gtk_notebook_page_compare (gconstpointer a,
4915 return (((GtkNotebookPage *) a)->child != b);
4919 gtk_notebook_find_child (GtkNotebook *notebook,
4921 const gchar *function)
4923 GtkNotebookPrivate *priv = notebook->priv;
4924 GList *list = g_list_find_custom (priv->children, child,
4925 gtk_notebook_page_compare);
4927 #ifndef G_DISABLE_CHECKS
4928 if (!list && function)
4929 g_warning ("%s: unable to find child %p in notebook %p",
4930 function, child, notebook);
4937 gtk_notebook_remove_tab_label (GtkNotebook *notebook,
4938 GtkNotebookPage *page)
4940 if (page->tab_label)
4942 if (page->mnemonic_activate_signal)
4943 g_signal_handler_disconnect (page->tab_label,
4944 page->mnemonic_activate_signal);
4945 page->mnemonic_activate_signal = 0;
4947 gtk_widget_set_state_flags (page->tab_label, 0, TRUE);
4948 if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (GTK_WIDGET (notebook)) ||
4949 !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
4953 /* we hit this condition during dnd of a detached tab */
4954 parent = gtk_widget_get_parent (page->tab_label);
4955 if (GTK_IS_WINDOW (parent))
4956 gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
4958 gtk_widget_unparent (page->tab_label);
4962 gtk_widget_unparent (page->tab_label);
4965 page->tab_label = NULL;
4970 gtk_notebook_real_remove (GtkNotebook *notebook,
4973 GtkNotebookPrivate *priv = notebook->priv;
4974 GtkNotebookPage *page;
4976 gint need_resize = FALSE;
4977 GtkWidget *tab_label;
4978 gboolean destroying;
4980 destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4982 next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4984 next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4986 priv->children = g_list_remove_link (priv->children, list);
4988 if (priv->cur_page == list->data)
4990 priv->cur_page = NULL;
4991 if (next_list && !destroying)
4992 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4995 if (priv->detached_tab == list->data)
4996 priv->detached_tab = NULL;
4997 if (priv->switch_tab == list)
4998 priv->switch_tab = NULL;
5000 if (list == priv->first_tab)
5001 priv->first_tab = next_list;
5002 if (list == priv->focus_tab && !destroying)
5003 gtk_notebook_switch_focus_tab (notebook, next_list);
5007 g_signal_handler_disconnect (page->child, page->notify_visible_handler);
5009 if (gtk_widget_get_visible (page->child) &&
5010 gtk_widget_get_visible (GTK_WIDGET (notebook)))
5013 gtk_widget_unparent (page->child);
5015 tab_label = page->tab_label;
5018 g_object_ref (tab_label);
5019 gtk_notebook_remove_tab_label (notebook, page);
5021 gtk_widget_destroy (tab_label);
5022 g_object_unref (tab_label);
5027 GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
5029 gtk_notebook_menu_label_unparent (parent, NULL);
5030 gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
5032 gtk_widget_queue_resize (priv->menu);
5034 if (!page->default_menu)
5035 g_object_unref (page->menu_label);
5039 if (page->last_focus_child)
5041 g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
5042 page->last_focus_child = NULL;
5045 g_slice_free (GtkNotebookPage, page);
5047 gtk_notebook_update_labels (notebook);
5049 gtk_widget_queue_resize (GTK_WIDGET (notebook));
5053 gtk_notebook_update_labels (GtkNotebook *notebook)
5055 GtkNotebookPrivate *priv = notebook->priv;
5056 GtkNotebookPage *page;
5061 if (!priv->show_tabs && !priv->menu)
5064 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
5066 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
5069 g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
5070 if (priv->show_tabs)
5072 if (page->default_tab)
5074 if (!page->tab_label)
5076 page->tab_label = gtk_label_new (string);
5077 gtk_widget_set_parent (page->tab_label,
5078 GTK_WIDGET (notebook));
5081 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
5084 if (gtk_widget_get_visible (page->child) &&
5085 !gtk_widget_get_visible (page->tab_label))
5086 gtk_widget_show (page->tab_label);
5087 else if (!gtk_widget_get_visible (page->child) &&
5088 gtk_widget_get_visible (page->tab_label))
5089 gtk_widget_hide (page->tab_label);
5091 if (priv->menu && page->default_menu)
5093 if (GTK_IS_LABEL (page->tab_label))
5094 gtk_label_set_text (GTK_LABEL (page->menu_label),
5095 gtk_label_get_label (GTK_LABEL (page->tab_label)));
5097 gtk_label_set_text (GTK_LABEL (page->menu_label), string);
5103 gtk_notebook_search_page (GtkNotebook *notebook,
5106 gboolean find_visible)
5108 GtkNotebookPrivate *priv = notebook->priv;
5109 GtkNotebookPage *page = NULL;
5110 GList *old_list = NULL;
5115 if (!page || direction == STEP_NEXT)
5123 list = priv->children;
5128 if (direction == STEP_NEXT &&
5130 (gtk_widget_get_visible (page->child) &&
5131 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
5146 if (direction == STEP_PREV &&
5148 (gtk_widget_get_visible (page->child) &&
5149 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
5157 /* Private GtkNotebook Drawing Functions:
5159 * gtk_notebook_paint
5160 * gtk_notebook_draw_tab
5161 * gtk_notebook_draw_arrow
5164 gtk_notebook_paint (GtkWidget *widget,
5167 GtkNotebook *notebook;
5168 GtkNotebookPrivate *priv;
5169 GtkNotebookPage *page;
5170 GtkAllocation allocation;
5175 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
5176 gint gap_x = 0, gap_width = 0, step = STEP_PREV;
5179 GtkStyleContext *context;
5181 notebook = GTK_NOTEBOOK (widget);
5182 priv = notebook->priv;
5183 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5184 tab_pos = get_effective_tab_pos (notebook);
5185 context = gtk_widget_get_style_context (widget);
5188 if ((!priv->show_tabs && !priv->show_border) ||
5189 !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
5192 gtk_widget_get_allocation (widget, &allocation);
5194 x = allocation.x + border_width;
5195 y = allocation.y + border_width;
5196 width = allocation.width - border_width * 2;
5197 height = allocation.height - border_width * 2;
5199 if (priv->show_border && (!priv->show_tabs || !priv->children))
5201 gtk_render_background (context, cr,
5202 x, y, width, height);
5203 gtk_render_frame (context, cr,
5204 x, y, width, height);
5208 if (!priv->first_tab)
5209 priv->first_tab = priv->children;
5211 if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
5212 page = GTK_NOTEBOOK_PAGE (priv->first_tab);
5214 page = priv->cur_page;
5219 y += page->allocation.height;
5221 case GTK_POS_BOTTOM:
5222 height -= page->allocation.height;
5225 x += page->allocation.width;
5228 width -= page->allocation.width;
5232 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
5233 !gtk_widget_get_mapped (priv->cur_page->tab_label))
5243 case GTK_POS_BOTTOM:
5244 if (priv->operation == DRAG_OPERATION_REORDER)
5245 gap_x = priv->drag_window_x - allocation.x - border_width;
5247 gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
5249 gap_width = priv->cur_page->allocation.width;
5250 step = is_rtl ? STEP_PREV : STEP_NEXT;
5254 if (priv->operation == DRAG_OPERATION_REORDER)
5255 gap_x = priv->drag_window_y - border_width - allocation.y;
5257 gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
5259 gap_width = priv->cur_page->allocation.height;
5265 for (children = priv->children; children; children = children->next)
5267 page = children->data;
5269 if (!gtk_widget_get_visible (page->child))
5272 if (!gtk_widget_get_mapped (page->tab_label))
5275 /* No point in keeping searching */
5280 gtk_style_context_save (context);
5282 if (!showarrow || !priv->scrollable)
5284 GtkJunctionSides junction = 0;
5286 /* Apply junction sides, if no arrows are shown,
5287 * then make corners with connecting tabs square.
5292 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_TOPRIGHT : GTK_JUNCTION_CORNER_TOPLEFT;
5295 case GTK_POS_BOTTOM:
5296 junction |= (is_rtl) ? GTK_JUNCTION_CORNER_BOTTOMRIGHT : GTK_JUNCTION_CORNER_BOTTOMLEFT;
5300 junction |= GTK_JUNCTION_CORNER_TOPLEFT;
5304 junction |= GTK_JUNCTION_CORNER_TOPRIGHT;
5309 gtk_style_context_set_junction_sides (context, junction);
5312 gtk_render_background (context, cr,
5313 x, y, width, height);
5314 gtk_render_frame_gap (context, cr,
5315 x, y, width, height,
5316 tab_pos, gap_x, gap_x + gap_width);
5318 gtk_style_context_restore (context);
5320 children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
5324 page = children->data;
5326 if (page == priv->cur_page)
5329 children = gtk_notebook_search_page (notebook, children,
5332 if (!gtk_widget_get_visible (page->child) ||
5333 !gtk_widget_get_mapped (page->tab_label))
5336 gtk_notebook_draw_tab (notebook, page, cr, TRUE);
5339 if (children != NULL)
5341 GList *other_order = NULL;
5345 page = children->data;
5346 children = gtk_notebook_search_page (notebook, children,
5348 if (!gtk_widget_get_visible (page->child) ||
5349 !gtk_widget_get_mapped (page->tab_label))
5352 if (children != NULL)
5353 other_order = g_list_prepend (other_order, children->data);
5356 /* draw them with the opposite order */
5357 for (children = other_order; children; children = children->next)
5359 page = children->data;
5360 gtk_notebook_draw_tab (notebook, page, cr, TRUE);
5363 g_list_free (other_order);
5366 if (showarrow && priv->scrollable)
5368 if (priv->has_before_previous)
5369 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
5370 if (priv->has_before_next)
5371 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
5372 if (priv->has_after_previous)
5373 gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
5374 if (priv->has_after_next)
5375 gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
5378 if (priv->operation != DRAG_OPERATION_REORDER)
5379 gtk_notebook_draw_tab (notebook, priv->cur_page, cr, TRUE);
5383 gtk_notebook_draw_tab (GtkNotebook *notebook,
5384 GtkNotebookPage *page,
5388 GtkNotebookPrivate *priv;
5390 GtkStyleContext *context;
5392 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5393 !gtk_widget_get_mapped (page->tab_label) ||
5394 (page->allocation.width == 0) || (page->allocation.height == 0))
5397 widget = GTK_WIDGET (notebook);
5398 priv = notebook->priv;
5400 context = gtk_widget_get_style_context (widget);
5401 gtk_style_context_save (context);
5402 notebook_tab_prepare_style_context (notebook, page, context, use_flags);
5404 gtk_render_extension (context, cr,
5407 page->allocation.width,
5408 page->allocation.height,
5409 get_tab_gap_pos (notebook));
5411 if (gtk_widget_has_visible_focus (widget) &&
5412 priv->cur_page == page)
5414 gint focus_width, focus_pad;
5415 GtkAllocation allocation;
5417 gtk_widget_get_allocation (page->tab_label, &allocation);
5418 gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
5419 gtk_widget_style_get (widget, "focus-padding", &focus_pad, NULL);
5421 gtk_render_focus (context, cr,
5422 allocation.x - focus_width - focus_pad,
5423 allocation.y - focus_width - focus_pad,
5424 allocation.width + 2 * (focus_width + focus_pad),
5425 allocation.height + 2 * (focus_width + focus_pad));
5428 gtk_style_context_restore (context);
5432 gtk_notebook_draw_arrow (GtkNotebook *notebook,
5434 GtkNotebookArrow nbarrow)
5436 GtkNotebookPrivate *priv = notebook->priv;
5437 GtkStyleContext *context;
5438 GtkStateFlags state = 0;
5440 GdkRectangle arrow_rect;
5441 gboolean is_rtl, left;
5442 gint scroll_arrow_hlength;
5443 gint scroll_arrow_vlength;
5447 widget = GTK_WIDGET (notebook);
5448 context = gtk_widget_get_style_context (widget);
5449 state = gtk_widget_get_state_flags (widget);
5451 gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
5453 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5454 left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
5455 (!ARROW_IS_LEFT (nbarrow) && is_rtl);
5457 gtk_widget_style_get (widget,
5458 "scroll-arrow-hlength", &scroll_arrow_hlength,
5459 "scroll-arrow-vlength", &scroll_arrow_vlength,
5462 if (priv->focus_tab &&
5463 !gtk_notebook_search_page (notebook, priv->focus_tab,
5464 left ? STEP_PREV : STEP_NEXT, TRUE))
5466 state |= GTK_STATE_FLAG_INSENSITIVE;
5468 else if (priv->in_child == nbarrow)
5470 state |= GTK_STATE_FLAG_PRELIGHT;
5472 if (priv->click_child == nbarrow)
5473 state |= GTK_STATE_FLAG_ACTIVE;
5476 if (priv->tab_pos == GTK_POS_LEFT ||
5477 priv->tab_pos == GTK_POS_RIGHT)
5479 angle = (ARROW_IS_LEFT (nbarrow)) ? 0 : G_PI;
5480 arrow_size = scroll_arrow_vlength;
5484 angle = (ARROW_IS_LEFT (nbarrow)) ? 3 * (G_PI / 2) : G_PI / 2;
5485 arrow_size = scroll_arrow_hlength;
5488 gtk_style_context_save (context);
5489 gtk_style_context_set_state (context, state);
5491 gtk_render_arrow (context, cr, angle,
5492 arrow_rect.x, arrow_rect.y,
5495 gtk_style_context_restore (context);
5498 /* Private GtkNotebook Size Allocate Functions:
5500 * gtk_notebook_tab_space
5501 * gtk_notebook_calculate_shown_tabs
5502 * gtk_notebook_calculate_tabs_allocation
5503 * gtk_notebook_pages_allocate
5504 * gtk_notebook_page_allocate
5505 * gtk_notebook_calc_tabs
5508 gtk_notebook_tab_space (GtkNotebook *notebook,
5509 gboolean *show_arrows,
5514 GtkNotebookPrivate *priv = notebook->priv;
5515 GtkAllocation allocation, action_allocation;
5518 gint tab_pos = get_effective_tab_pos (notebook);
5521 gint scroll_arrow_hlength;
5522 gint scroll_arrow_vlength;
5529 widget = GTK_WIDGET (notebook);
5530 children = priv->children;
5531 is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5533 gtk_widget_style_get (GTK_WIDGET (notebook),
5534 "arrow-spacing", &arrow_spacing,
5535 "scroll-arrow-hlength", &scroll_arrow_hlength,
5536 "scroll-arrow-vlength", &scroll_arrow_vlength,
5537 "initial-gap", &initial_gap,
5540 border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5541 get_padding_and_border (notebook, &padding);
5543 gtk_widget_get_allocation (widget, &allocation);
5548 case GTK_POS_BOTTOM:
5549 *min = allocation.x + border_width;
5550 *max = allocation.x + allocation.width - border_width;
5552 for (i = 0; i < N_ACTION_WIDGETS; i++)
5554 if (priv->action_widget[i])
5556 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5558 if ((i == ACTION_WIDGET_START && !is_rtl) ||
5559 (i == ACTION_WIDGET_END && is_rtl))
5560 *min += action_allocation.width + padding.left;
5562 *max -= action_allocation.width + padding.right;
5568 GtkNotebookPage *page;
5570 page = children->data;
5571 children = children->next;
5573 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5574 gtk_widget_get_visible (page->child))
5575 *tab_space += page->requisition.width;
5580 *min = allocation.y + border_width;
5581 *max = allocation.y + allocation.height - border_width;
5583 for (i = 0; i < N_ACTION_WIDGETS; i++)
5585 if (priv->action_widget[i])
5587 gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5589 if (i == ACTION_WIDGET_START)
5590 *min += action_allocation.height + padding.top;
5592 *max -= action_allocation.height + padding.bottom;
5598 GtkNotebookPage *page;
5600 page = children->data;
5601 children = children->next;
5603 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5604 gtk_widget_get_visible (page->child))
5605 *tab_space += page->requisition.height;
5610 *min += initial_gap;
5611 *max -= (2 * initial_gap);
5613 if (!priv->scrollable)
5614 *show_arrows = FALSE;
5617 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5622 case GTK_POS_BOTTOM:
5623 if (*tab_space > *max - *min - tab_overlap)
5625 *show_arrows = TRUE;
5627 /* take arrows into account */
5628 *tab_space = *max - *min - tab_overlap;
5630 if (priv->has_after_previous)
5632 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5633 *max -= arrow_spacing + scroll_arrow_hlength;
5636 if (priv->has_after_next)
5638 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5639 *max -= arrow_spacing + scroll_arrow_hlength;
5642 if (priv->has_before_previous)
5644 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5645 *min += arrow_spacing + scroll_arrow_hlength;
5648 if (priv->has_before_next)
5650 *tab_space -= arrow_spacing + scroll_arrow_hlength;
5651 *min += arrow_spacing + scroll_arrow_hlength;
5657 if (*tab_space > *max - *min - tab_overlap)
5659 *show_arrows = TRUE;
5661 /* take arrows into account */
5662 *tab_space = *max - *min - tab_overlap;
5664 if (priv->has_after_previous || priv->has_after_next)
5666 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5667 *max -= arrow_spacing + scroll_arrow_vlength;
5670 if (priv->has_before_previous || priv->has_before_next)
5672 *tab_space -= arrow_spacing + scroll_arrow_vlength;
5673 *min += arrow_spacing + scroll_arrow_vlength;
5682 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5683 gboolean show_arrows,
5689 gint *remaining_space)
5691 GtkNotebookPrivate *priv = notebook->priv;
5694 GtkNotebookPage *page;
5697 widget = GTK_WIDGET (notebook);
5698 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5700 if (show_arrows) /* first_tab <- focus_tab */
5702 *remaining_space = tab_space;
5704 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5705 gtk_widget_get_visible (priv->cur_page->child))
5707 gtk_notebook_calc_tabs (notebook,
5710 remaining_space, STEP_NEXT);
5713 if (tab_space <= 0 || *remaining_space <= 0)
5716 priv->first_tab = priv->focus_tab;
5717 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5719 page = priv->first_tab->data;
5720 *remaining_space = tab_space - page->requisition.width;
5727 if (priv->first_tab && priv->first_tab != priv->focus_tab)
5729 /* Is first_tab really predecessor of focus_tab? */
5730 page = priv->first_tab->data;
5731 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5732 gtk_widget_get_visible (page->child))
5733 for (children = priv->focus_tab;
5734 children && children != priv->first_tab;
5735 children = gtk_notebook_search_page (notebook,
5743 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5744 priv->first_tab = priv->focus_tab;
5746 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5750 /* calculate shown tabs counting backwards from the focus tab */
5751 gtk_notebook_calc_tabs (notebook,
5752 gtk_notebook_search_page (notebook,
5760 if (*remaining_space < 0)
5763 gtk_notebook_search_page (notebook, priv->first_tab,
5765 if (!priv->first_tab)
5766 priv->first_tab = priv->focus_tab;
5768 *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5771 else /* focus_tab -> end */
5773 if (!priv->first_tab)
5774 priv->first_tab = gtk_notebook_search_page (notebook,
5779 gtk_notebook_calc_tabs (notebook,
5780 gtk_notebook_search_page (notebook,
5788 if (*remaining_space <= 0)
5789 *last_child = children;
5790 else /* start <- first_tab */
5795 gtk_notebook_calc_tabs (notebook,
5796 gtk_notebook_search_page (notebook,
5804 if (*remaining_space == 0)
5805 priv->first_tab = children;
5807 priv->first_tab = gtk_notebook_search_page(notebook,
5814 if (*remaining_space < 0)
5816 /* calculate number of tabs */
5817 *remaining_space = - (*remaining_space);
5820 for (children = priv->first_tab;
5821 children && children != *last_child;
5822 children = gtk_notebook_search_page (notebook, children,
5827 *remaining_space = 0;
5830 /* unmap all non-visible tabs */
5831 for (children = gtk_notebook_search_page (notebook, NULL,
5833 children && children != priv->first_tab;
5834 children = gtk_notebook_search_page (notebook, children,
5837 page = children->data;
5839 if (page->tab_label &&
5840 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5841 gtk_widget_set_child_visible (page->tab_label, FALSE);
5844 for (children = *last_child; children;
5845 children = gtk_notebook_search_page (notebook, children,
5848 page = children->data;
5850 if (page->tab_label &&
5851 NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5852 gtk_widget_set_child_visible (page->tab_label, FALSE);
5855 else /* !show_arrows */
5857 GtkOrientation tab_expand_orientation;
5861 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5862 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5864 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5865 *remaining_space = max - min - tab_overlap - tab_space;
5866 children = priv->children;
5867 priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5871 page = children->data;
5872 children = children->next;
5874 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5875 !gtk_widget_get_visible (page->child))
5881 (gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5888 get_allocate_at_bottom (GtkWidget *widget,
5889 gint search_direction)
5891 gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5892 gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5897 case GTK_POS_BOTTOM:
5899 return (search_direction == STEP_PREV);
5901 return (search_direction == STEP_NEXT);
5906 return (search_direction == STEP_PREV);
5914 gtk_notebook_calculate_tabs_allocation (GtkNotebook *notebook,
5919 gint *remaining_space,
5920 gint *expanded_tabs,
5924 GtkNotebookPrivate *priv = notebook->priv;
5925 GtkAllocation allocation;
5927 GtkContainer *container;
5928 GtkNotebookPage *page;
5929 GtkStyleContext *context;
5930 gboolean allocate_at_bottom;
5931 gint tab_overlap, tab_pos, tab_extra_space;
5932 gint left_x, right_x, top_y, bottom_y, anchor;
5934 gboolean gap_left, packing_changed;
5935 GtkAllocation child_allocation = { 0, };
5936 GtkOrientation tab_expand_orientation;
5938 widget = GTK_WIDGET (notebook);
5939 container = GTK_CONTAINER (notebook);
5940 gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5941 tab_pos = get_effective_tab_pos (notebook);
5942 allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5945 gtk_widget_get_allocation (widget, &allocation);
5947 border_width = gtk_container_get_border_width (container);
5948 child_allocation.x = allocation.x + border_width;
5949 child_allocation.y = allocation.y + border_width;
5951 context = gtk_widget_get_style_context (widget);
5955 case GTK_POS_BOTTOM:
5956 child_allocation.y = allocation.y + allocation.height -
5957 priv->cur_page->requisition.height - border_width;
5960 child_allocation.x = (allocate_at_bottom) ? max : min;
5961 child_allocation.height = priv->cur_page->requisition.height;
5962 anchor = child_allocation.x;
5966 child_allocation.x = allocation.x + allocation.width -
5967 priv->cur_page->requisition.width - border_width;
5970 child_allocation.y = (allocate_at_bottom) ? max : min;
5971 child_allocation.width = priv->cur_page->requisition.width;
5972 anchor = child_allocation.y;
5976 left_x = CLAMP (priv->mouse_x - priv->drag_offset_x,
5977 min, max - priv->cur_page->allocation.width);
5978 top_y = CLAMP (priv->mouse_y - priv->drag_offset_y,
5979 min, max - priv->cur_page->allocation.height);
5980 right_x = left_x + priv->cur_page->allocation.width;
5981 bottom_y = top_y + priv->cur_page->allocation.height;
5982 gap_left = packing_changed = FALSE;
5984 if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5985 tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5987 tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5989 while (*children && *children != last_child)
5991 page = (*children)->data;
5993 if (direction == STEP_NEXT)
5994 *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5997 *children = (*children)->next;
6001 if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
6004 tab_extra_space = 0;
6005 if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
6007 tab_extra_space = *remaining_space / *expanded_tabs;
6008 *remaining_space -= tab_extra_space;
6015 case GTK_POS_BOTTOM:
6016 child_allocation.width = MAX (1, page->requisition.width + tab_overlap + tab_extra_space);
6018 /* make sure that the reordered tab doesn't go past the last position */
6019 if (priv->operation == DRAG_OPERATION_REORDER &&
6020 !gap_left && packing_changed)
6022 if (!allocate_at_bottom)
6024 if (left_x >= anchor)
6026 left_x = priv->drag_window_x = anchor;
6027 anchor += priv->cur_page->allocation.width - tab_overlap;
6032 if (right_x <= anchor)
6034 anchor -= priv->cur_page->allocation.width;
6035 left_x = priv->drag_window_x = anchor;
6036 anchor += tab_overlap;
6043 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
6045 priv->drag_window_x = left_x;
6046 priv->drag_window_y = child_allocation.y;
6050 if (allocate_at_bottom)
6051 anchor -= child_allocation.width;
6053 if (priv->operation == DRAG_OPERATION_REORDER)
6055 if (!allocate_at_bottom &&
6057 left_x <= anchor + child_allocation.width / 2)
6058 anchor += priv->cur_page->allocation.width - tab_overlap;
6059 else if (allocate_at_bottom &&
6060 right_x >= anchor + child_allocation.width / 2 &&
6061 right_x <= anchor + child_allocation.width)
6062 anchor -= priv->cur_page->allocation.width - tab_overlap;
6065 child_allocation.x = anchor;
6071 child_allocation.height = MAX (1, page->requisition.height + tab_overlap + tab_extra_space);
6073 /* make sure that the reordered tab doesn't go past the last position */
6074 if (priv->operation == DRAG_OPERATION_REORDER &&
6075 !gap_left && packing_changed)
6077 if (!allocate_at_bottom && top_y >= anchor)
6079 top_y = priv->drag_window_y = anchor;
6080 anchor += priv->cur_page->allocation.height - tab_overlap;
6086 if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
6088 priv->drag_window_x = child_allocation.x;
6089 priv->drag_window_y = top_y;
6093 if (allocate_at_bottom)
6094 anchor -= child_allocation.height;
6096 if (priv->operation == DRAG_OPERATION_REORDER)
6098 if (!allocate_at_bottom &&
6100 top_y <= anchor + child_allocation.height / 2)
6101 anchor += priv->cur_page->allocation.height - tab_overlap;
6102 else if (allocate_at_bottom &&
6103 bottom_y >= anchor + child_allocation.height / 2 &&
6104 bottom_y <= anchor + child_allocation.height)
6105 anchor -= priv->cur_page->allocation.height - tab_overlap;
6108 child_allocation.y = anchor;
6114 page->allocation = child_allocation;
6116 if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
6117 (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
6119 /* needs to be allocated at 0,0
6120 * to be shown in the drag window */
6121 page->allocation.x = 0;
6122 page->allocation.y = 0;
6125 if (page != priv->cur_page)
6127 GtkBorder active_padding, normal_padding, padding;
6129 /* The active tab is by definition at least the same height as the inactive one.
6130 * The padding we're building is the offset between the two tab states,
6131 * so in case the style specifies normal_padding > active_padding we
6132 * remove the offset and draw them with the same height.
6133 * Note that the padding will still be applied to the tab content though,
6134 * see gtk_notebook_page_allocate().
6136 gtk_style_context_save (context);
6137 notebook_tab_prepare_style_context (notebook, page, context, TRUE);
6139 gtk_style_context_get_padding (context, GTK_STATE_FLAG_ACTIVE, &active_padding);
6140 gtk_style_context_get_padding (context, GTK_STATE_FLAG_NORMAL, &normal_padding);
6142 gtk_style_context_restore (context);
6144 padding.top = MAX (0, active_padding.top - normal_padding.top);
6145 padding.right = MAX (0, active_padding.right - normal_padding.right);
6146 padding.bottom = MAX (0, active_padding.bottom - normal_padding.bottom);
6147 padding.left = MAX (0, active_padding.left - normal_padding.left);
6152 page->allocation.y += padding.top + padding.bottom;
6153 page->allocation.height = MAX (1, page->allocation.height - padding.top - padding.bottom);
6155 case GTK_POS_BOTTOM:
6156 page->allocation.height = MAX (1, page->allocation.height - padding.top - padding.bottom);
6159 page->allocation.x += padding.left + padding.right;
6160 page->allocation.width = MAX (1, page->allocation.width - padding.left - padding.right);
6163 page->allocation.width = MAX (1, page->allocation.width - padding.left - padding.right);
6168 /* calculate whether to leave a gap based on reorder operation or not */
6172 case GTK_POS_BOTTOM:
6173 if (priv->operation != DRAG_OPERATION_REORDER ||
6174 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6176 if (priv->operation == DRAG_OPERATION_REORDER)
6178 if (!allocate_at_bottom &&
6179 left_x > anchor + child_allocation.width / 2 &&
6180 left_x <= anchor + child_allocation.width)
6181 anchor += priv->cur_page->allocation.width - tab_overlap;
6182 else if (allocate_at_bottom &&
6183 right_x >= anchor &&
6184 right_x <= anchor + child_allocation.width / 2)
6185 anchor -= priv->cur_page->allocation.width - tab_overlap;
6188 if (!allocate_at_bottom)
6189 anchor += child_allocation.width - tab_overlap;
6191 anchor += tab_overlap;
6197 if (priv->operation != DRAG_OPERATION_REORDER ||
6198 (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
6200 if (priv->operation == DRAG_OPERATION_REORDER)
6202 if (!allocate_at_bottom &&
6203 top_y >= anchor + child_allocation.height / 2 &&
6204 top_y <= anchor + child_allocation.height)
6205 anchor += priv->cur_page->allocation.height - tab_overlap;
6206 else if (allocate_at_bottom &&
6207 bottom_y >= anchor &&
6208 bottom_y <= anchor + child_allocation.height / 2)
6209 anchor -= priv->cur_page->allocation.height - tab_overlap;
6212 if (!allocate_at_bottom)
6213 anchor += child_allocation.height - tab_overlap;
6215 anchor += tab_overlap;
6221 /* set child visible */
6222 if (page->tab_label)
6223 gtk_widget_set_child_visible (page->tab_label, TRUE);
6226 /* Don't move the current tab past the last position during tabs reordering */
6228 priv->operation == DRAG_OPERATION_REORDER &&
6229 direction == STEP_NEXT)
6234 case GTK_POS_BOTTOM:
6235 if (allocate_at_bottom)
6236 anchor -= priv->cur_page->allocation.width;
6238 if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
6239 (allocate_at_bottom && priv->drag_window_x < anchor))
6240 priv->drag_window_x = anchor;
6244 if (allocate_at_bottom)
6245 anchor -= priv->cur_page->allocation.height;
6247 if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
6248 (allocate_at_bottom && priv->drag_window_y < anchor))
6249 priv->drag_window_y = anchor;
6256 gtk_notebook_pages_allocate (GtkNotebook *notebook)
6258 GtkNotebookPrivate *priv = notebook->priv;
6259 GList *children = NULL;
6260 GList *last_child = NULL;
6261 gboolean showarrow = FALSE;
6262 gint tab_space, min, max, remaining_space;
6264 gboolean tab_allocations_changed = FALSE;
6266 if (!priv->show_tabs || !priv->children || !priv->cur_page)
6269 min = max = tab_space = remaining_space = 0;
6272 gtk_notebook_tab_space (notebook, &showarrow,
6273 &min, &max, &tab_space);
6275 gtk_notebook_calculate_shown_tabs (notebook, showarrow,
6276 min, max, tab_space, &last_child,
6277 &expanded_tabs, &remaining_space);
6279 children = priv->first_tab;
6280 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6281 showarrow, STEP_NEXT,
6282 &remaining_space, &expanded_tabs, min, max);
6283 if (children && children != last_child)
6285 children = priv->children;
6286 gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
6287 showarrow, STEP_PREV,
6288 &remaining_space, &expanded_tabs, min, max);
6291 children = priv->children;
6295 if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
6296 tab_allocations_changed = TRUE;
6297 children = children->next;
6300 if (!priv->first_tab)
6301 priv->first_tab = priv->children;
6303 if (tab_allocations_changed)
6304 gtk_notebook_redraw_tabs (notebook);
6308 gtk_notebook_page_allocate (GtkNotebook *notebook,
6309 GtkNotebookPage *page)
6311 GtkWidget *widget = GTK_WIDGET (notebook);
6312 GtkNotebookPrivate *priv = notebook->priv;
6313 GtkAllocation child_allocation, label_allocation;
6314 GtkRequisition tab_requisition;
6315 GtkStyleContext *context;
6317 gint focus_width, focus_padding;
6318 gint tab_curvature, tab_overlap;
6319 gint tab_pos = get_effective_tab_pos (notebook);
6320 gboolean tab_allocation_changed;
6321 gboolean was_visible = page->tab_allocated_visible;
6322 GtkBorder tab_padding;
6323 GtkStateFlags state;
6325 if (!page->tab_label ||
6326 !gtk_widget_get_visible (page->tab_label) ||
6327 !gtk_widget_get_child_visible (page->tab_label))
6329 page->tab_allocated_visible = FALSE;
6333 context = gtk_widget_get_style_context (widget);
6335 gtk_style_context_save (context);
6336 state = notebook_tab_prepare_style_context (notebook, page, context, TRUE);
6338 gtk_style_context_get_padding (context, state, &tab_padding);
6340 gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
6341 gtk_widget_style_get (widget,
6342 "focus-line-width", &focus_width,
6343 "focus-padding", &focus_padding,
6344 "tab-curvature", &tab_curvature,
6345 "tab-overlap", &tab_overlap,
6350 case GTK_POS_BOTTOM:
6351 padding = tab_curvature + focus_width + focus_padding;
6354 child_allocation.x = tab_padding.left + padding;
6355 child_allocation.width = MAX (1, (page->allocation.width -
6356 tab_padding.left - tab_padding.right -
6358 child_allocation.x += page->allocation.x;
6360 /* if we're drawing an inactive page, trim the allocation width
6361 * for the children by the difference between tab-curvature
6363 * if we're after the active tab, we need to trim the x
6364 * coordinate of the allocation too, to position it after
6365 * the end of the overlap.
6367 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.left, tab_padding.right))
6369 if (gtk_notebook_page_num (notebook, page->child) >
6370 gtk_notebook_page_num (notebook, priv->cur_page->child))
6372 child_allocation.x += tab_overlap - tab_curvature - tab_padding.left;
6373 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.left;
6377 child_allocation.width -= tab_overlap - tab_curvature - tab_padding.right;
6383 child_allocation.x = page->allocation.x +
6384 (page->allocation.width - tab_requisition.width) / 2;
6386 child_allocation.width = tab_requisition.width;
6389 child_allocation.y =
6390 page->allocation.y + tab_padding.top + focus_width + focus_padding;
6392 child_allocation.height = MAX (1, (page->allocation.height -
6393 tab_padding.top - tab_padding.bottom -
6394 2 * (focus_width + focus_padding)));
6398 padding = tab_curvature + focus_width + focus_padding;
6401 child_allocation.y = tab_padding.top + padding;
6402 child_allocation.height = MAX (1, (page->allocation.height -
6403 tab_padding.bottom - tab_padding.top -
6405 child_allocation.y += page->allocation.y;
6407 /* if we're drawing an inactive page, trim the allocation height
6408 * for the children by the difference between tab-curvature
6410 * if we're after the active tab, we need to trim the y
6411 * coordinate of the allocation too, to position it after
6412 * the end of the overlap.
6414 if (page != priv->cur_page && tab_overlap > tab_curvature + MIN (tab_padding.top, tab_padding.bottom))
6416 if (gtk_notebook_page_num (notebook, page->child) >
6417 gtk_notebook_page_num (notebook, priv->cur_page->child))
6419 child_allocation.y += tab_overlap - tab_curvature - tab_padding.top;
6420 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.top;
6424 child_allocation.height -= tab_overlap - tab_curvature - tab_padding.bottom;
6430 child_allocation.y = page->allocation.y +
6431 (page->allocation.height - tab_requisition.height) / 2;
6433 child_allocation.height = tab_requisition.height;
6436 child_allocation.x =
6437 page->allocation.x + tab_padding.left + focus_width + focus_padding;
6439 child_allocation.width = MAX (1, (page->allocation.width -
6440 tab_padding.left - tab_padding.right -
6441 2 * (focus_width + focus_padding)));
6445 gtk_widget_get_allocation (page->tab_label, &label_allocation);
6446 tab_allocation_changed = (child_allocation.x != label_allocation.x ||
6447 child_allocation.y != label_allocation.y ||
6448 child_allocation.width != label_allocation.width ||
6449 child_allocation.height != label_allocation.height);
6451 gtk_widget_size_allocate (page->tab_label, &child_allocation);
6455 page->tab_allocated_visible = TRUE;
6456 tab_allocation_changed = TRUE;
6459 gtk_style_context_restore (context);
6461 return tab_allocation_changed;
6465 gtk_notebook_calc_tabs (GtkNotebook *notebook,
6471 GtkNotebookPage *page = NULL;
6473 GList *last_calculated_child = NULL;
6474 gint tab_pos = get_effective_tab_pos (notebook);
6484 case GTK_POS_BOTTOM:
6487 page = children->data;
6488 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6489 gtk_widget_get_visible (page->child))
6491 *tab_space -= page->requisition.width;
6492 if (*tab_space < 0 || children == *end)
6496 *tab_space = - (*tab_space +
6497 page->requisition.width);
6499 if (*tab_space == 0 && direction == STEP_PREV)
6500 children = last_calculated_child;
6507 last_calculated_child = children;
6509 if (direction == STEP_NEXT)
6510 children = children->next;
6512 children = children->prev;
6519 page = children->data;
6520 if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6521 gtk_widget_get_visible (page->child))
6523 *tab_space -= page->requisition.height;
6524 if (*tab_space < 0 || children == *end)
6528 *tab_space = - (*tab_space + page->requisition.height);
6530 if (*tab_space == 0 && direction == STEP_PREV)
6531 children = last_calculated_child;
6538 last_calculated_child = children;
6540 if (direction == STEP_NEXT)
6541 children = children->next;
6543 children = children->prev;
6550 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6552 GtkNotebookPrivate *priv = notebook->priv;
6556 pos = gtk_widget_path_length (gtk_widget_get_path (GTK_WIDGET (notebook))) - 1;
6558 for (list = priv->children; list != NULL; list = list->next)
6560 GtkNotebookPage *page = list->data;
6562 if (page->tab_label)
6564 GtkRegionFlags current_flags;
6566 /* FIXME: We should store these flags somewhere instead of poking
6567 * the widget's path */
6568 if (!gtk_widget_path_iter_has_region (gtk_widget_get_path (page->tab_label),
6570 GTK_STYLE_REGION_TAB,
6572 || current_flags != _gtk_notebook_get_tab_flags (notebook, page))
6573 _gtk_widget_invalidate_style_context (page->tab_label, GTK_CSS_CHANGE_PARENT_STATE);
6578 /* Private GtkNotebook Page Switch Methods:
6580 * gtk_notebook_real_switch_page
6583 gtk_notebook_real_switch_page (GtkNotebook *notebook,
6587 GtkNotebookPrivate *priv = notebook->priv;
6588 GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6589 GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6590 gboolean child_has_focus;
6592 if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6595 /* save the value here, changing visibility changes focus */
6596 child_has_focus = priv->child_has_focus;
6599 gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6601 priv->cur_page = page;
6603 if (!priv->focus_tab ||
6604 priv->focus_tab->data != (gpointer) priv->cur_page)
6606 g_list_find (priv->children, priv->cur_page);
6608 gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6610 /* If the focus was on the previous page, move it to the first
6611 * element on the new page, if possible, or if not, to the
6614 if (child_has_focus)
6616 if (priv->cur_page->last_focus_child &&
6617 gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6618 gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6620 if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6621 gtk_widget_grab_focus (GTK_WIDGET (notebook));
6624 gtk_notebook_update_tab_states (notebook);
6625 gtk_notebook_pages_allocate (notebook);
6627 gtk_widget_queue_resize (GTK_WIDGET (notebook));
6628 g_object_notify (G_OBJECT (notebook), "page");
6631 /* Private GtkNotebook Page Switch Functions:
6633 * gtk_notebook_switch_page
6634 * gtk_notebook_page_select
6635 * gtk_notebook_switch_focus_tab
6636 * gtk_notebook_menu_switch_page
6639 gtk_notebook_switch_page (GtkNotebook *notebook,
6640 GtkNotebookPage *page)
6642 GtkNotebookPrivate *priv = notebook->priv;
6645 if (priv->cur_page == page)
6648 page_num = g_list_index (priv->children, page);
6650 g_signal_emit (notebook,
6651 notebook_signals[SWITCH_PAGE],
6658 gtk_notebook_page_select (GtkNotebook *notebook,
6659 gboolean move_focus)
6661 GtkNotebookPrivate *priv = notebook->priv;
6662 GtkNotebookPage *page;
6663 GtkDirectionType dir = GTK_DIR_DOWN; /* Quiet GCC */
6664 gint tab_pos = get_effective_tab_pos (notebook);
6666 if (!priv->focus_tab)
6669 page = priv->focus_tab->data;
6670 gtk_notebook_switch_page (notebook, page);
6679 case GTK_POS_BOTTOM:
6683 dir = GTK_DIR_RIGHT;
6690 if (gtk_widget_child_focus (page->child, dir))
6697 gtk_notebook_switch_focus_tab (GtkNotebook *notebook,
6700 GtkNotebookPrivate *priv = notebook->priv;
6701 GtkNotebookPage *page;
6703 if (priv->focus_tab == new_child)
6706 priv->focus_tab = new_child;
6708 if (priv->scrollable)
6709 gtk_notebook_redraw_arrows (notebook);
6711 if (!priv->show_tabs || !priv->focus_tab)
6714 page = priv->focus_tab->data;
6715 gtk_notebook_switch_page (notebook, page);
6719 gtk_notebook_menu_switch_page (GtkWidget *widget,
6720 GtkNotebookPage *page)
6722 GtkNotebookPrivate *priv;
6723 GtkNotebook *notebook;
6728 parent = gtk_widget_get_parent (widget);
6729 notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6730 priv = notebook->priv;
6732 if (priv->cur_page == page)
6736 children = priv->children;
6737 while (children && children->data != page)
6739 children = children->next;
6743 g_signal_emit (notebook,
6744 notebook_signals[SWITCH_PAGE],
6750 /* Private GtkNotebook Menu Functions:
6752 * gtk_notebook_menu_item_create
6753 * gtk_notebook_menu_label_unparent
6754 * gtk_notebook_menu_detacher
6757 gtk_notebook_menu_item_create (GtkNotebook *notebook,
6760 GtkNotebookPrivate *priv = notebook->priv;
6761 GtkNotebookPage *page;
6762 GtkWidget *menu_item;
6765 if (page->default_menu)
6767 if (GTK_IS_LABEL (page->tab_label))
6768 page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6770 page->menu_label = gtk_label_new ("");
6771 gtk_widget_set_halign (page->menu_label, GTK_ALIGN_START);
6772 gtk_widget_set_valign (page->menu_label, GTK_ALIGN_CENTER);
6775 gtk_widget_show (page->menu_label);
6776 menu_item = gtk_menu_item_new ();
6777 gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6778 gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6779 g_list_position (priv->children, list));
6780 g_signal_connect (menu_item, "activate",
6781 G_CALLBACK (gtk_notebook_menu_switch_page), page);
6782 if (gtk_widget_get_visible (page->child))
6783 gtk_widget_show (menu_item);
6787 gtk_notebook_menu_label_unparent (GtkWidget *widget,
6790 gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6791 _gtk_bin_set_child (GTK_BIN (widget), NULL);
6795 gtk_notebook_menu_detacher (GtkWidget *widget,
6798 GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6799 GtkNotebookPrivate *priv = notebook->priv;
6801 g_return_if_fail (priv->menu == (GtkWidget*) menu);
6806 /* Public GtkNotebook Page Insert/Remove Methods :
6808 * gtk_notebook_append_page
6809 * gtk_notebook_append_page_menu
6810 * gtk_notebook_prepend_page
6811 * gtk_notebook_prepend_page_menu
6812 * gtk_notebook_insert_page
6813 * gtk_notebook_insert_page_menu
6814 * gtk_notebook_remove_page
6817 * gtk_notebook_append_page:
6818 * @notebook: a #GtkNotebook
6819 * @child: the #GtkWidget to use as the contents of the page
6820 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6821 * for the page, or %NULL to use the default label, 'page N'
6823 * Appends a page to @notebook.
6825 * Return value: the index (starting from 0) of the appended
6826 * page in the notebook, or -1 if function fails
6829 gtk_notebook_append_page (GtkNotebook *notebook,
6831 GtkWidget *tab_label)
6833 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6834 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6835 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6837 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6841 * gtk_notebook_append_page_menu:
6842 * @notebook: a #GtkNotebook
6843 * @child: the #GtkWidget to use as the contents of the page
6844 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6845 * for the page, or %NULL to use the default label, 'page N'
6846 * @menu_label: (allow-none): the widget to use as a label for the
6847 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6848 * is a #GtkLabel or %NULL, then the menu label will be a newly
6849 * created label with the same text as @tab_label; if @tab_label
6850 * is not a #GtkLabel, @menu_label must be specified if the
6851 * page-switch menu is to be used.
6853 * Appends a page to @notebook, specifying the widget to use as the
6854 * label in the popup menu.
6856 * Return value: the index (starting from 0) of the appended
6857 * page in the notebook, or -1 if function fails
6860 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6862 GtkWidget *tab_label,
6863 GtkWidget *menu_label)
6865 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6866 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6867 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6868 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6870 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6874 * gtk_notebook_prepend_page:
6875 * @notebook: a #GtkNotebook
6876 * @child: the #GtkWidget to use as the contents of the page
6877 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6878 * for the page, or %NULL to use the default label, 'page N'
6880 * Prepends a page to @notebook.
6882 * Return value: the index (starting from 0) of the prepended
6883 * page in the notebook, or -1 if function fails
6886 gtk_notebook_prepend_page (GtkNotebook *notebook,
6888 GtkWidget *tab_label)
6890 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6891 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6892 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6894 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6898 * gtk_notebook_prepend_page_menu:
6899 * @notebook: a #GtkNotebook
6900 * @child: the #GtkWidget to use as the contents of the page
6901 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6902 * for the page, or %NULL to use the default label, 'page N'
6903 * @menu_label: (allow-none): the widget to use as a label for the
6904 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6905 * is a #GtkLabel or %NULL, then the menu label will be a newly
6906 * created label with the same text as @tab_label; if @tab_label
6907 * is not a #GtkLabel, @menu_label must be specified if the
6908 * page-switch menu is to be used.
6910 * Prepends a page to @notebook, specifying the widget to use as the
6911 * label in the popup menu.
6913 * Return value: the index (starting from 0) of the prepended
6914 * page in the notebook, or -1 if function fails
6917 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6919 GtkWidget *tab_label,
6920 GtkWidget *menu_label)
6922 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6923 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6924 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6925 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6927 return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6931 * gtk_notebook_insert_page:
6932 * @notebook: a #GtkNotebook
6933 * @child: the #GtkWidget to use as the contents of the page
6934 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6935 * for the page, or %NULL to use the default label, 'page N'
6936 * @position: the index (starting at 0) at which to insert the page,
6937 * or -1 to append the page after all other pages
6939 * Insert a page into @notebook at the given position.
6941 * Return value: the index (starting from 0) of the inserted
6942 * page in the notebook, or -1 if function fails
6945 gtk_notebook_insert_page (GtkNotebook *notebook,
6947 GtkWidget *tab_label,
6950 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6951 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6952 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6954 return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6959 gtk_notebook_page_compare_tab (gconstpointer a,
6962 return (((GtkNotebookPage *) a)->tab_label != b);
6966 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6970 GtkNotebook *notebook = GTK_NOTEBOOK (data);
6971 GtkNotebookPrivate *priv = notebook->priv;
6974 list = g_list_find_custom (priv->children, child,
6975 gtk_notebook_page_compare_tab);
6978 GtkNotebookPage *page = list->data;
6980 gtk_widget_grab_focus (GTK_WIDGET (notebook)); /* Do this first to avoid focusing new page */
6981 gtk_notebook_switch_page (notebook, page);
6982 focus_tabs_in (notebook);
6989 * gtk_notebook_insert_page_menu:
6990 * @notebook: a #GtkNotebook
6991 * @child: the #GtkWidget to use as the contents of the page
6992 * @tab_label: (allow-none): the #GtkWidget to be used as the label
6993 * for the page, or %NULL to use the default label, 'page N'
6994 * @menu_label: (allow-none): the widget to use as a label for the
6995 * page-switch menu, if that is enabled. If %NULL, and @tab_label
6996 * is a #GtkLabel or %NULL, then the menu label will be a newly
6997 * created label with the same text as @tab_label; if @tab_label
6998 * is not a #GtkLabel, @menu_label must be specified if the
6999 * page-switch menu is to be used.
7000 * @position: the index (starting at 0) at which to insert the page,
7001 * or -1 to append the page after all other pages.
7003 * Insert a page into @notebook at the given position, specifying
7004 * the widget to use as the label in the popup menu.
7006 * Return value: the index (starting from 0) of the inserted
7007 * page in the notebook
7010 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
7012 GtkWidget *tab_label,
7013 GtkWidget *menu_label,
7016 GtkNotebookClass *class;
7018 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7019 g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
7020 g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
7021 g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
7023 class = GTK_NOTEBOOK_GET_CLASS (notebook);
7025 return (class->insert_page) (notebook, child, tab_label, menu_label, position);
7029 * gtk_notebook_remove_page:
7030 * @notebook: a #GtkNotebook
7031 * @page_num: the index of a notebook page, starting
7032 * from 0. If -1, the last page will be removed.
7034 * Removes a page from the notebook given its index
7038 gtk_notebook_remove_page (GtkNotebook *notebook,
7041 GtkNotebookPrivate *priv;
7044 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7046 priv = notebook->priv;
7049 list = g_list_nth (priv->children, page_num);
7051 list = g_list_last (priv->children);
7054 gtk_container_remove (GTK_CONTAINER (notebook),
7055 ((GtkNotebookPage *) list->data)->child);
7058 /* Public GtkNotebook Page Switch Methods :
7059 * gtk_notebook_get_current_page
7060 * gtk_notebook_page_num
7061 * gtk_notebook_set_current_page
7062 * gtk_notebook_next_page
7063 * gtk_notebook_prev_page
7066 * gtk_notebook_get_current_page:
7067 * @notebook: a #GtkNotebook
7069 * Returns the page number of the current page.
7071 * Return value: the index (starting from 0) of the current
7072 * page in the notebook. If the notebook has no pages,
7073 * then -1 will be returned.
7076 gtk_notebook_get_current_page (GtkNotebook *notebook)
7078 GtkNotebookPrivate *priv;
7080 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7082 priv = notebook->priv;
7084 if (!priv->cur_page)
7087 return g_list_index (priv->children, priv->cur_page);
7091 * gtk_notebook_get_nth_page:
7092 * @notebook: a #GtkNotebook
7093 * @page_num: the index of a page in the notebook, or -1
7094 * to get the last page
7096 * Returns the child widget contained in page number @page_num.
7098 * Return value: (transfer none): the child widget, or %NULL
7099 * if @page_num is out of bounds
7102 gtk_notebook_get_nth_page (GtkNotebook *notebook,
7105 GtkNotebookPrivate *priv;
7106 GtkNotebookPage *page;
7109 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7111 priv = notebook->priv;
7114 list = g_list_nth (priv->children, page_num);
7116 list = g_list_last (priv->children);
7128 * gtk_notebook_get_n_pages:
7129 * @notebook: a #GtkNotebook
7131 * Gets the number of pages in a notebook.
7133 * Return value: the number of pages in the notebook
7138 gtk_notebook_get_n_pages (GtkNotebook *notebook)
7140 GtkNotebookPrivate *priv;
7142 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
7144 priv = notebook->priv;
7146 return g_list_length (priv->children);
7150 * gtk_notebook_page_num:
7151 * @notebook: a #GtkNotebook
7152 * @child: a #GtkWidget
7154 * Finds the index of the page which contains the given child
7157 * Return value: the index of the page containing @child, or
7158 * -1 if @child is not in the notebook
7161 gtk_notebook_page_num (GtkNotebook *notebook,
7164 GtkNotebookPrivate *priv;
7168 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
7170 priv = notebook->priv;
7173 children = priv->children;
7176 GtkNotebookPage *page = children->data;
7178 if (page->child == child)
7181 children = children->next;
7189 * gtk_notebook_set_current_page:
7190 * @notebook: a #GtkNotebook
7191 * @page_num: index of the page to switch to, starting from 0.
7192 * If negative, the last page will be used. If greater
7193 * than the number of pages in the notebook, nothing
7196 * Switches to the page number @page_num.
7198 * Note that due to historical reasons, GtkNotebook refuses
7199 * to switch to a page unless the child widget is visible.
7200 * Therefore, it is recommended to show child widgets before
7201 * adding them to a notebook.
7204 gtk_notebook_set_current_page (GtkNotebook *notebook,
7207 GtkNotebookPrivate *priv;
7210 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7212 priv = notebook->priv;
7215 page_num = g_list_length (priv->children) - 1;
7217 list = g_list_nth (priv->children, page_num);
7219 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7223 * gtk_notebook_next_page:
7224 * @notebook: a #GtkNotebook
7226 * Switches to the next page. Nothing happens if the current page is
7230 gtk_notebook_next_page (GtkNotebook *notebook)
7232 GtkNotebookPrivate *priv;
7235 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7237 priv = notebook->priv;
7239 list = g_list_find (priv->children, priv->cur_page);
7243 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
7247 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7251 * gtk_notebook_prev_page:
7252 * @notebook: a #GtkNotebook
7254 * Switches to the previous page. Nothing happens if the current page
7255 * is the first page.
7258 gtk_notebook_prev_page (GtkNotebook *notebook)
7260 GtkNotebookPrivate *priv;
7263 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7265 priv = notebook->priv;
7267 list = g_list_find (priv->children, priv->cur_page);
7271 list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
7275 gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
7278 /* Public GtkNotebook/Tab Style Functions
7280 * gtk_notebook_set_show_border
7281 * gtk_notebook_get_show_border
7282 * gtk_notebook_set_show_tabs
7283 * gtk_notebook_get_show_tabs
7284 * gtk_notebook_set_tab_pos
7285 * gtk_notebook_get_tab_pos
7286 * gtk_notebook_set_scrollable
7287 * gtk_notebook_get_scrollable
7288 * gtk_notebook_get_tab_hborder
7289 * gtk_notebook_get_tab_vborder
7292 * gtk_notebook_set_show_border:
7293 * @notebook: a #GtkNotebook
7294 * @show_border: %TRUE if a bevel should be drawn around the notebook
7296 * Sets whether a bevel will be drawn around the notebook pages.
7297 * This only has a visual effect when the tabs are not shown.
7298 * See gtk_notebook_set_show_tabs().
7301 gtk_notebook_set_show_border (GtkNotebook *notebook,
7302 gboolean show_border)
7304 GtkNotebookPrivate *priv;
7306 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7308 priv = notebook->priv;
7310 if (priv->show_border != show_border)
7312 priv->show_border = show_border;
7314 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7315 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7317 g_object_notify (G_OBJECT (notebook), "show-border");
7322 * gtk_notebook_get_show_border:
7323 * @notebook: a #GtkNotebook
7325 * Returns whether a bevel will be drawn around the notebook pages.
7326 * See gtk_notebook_set_show_border().
7328 * Return value: %TRUE if the bevel is drawn
7331 gtk_notebook_get_show_border (GtkNotebook *notebook)
7333 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7335 return notebook->priv->show_border;
7339 * gtk_notebook_set_show_tabs:
7340 * @notebook: a #GtkNotebook
7341 * @show_tabs: %TRUE if the tabs should be shown
7343 * Sets whether to show the tabs for the notebook or not.
7346 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
7349 GtkNotebookPrivate *priv;
7350 GtkNotebookPage *page;
7351 GtkStyleContext *context;
7355 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7357 priv = notebook->priv;
7359 show_tabs = show_tabs != FALSE;
7361 if (priv->show_tabs == show_tabs)
7364 priv->show_tabs = show_tabs;
7365 children = priv->children;
7366 context = gtk_widget_get_style_context (GTK_WIDGET (notebook));
7370 gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
7374 page = children->data;
7375 children = children->next;
7376 if (page->default_tab)
7378 gtk_widget_destroy (page->tab_label);
7379 page->tab_label = NULL;
7382 gtk_widget_hide (page->tab_label);
7385 gtk_style_context_remove_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7389 gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
7390 gtk_notebook_update_labels (notebook);
7391 gtk_style_context_add_class (context, GTK_STYLE_CLASS_NOTEBOOK);
7394 for (i = 0; i < N_ACTION_WIDGETS; i++)
7396 if (priv->action_widget[i])
7397 gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
7400 gtk_widget_reset_style (GTK_WIDGET (notebook));
7401 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7403 g_object_notify (G_OBJECT (notebook), "show-tabs");
7407 * gtk_notebook_get_show_tabs:
7408 * @notebook: a #GtkNotebook
7410 * Returns whether the tabs of the notebook are shown.
7411 * See gtk_notebook_set_show_tabs().
7413 * Return value: %TRUE if the tabs are shown
7416 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
7418 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7420 return notebook->priv->show_tabs;
7424 * gtk_notebook_set_tab_pos:
7425 * @notebook: a #GtkNotebook.
7426 * @pos: the edge to draw the tabs at
7428 * Sets the edge at which the tabs for switching pages in the
7429 * notebook are drawn.
7432 gtk_notebook_set_tab_pos (GtkNotebook *notebook,
7433 GtkPositionType pos)
7435 GtkNotebookPrivate *priv;
7437 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7439 priv = notebook->priv;
7441 if (priv->tab_pos != pos)
7443 priv->tab_pos = pos;
7444 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7445 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7448 g_object_notify (G_OBJECT (notebook), "tab-pos");
7452 * gtk_notebook_get_tab_pos:
7453 * @notebook: a #GtkNotebook
7455 * Gets the edge at which the tabs for switching pages in the
7456 * notebook are drawn.
7458 * Return value: the edge at which the tabs are drawn
7461 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
7463 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
7465 return notebook->priv->tab_pos;
7469 * gtk_notebook_set_scrollable:
7470 * @notebook: a #GtkNotebook
7471 * @scrollable: %TRUE if scroll arrows should be added
7473 * Sets whether the tab label area will have arrows for
7474 * scrolling if there are too many tabs to fit in the area.
7477 gtk_notebook_set_scrollable (GtkNotebook *notebook,
7478 gboolean scrollable)
7480 GtkNotebookPrivate *priv;
7482 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7484 priv = notebook->priv;
7486 scrollable = (scrollable != FALSE);
7488 if (scrollable != priv->scrollable)
7490 priv->scrollable = scrollable;
7492 if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7493 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7495 g_object_notify (G_OBJECT (notebook), "scrollable");
7500 * gtk_notebook_get_scrollable:
7501 * @notebook: a #GtkNotebook
7503 * Returns whether the tab label area has arrows for scrolling.
7504 * See gtk_notebook_set_scrollable().
7506 * Return value: %TRUE if arrows for scrolling are present
7509 gtk_notebook_get_scrollable (GtkNotebook *notebook)
7511 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7513 return notebook->priv->scrollable;
7517 * gtk_notebook_get_tab_hborder:
7518 * @notebook: a #GtkNotebook
7520 * Returns the horizontal width of a tab border.
7522 * Return value: horizontal width of a tab border
7526 * Deprecated: 3.4: this function returns zero
7529 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
7531 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7537 * gtk_notebook_get_tab_vborder:
7538 * @notebook: a #GtkNotebook
7540 * Returns the vertical width of a tab border.
7542 * Return value: vertical width of a tab border
7546 * Deprecated: 3.4: this function returns zero
7549 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7551 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7557 /* Public GtkNotebook Popup Menu Methods:
7559 * gtk_notebook_popup_enable
7560 * gtk_notebook_popup_disable
7565 * gtk_notebook_popup_enable:
7566 * @notebook: a #GtkNotebook
7568 * Enables the popup menu: if the user clicks with the right
7569 * mouse button on the tab labels, a menu with all the pages
7570 * will be popped up.
7573 gtk_notebook_popup_enable (GtkNotebook *notebook)
7575 GtkNotebookPrivate *priv;
7578 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7580 priv = notebook->priv;
7585 priv->menu = gtk_menu_new ();
7586 for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7588 list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7589 gtk_notebook_menu_item_create (notebook, list);
7591 gtk_notebook_update_labels (notebook);
7592 gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7593 GTK_WIDGET (notebook),
7594 gtk_notebook_menu_detacher);
7596 g_object_notify (G_OBJECT (notebook), "enable-popup");
7600 * gtk_notebook_popup_disable:
7601 * @notebook: a #GtkNotebook
7603 * Disables the popup menu.
7606 gtk_notebook_popup_disable (GtkNotebook *notebook)
7608 GtkNotebookPrivate *priv;
7610 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7612 priv = notebook->priv;
7617 gtk_container_foreach (GTK_CONTAINER (priv->menu),
7618 (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7619 gtk_widget_destroy (priv->menu);
7621 g_object_notify (G_OBJECT (notebook), "enable-popup");
7624 /* Public GtkNotebook Page Properties Functions:
7626 * gtk_notebook_get_tab_label
7627 * gtk_notebook_set_tab_label
7628 * gtk_notebook_set_tab_label_text
7629 * gtk_notebook_get_menu_label
7630 * gtk_notebook_set_menu_label
7631 * gtk_notebook_set_menu_label_text
7632 * gtk_notebook_get_tab_reorderable
7633 * gtk_notebook_set_tab_reorderable
7634 * gtk_notebook_get_tab_detachable
7635 * gtk_notebook_set_tab_detachable
7639 * gtk_notebook_get_tab_label:
7640 * @notebook: a #GtkNotebook
7643 * Returns the tab label widget for the page @child.
7644 * %NULL is returned if @child is not in @notebook or
7645 * if no tab label has specifically been set for @child.
7647 * Return value: (transfer none): the tab label
7650 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7655 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7656 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7658 list = CHECK_FIND_CHILD (notebook, child);
7662 if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7665 return GTK_NOTEBOOK_PAGE (list)->tab_label;
7669 * gtk_notebook_set_tab_label:
7670 * @notebook: a #GtkNotebook
7672 * @tab_label: (allow-none): the tab label widget to use, or %NULL
7673 * for default tab label
7675 * Changes the tab label for @child.
7676 * If %NULL is specified for @tab_label, then the page will
7677 * have the label 'page N'.
7680 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7682 GtkWidget *tab_label)
7684 GtkNotebookPrivate *priv;
7685 GtkNotebookPage *page;
7688 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7689 g_return_if_fail (GTK_IS_WIDGET (child));
7691 priv = notebook->priv;
7693 list = CHECK_FIND_CHILD (notebook, child);
7697 /* a NULL pointer indicates a default_tab setting, otherwise
7698 * we need to set the associated label
7702 if (page->tab_label == tab_label)
7706 gtk_notebook_remove_tab_label (notebook, page);
7710 page->default_tab = FALSE;
7711 page->tab_label = tab_label;
7712 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7716 page->default_tab = TRUE;
7717 page->tab_label = NULL;
7719 if (priv->show_tabs)
7723 g_snprintf (string, sizeof(string), _("Page %u"),
7724 g_list_position (priv->children, list));
7725 page->tab_label = gtk_label_new (string);
7726 gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7730 if (page->tab_label)
7731 page->mnemonic_activate_signal =
7732 g_signal_connect (page->tab_label,
7733 "mnemonic-activate",
7734 G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7737 if (priv->show_tabs && gtk_widget_get_visible (child))
7739 gtk_widget_show (page->tab_label);
7740 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7743 gtk_notebook_update_tab_states (notebook);
7744 gtk_widget_child_notify (child, "tab-label");
7748 * gtk_notebook_set_tab_label_text:
7749 * @notebook: a #GtkNotebook
7751 * @tab_text: the label text
7753 * Creates a new label and sets it as the tab label for the page
7754 * containing @child.
7757 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7759 const gchar *tab_text)
7761 GtkWidget *tab_label = NULL;
7763 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7766 tab_label = gtk_label_new (tab_text);
7767 gtk_notebook_set_tab_label (notebook, child, tab_label);
7768 gtk_widget_child_notify (child, "tab-label");
7772 * gtk_notebook_get_tab_label_text:
7773 * @notebook: a #GtkNotebook
7774 * @child: a widget contained in a page of @notebook
7776 * Retrieves the text of the tab label for the page containing
7779 * Return value: the text of the tab label, or %NULL if the
7780 * tab label widget is not a #GtkLabel. The string is owned
7781 * by the widget and must not be freed.
7784 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7787 GtkWidget *tab_label;
7789 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7790 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7792 tab_label = gtk_notebook_get_tab_label (notebook, child);
7794 if (GTK_IS_LABEL (tab_label))
7795 return gtk_label_get_text (GTK_LABEL (tab_label));
7801 * gtk_notebook_get_menu_label:
7802 * @notebook: a #GtkNotebook
7803 * @child: a widget contained in a page of @notebook
7805 * Retrieves the menu label widget of the page containing @child.
7807 * Return value: (transfer none): the menu label, or %NULL if the
7808 * notebook page does not have a menu label other than the
7809 * default (the tab label).
7812 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7817 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7818 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7820 list = CHECK_FIND_CHILD (notebook, child);
7824 if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7827 return GTK_NOTEBOOK_PAGE (list)->menu_label;
7831 * gtk_notebook_set_menu_label:
7832 * @notebook: a #GtkNotebook
7833 * @child: the child widget
7834 * @menu_label: (allow-none): the menu label, or %NULL for default
7836 * Changes the menu label for the page containing @child.
7839 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7841 GtkWidget *menu_label)
7843 GtkNotebookPrivate *priv;
7844 GtkNotebookPage *page;
7847 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7848 g_return_if_fail (GTK_IS_WIDGET (child));
7850 priv = notebook->priv;
7852 list = CHECK_FIND_CHILD (notebook, child);
7857 if (page->menu_label)
7860 gtk_container_remove (GTK_CONTAINER (priv->menu),
7861 gtk_widget_get_parent (page->menu_label));
7863 if (!page->default_menu)
7864 g_object_unref (page->menu_label);
7869 page->menu_label = menu_label;
7870 g_object_ref_sink (page->menu_label);
7871 page->default_menu = FALSE;
7874 page->default_menu = TRUE;
7877 gtk_notebook_menu_item_create (notebook, list);
7878 gtk_widget_child_notify (child, "menu-label");
7882 * gtk_notebook_set_menu_label_text:
7883 * @notebook: a #GtkNotebook
7884 * @child: the child widget
7885 * @menu_text: the label text
7887 * Creates a new label and sets it as the menu label of @child.
7890 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7892 const gchar *menu_text)
7894 GtkWidget *menu_label = NULL;
7896 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7900 menu_label = gtk_label_new (menu_text);
7901 gtk_widget_set_halign (menu_label, GTK_ALIGN_START);
7902 gtk_widget_set_valign (menu_label, GTK_ALIGN_CENTER);
7904 gtk_notebook_set_menu_label (notebook, child, menu_label);
7905 gtk_widget_child_notify (child, "menu-label");
7909 * gtk_notebook_get_menu_label_text:
7910 * @notebook: a #GtkNotebook
7911 * @child: the child widget of a page of the notebook.
7913 * Retrieves the text of the menu label for the page containing
7916 * Return value: the text of the tab label, or %NULL if the
7917 * widget does not have a menu label other than the default
7918 * menu label, or the menu label widget is not a #GtkLabel.
7919 * The string is owned by the widget and must not be freed.
7922 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7925 GtkWidget *menu_label;
7927 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7928 g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7930 menu_label = gtk_notebook_get_menu_label (notebook, child);
7932 if (GTK_IS_LABEL (menu_label))
7933 return gtk_label_get_text (GTK_LABEL (menu_label));
7938 /* Helper function called when pages are reordered
7941 gtk_notebook_child_reordered (GtkNotebook *notebook,
7942 GtkNotebookPage *page)
7944 GtkNotebookPrivate *priv = notebook->priv;
7948 GtkWidget *menu_item;
7950 menu_item = gtk_widget_get_parent (page->menu_label);
7951 gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7952 gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7953 gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7956 gtk_notebook_update_tab_states (notebook);
7957 gtk_notebook_update_labels (notebook);
7961 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7966 GtkNotebookPrivate *priv;
7967 GtkNotebookPage *page;
7970 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7971 g_return_if_fail (GTK_IS_WIDGET (child));
7973 priv = notebook->priv;
7975 list = CHECK_FIND_CHILD (notebook, child);
7980 expand = expand != FALSE;
7981 fill = fill != FALSE;
7982 if (page->expand == expand && page->fill == fill)
7985 gtk_widget_freeze_child_notify (child);
7986 page->expand = expand;
7987 gtk_widget_child_notify (child, "tab-expand");
7989 gtk_widget_child_notify (child, "tab-fill");
7990 gtk_widget_child_notify (child, "position");
7991 if (priv->show_tabs)
7992 gtk_widget_queue_resize (GTK_WIDGET (notebook));
7993 gtk_widget_thaw_child_notify (child);
7997 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
8004 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8005 g_return_if_fail (GTK_IS_WIDGET (child));
8007 list = CHECK_FIND_CHILD (notebook, child);
8012 *expand = GTK_NOTEBOOK_PAGE (list)->expand;
8014 *fill = GTK_NOTEBOOK_PAGE (list)->fill;
8018 * gtk_notebook_reorder_child:
8019 * @notebook: a #GtkNotebook
8020 * @child: the child to move
8021 * @position: the new position, or -1 to move to the end
8023 * Reorders the page containing @child, so that it appears in position
8024 * @position. If @position is greater than or equal to the number of
8025 * children in the list or negative, @child will be moved to the end
8029 gtk_notebook_reorder_child (GtkNotebook *notebook,
8033 GtkNotebookPrivate *priv;
8034 GList *list, *new_list;
8035 GtkNotebookPage *page;
8040 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8041 g_return_if_fail (GTK_IS_WIDGET (child));
8043 priv = notebook->priv;
8045 list = CHECK_FIND_CHILD (notebook, child);
8049 max_pos = g_list_length (priv->children) - 1;
8050 if (position < 0 || position > max_pos)
8053 old_pos = g_list_position (priv->children, list);
8055 if (old_pos == position)
8059 priv->children = g_list_delete_link (priv->children, list);
8061 priv->children = g_list_insert (priv->children, page, position);
8062 new_list = g_list_nth (priv->children, position);
8064 /* Fix up GList references in GtkNotebook structure */
8065 if (priv->first_tab == list)
8066 priv->first_tab = new_list;
8067 if (priv->focus_tab == list)
8068 priv->focus_tab = new_list;
8070 gtk_widget_freeze_child_notify (child);
8072 /* Move around the menu items if necessary */
8073 gtk_notebook_child_reordered (notebook, page);
8075 for (list = priv->children, i = 0; list; list = list->next, i++)
8077 if (MIN (old_pos, position) <= i && i <= MAX (old_pos, position))
8078 gtk_widget_child_notify (((GtkNotebookPage *) list->data)->child, "position");
8081 if (priv->show_tabs)
8082 gtk_notebook_pages_allocate (notebook);
8084 gtk_widget_thaw_child_notify (child);
8086 g_signal_emit (notebook,
8087 notebook_signals[PAGE_REORDERED],
8094 * gtk_notebook_set_group_name:
8095 * @notebook: a #GtkNotebook
8096 * @group_name: (allow-none): the name of the notebook group,
8097 * or %NULL to unset it
8099 * Sets a group name for @notebook.
8101 * Notebooks with the same name will be able to exchange tabs
8102 * via drag and drop. A notebook with a %NULL group name will
8103 * not be able to exchange tabs with any other notebook.
8108 gtk_notebook_set_group_name (GtkNotebook *notebook,
8109 const gchar *group_name)
8111 GtkNotebookPrivate *priv;
8114 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8116 priv = notebook->priv;
8118 group = g_quark_from_string (group_name);
8120 if (priv->group != group)
8122 priv->group = group;
8123 g_object_notify (G_OBJECT (notebook), "group-name");
8128 * gtk_notebook_get_group_name:
8129 * @notebook: a #GtkNotebook
8131 * Gets the current group name for @notebook.
8133 * Return Value: (transfer none): the group name,
8134 * or %NULL if none is set.
8139 gtk_notebook_get_group_name (GtkNotebook *notebook)
8141 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8143 return g_quark_to_string (notebook->priv->group);
8147 * gtk_notebook_get_tab_reorderable:
8148 * @notebook: a #GtkNotebook
8149 * @child: a child #GtkWidget
8151 * Gets whether the tab can be reordered via drag and drop or not.
8153 * Return Value: %TRUE if the tab is reorderable.
8158 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
8163 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8164 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8166 list = CHECK_FIND_CHILD (notebook, child);
8170 return GTK_NOTEBOOK_PAGE (list)->reorderable;
8174 * gtk_notebook_set_tab_reorderable:
8175 * @notebook: a #GtkNotebook
8176 * @child: a child #GtkWidget
8177 * @reorderable: whether the tab is reorderable or not
8179 * Sets whether the notebook tab can be reordered
8180 * via drag and drop or not.
8185 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
8187 gboolean reorderable)
8191 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8192 g_return_if_fail (GTK_IS_WIDGET (child));
8194 list = CHECK_FIND_CHILD (notebook, child);
8198 if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
8200 GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
8201 gtk_widget_child_notify (child, "reorderable");
8206 * gtk_notebook_get_tab_detachable:
8207 * @notebook: a #GtkNotebook
8208 * @child: a child #GtkWidget
8210 * Returns whether the tab contents can be detached from @notebook.
8212 * Return Value: %TRUE if the tab is detachable.
8217 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
8222 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
8223 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
8225 list = CHECK_FIND_CHILD (notebook, child);
8229 return GTK_NOTEBOOK_PAGE (list)->detachable;
8233 * gtk_notebook_set_tab_detachable:
8234 * @notebook: a #GtkNotebook
8235 * @child: a child #GtkWidget
8236 * @detachable: whether the tab is detachable or not
8238 * Sets whether the tab can be detached from @notebook to another
8239 * notebook or widget.
8241 * Note that 2 notebooks must share a common group identificator
8242 * (see gtk_notebook_set_group_name()) to allow automatic tabs
8243 * interchange between them.
8245 * If you want a widget to interact with a notebook through DnD
8246 * (i.e.: accept dragged tabs from it) it must be set as a drop
8247 * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
8248 * will fill the selection with a GtkWidget** pointing to the child
8249 * widget that corresponds to the dropped tab.
8252 * on_drop_zone_drag_data_received (GtkWidget *widget,
8253 * GdkDragContext *context,
8256 * GtkSelectionData *selection_data,
8259 * gpointer user_data)
8261 * GtkWidget *notebook;
8262 * GtkWidget **child;
8264 * notebook = gtk_drag_get_source_widget (context);
8265 * child = (void*) gtk_selection_data_get_data (selection_data);
8267 * process_widget (*child);
8268 * gtk_container_remove (GTK_CONTAINER (notebook), *child);
8272 * If you want a notebook to accept drags from other widgets,
8273 * you will have to set your own DnD code to do it.
8278 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
8280 gboolean detachable)
8284 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8285 g_return_if_fail (GTK_IS_WIDGET (child));
8287 list = CHECK_FIND_CHILD (notebook, child);
8291 if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
8293 GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
8294 gtk_widget_child_notify (child, "detachable");
8299 * gtk_notebook_get_action_widget:
8300 * @notebook: a #GtkNotebook
8301 * @pack_type: pack type of the action widget to receive
8303 * Gets one of the action widgets. See gtk_notebook_set_action_widget().
8305 * Returns: (transfer none): The action widget with the given @pack_type
8306 * or %NULL when this action widget has not been set
8311 gtk_notebook_get_action_widget (GtkNotebook *notebook,
8312 GtkPackType pack_type)
8314 g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
8316 return notebook->priv->action_widget[pack_type];
8320 * gtk_notebook_set_action_widget:
8321 * @notebook: a #GtkNotebook
8322 * @widget: a #GtkWidget
8323 * @pack_type: pack type of the action widget
8325 * Sets @widget as one of the action widgets. Depending on the pack type
8326 * the widget will be placed before or after the tabs. You can use
8327 * a #GtkBox if you need to pack more than one widget on the same side.
8329 * Note that action widgets are "internal" children of the notebook and thus
8330 * not included in the list returned from gtk_container_foreach().
8335 gtk_notebook_set_action_widget (GtkNotebook *notebook,
8337 GtkPackType pack_type)
8339 GtkNotebookPrivate *priv;
8341 g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
8342 g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
8343 g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
8345 priv = notebook->priv;
8347 if (priv->action_widget[pack_type])
8348 gtk_widget_unparent (priv->action_widget[pack_type]);
8350 priv->action_widget[pack_type] = widget;
8354 gtk_widget_set_child_visible (widget, priv->show_tabs);
8355 gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
8358 gtk_widget_queue_resize (GTK_WIDGET (notebook));