]> Pileus Git - ~andy/gtk/blob - gtk/gtknotebook.c
Removed size_request from GtkNotebook
[~andy/gtk] / gtk / gtknotebook.c
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
4  *
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.
9  *
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.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
26  */
27
28 #include "config.h"
29
30 #include "gtknotebook.h"
31
32 #include <stdio.h>
33 #include <string.h>
34
35 #include <gdk/gdkkeysyms.h>
36
37 #include "gtkmain.h"
38 #include "gtkmenu.h"
39 #include "gtkmenuitem.h"
40 #include "gtklabel.h"
41 #include "gtkintl.h"
42 #include "gtkmarshalers.h"
43 #include "gtkbindings.h"
44 #include "gtkprivate.h"
45 #include "gtkdnd.h"
46 #include "gtkbuildable.h"
47
48
49 /**
50  * SECTION:gtknotebook
51  * @Short_description: A tabbed notebook container
52  * @Title: GtkNotebook
53  * @See_also: #GtkContainer
54  *
55  * The #GtkNotebook widget is a #GtkContainer whose children are pages that
56  * can be switched between using tab labels along one edge.
57  *
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())
65  *
66  * <refsect2 id="GtkNotebook-BUILDER-UI">
67  * <title>GtkNotebook as GtkBuildable</title>
68  * <para>
69  * The GtkNotebook implementation of the #GtkBuildable interface
70  * supports placing children into tabs by specifying "tab" as the
71  * "type" attribute of a &lt;child&gt; 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 &lt;child&gt;
74  * type attribute.
75  *
76  * To add a child widget in the notebooks action area, specify
77  * "action-start" or "action-end" as the "type" attribute of the &lt;child&gt;
78  * element.
79  * </para>
80  * <example>
81  * <title>A UI definition fragment with GtkNotebook</title>
82  * <programlisting><![CDATA[
83  * <object class="GtkNotebook">
84  *   <child>
85  *     <object class="GtkLabel" id="notebook-content">
86  *       <property name="label">Content</property>
87  *     </object>
88  *   </child>
89  *   <child type="tab">
90  *     <object class="GtkLabel" id="notebook-tab">
91  *       <property name="label">Tab</property>
92  *     </object>
93  *   </child>
94  * </object>
95  * ]]></programlisting>
96  * </example>
97  * </refsect2>
98  */
99
100
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)
106
107 typedef struct _GtkNotebookPage GtkNotebookPage;
108
109 typedef enum
110 {
111   DRAG_OPERATION_NONE,
112   DRAG_OPERATION_REORDER,
113   DRAG_OPERATION_DETACH
114 } GtkNotebookDragOperation;
115
116 enum {
117   ACTION_WIDGET_START,
118   ACTION_WIDGET_END,
119   N_ACTION_WIDGETS
120 };
121
122 struct _GtkNotebookPrivate
123 {
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;
130   GtkWidget                 *menu;
131
132   GdkWindow               *drag_window;
133   GdkWindow               *event_window;
134
135   GList         *children;
136   GList         *first_tab;             /* The first tab visible (for scrolling notebooks) */
137   GList         *focus_tab;
138
139   gint           drag_begin_x;
140   gint           drag_begin_y;
141   gint           drag_offset_x;
142   gint           drag_offset_y;
143   gint           drag_window_x;
144   gint           drag_window_y;
145   gint           mouse_x;
146   gint           mouse_y;
147   gint           pressed_button;
148
149   GQuark         group;
150
151   guint          dnd_timer;
152   guint          switch_tab_timer;
153
154   guint16        tab_hborder;
155   guint16        tab_vborder;
156
157   guint32        timer;
158   guint32        timestamp;
159
160   guint          button             : 2;
161   guint          child_has_focus    : 1;
162   guint          click_child        : 3;
163   guint          during_detach      : 1;
164   guint          during_reorder     : 1;
165   guint          focus_out          : 1; /* Flag used by ::move-focus-out implementation */
166   guint          has_scrolled       : 1;
167   guint          have_visible_child : 1;
168   guint          homogeneous        : 1;
169   guint          in_child           : 3;
170   guint          need_timer         : 1;
171   guint          show_border        : 1;
172   guint          show_tabs          : 1;
173   guint          scrollable         : 1;
174   guint          tab_pos            : 2;
175
176   guint          has_before_previous : 1;
177   guint          has_before_next     : 1;
178   guint          has_after_previous  : 1;
179   guint          has_after_next      : 1;
180 };
181
182 enum {
183   SWITCH_PAGE,
184   FOCUS_TAB,
185   SELECT_PAGE,
186   CHANGE_CURRENT_PAGE,
187   MOVE_FOCUS_OUT,
188   REORDER_TAB,
189   PAGE_REORDERED,
190   PAGE_REMOVED,
191   PAGE_ADDED,
192   CREATE_WINDOW,
193   LAST_SIGNAL
194 };
195
196 enum {
197   STEP_PREV,
198   STEP_NEXT
199 };
200
201 typedef enum
202 {
203   ARROW_NONE,
204   ARROW_LEFT_BEFORE,
205   ARROW_RIGHT_BEFORE,
206   ARROW_LEFT_AFTER,
207   ARROW_RIGHT_AFTER
208 } GtkNotebookArrow;
209
210 typedef enum
211 {
212   POINTER_BEFORE,
213   POINTER_AFTER,
214   POINTER_BETWEEN
215 } GtkNotebookPointerPosition;
216
217 #define ARROW_IS_LEFT(arrow)  ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
218 #define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)
219
220 enum {
221   PROP_0,
222   PROP_TAB_POS,
223   PROP_SHOW_TABS,
224   PROP_SHOW_BORDER,
225   PROP_SCROLLABLE,
226   PROP_PAGE,
227   PROP_ENABLE_POPUP,
228   PROP_GROUP_NAME
229 };
230
231 enum {
232   CHILD_PROP_0,
233   CHILD_PROP_TAB_LABEL,
234   CHILD_PROP_MENU_LABEL,
235   CHILD_PROP_POSITION,
236   CHILD_PROP_TAB_EXPAND,
237   CHILD_PROP_TAB_FILL,
238   CHILD_PROP_TAB_PACK,
239   CHILD_PROP_REORDERABLE,
240   CHILD_PROP_DETACHABLE
241 };
242
243 #define GTK_NOTEBOOK_PAGE(_glist_)         ((GtkNotebookPage *)((GList *)(_glist_))->data)
244
245 /* some useful defines for calculating coords */
246 #define PAGE_LEFT_X(_page_)   (((GtkNotebookPage *) (_page_))->allocation.x)
247 #define PAGE_RIGHT_X(_page_)  (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width)
248 #define PAGE_MIDDLE_X(_page_) (((GtkNotebookPage *) (_page_))->allocation.x + ((GtkNotebookPage *) (_page_))->allocation.width / 2)
249 #define PAGE_TOP_Y(_page_)    (((GtkNotebookPage *) (_page_))->allocation.y)
250 #define PAGE_BOTTOM_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height)
251 #define PAGE_MIDDLE_Y(_page_) (((GtkNotebookPage *) (_page_))->allocation.y + ((GtkNotebookPage *) (_page_))->allocation.height / 2)
252 #define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent (((GtkNotebookPage *) (_page_))->tab_label) == ((GtkWidget *) (_notebook_)))
253
254 struct _GtkNotebookPage
255 {
256   GtkWidget *child;
257   GtkWidget *tab_label;
258   GtkWidget *menu_label;
259   GtkWidget *last_focus_child;  /* Last descendant of the page that had focus */
260
261   guint default_menu : 1;       /* If true, we create the menu label ourself */
262   guint default_tab  : 1;       /* If true, we create the tab label ourself */
263   guint expand       : 1;
264   guint fill         : 1;
265   guint pack         : 1;
266   guint reorderable  : 1;
267   guint detachable   : 1;
268
269   /* if true, the tab label was visible on last allocation; we track this so
270    * that we know to redraw the tab area if a tab label was hidden then shown
271    * without changing position */
272   guint tab_allocated_visible : 1;
273
274   GtkRequisition requisition;
275   GtkAllocation allocation;
276
277   gulong mnemonic_activate_signal;
278   gulong notify_visible_handler;
279 };
280
281 static const GtkTargetEntry notebook_targets [] = {
282   { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
283 };
284
285 #ifdef G_DISABLE_CHECKS
286 #define CHECK_FIND_CHILD(notebook, child)                           \
287  gtk_notebook_find_child (notebook, child, G_STRLOC)
288 #else
289 #define CHECK_FIND_CHILD(notebook, child)                           \
290  gtk_notebook_find_child (notebook, child, NULL)
291 #endif
292  
293 /*** GtkNotebook Methods ***/
294 static gboolean gtk_notebook_select_page         (GtkNotebook      *notebook,
295                                                   gboolean          move_focus);
296 static gboolean gtk_notebook_focus_tab           (GtkNotebook      *notebook,
297                                                   GtkNotebookTab    type);
298 static gboolean gtk_notebook_change_current_page (GtkNotebook      *notebook,
299                                                   gint              offset);
300 static void     gtk_notebook_move_focus_out      (GtkNotebook      *notebook,
301                                                   GtkDirectionType  direction_type);
302 static gboolean gtk_notebook_reorder_tab         (GtkNotebook      *notebook,
303                                                   GtkDirectionType  direction_type,
304                                                   gboolean          move_to_last);
305 static void     gtk_notebook_remove_tab_label    (GtkNotebook      *notebook,
306                                                   GtkNotebookPage  *page);
307 static void     gtk_notebook_set_tab_label_packing   (GtkNotebook  *notebook,
308                                                       GtkWidget    *child,
309                                                       gboolean      expand,
310                                                       gboolean      fill,
311                                                       GtkPackType   pack_type);
312 static void     gtk_notebook_query_tab_label_packing (GtkNotebook  *notebook,
313                                                       GtkWidget    *child,
314                                                       gboolean     *expand,
315                                                       gboolean     *fill,
316                                                       GtkPackType  *pack_type);
317
318 /*** GObject Methods ***/
319 static void gtk_notebook_set_property        (GObject         *object,
320                                               guint            prop_id,
321                                               const GValue    *value,
322                                               GParamSpec      *pspec);
323 static void gtk_notebook_get_property        (GObject         *object,
324                                               guint            prop_id,
325                                               GValue          *value,
326                                               GParamSpec      *pspec);
327
328 /*** GtkWidget Methods ***/
329 static void gtk_notebook_destroy             (GtkWidget        *widget);
330 static void gtk_notebook_map                 (GtkWidget        *widget);
331 static void gtk_notebook_unmap               (GtkWidget        *widget);
332 static void gtk_notebook_realize             (GtkWidget        *widget);
333 static void gtk_notebook_unrealize           (GtkWidget        *widget);
334 static void gtk_notebook_size_request        (GtkWidget        *widget,
335                                               GtkRequisition   *requisition);
336 static void gtk_notebook_get_preferred_width (GtkWidget        *widget,
337                                               gint             *minimum,
338                                               gint             *natural);
339 static void gtk_notebook_get_preferred_height(GtkWidget        *widget,
340                                               gint             *minimum,
341                                               gint             *natural);
342 static void gtk_notebook_size_allocate       (GtkWidget        *widget,
343                                               GtkAllocation    *allocation);
344 static gint gtk_notebook_draw                (GtkWidget        *widget,
345                                               cairo_t          *cr);
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_changed       (GtkWidget          *widget,
362                                               GtkStateType        previous_state);
363 static gint gtk_notebook_focus               (GtkWidget        *widget,
364                                               GtkDirectionType  direction);
365 static void gtk_notebook_style_set           (GtkWidget        *widget,
366                                               GtkStyle         *previous);
367
368 /*** Drag and drop Methods ***/
369 static void gtk_notebook_drag_begin          (GtkWidget        *widget,
370                                               GdkDragContext   *context);
371 static void gtk_notebook_drag_end            (GtkWidget        *widget,
372                                               GdkDragContext   *context);
373 static gboolean gtk_notebook_drag_failed     (GtkWidget        *widget,
374                                               GdkDragContext   *context,
375                                               GtkDragResult     result,
376                                               gpointer          data);
377 static gboolean gtk_notebook_drag_motion     (GtkWidget        *widget,
378                                               GdkDragContext   *context,
379                                               gint              x,
380                                               gint              y,
381                                               guint             time);
382 static void gtk_notebook_drag_leave          (GtkWidget        *widget,
383                                               GdkDragContext   *context,
384                                               guint             time);
385 static gboolean gtk_notebook_drag_drop       (GtkWidget        *widget,
386                                               GdkDragContext   *context,
387                                               gint              x,
388                                               gint              y,
389                                               guint             time);
390 static void gtk_notebook_drag_data_get       (GtkWidget        *widget,
391                                               GdkDragContext   *context,
392                                               GtkSelectionData *data,
393                                               guint             info,
394                                               guint             time);
395 static void gtk_notebook_drag_data_received  (GtkWidget        *widget,
396                                               GdkDragContext   *context,
397                                               gint              x,
398                                               gint              y,
399                                               GtkSelectionData *data,
400                                               guint             info,
401                                               guint             time);
402
403 /*** GtkContainer Methods ***/
404 static void gtk_notebook_set_child_property  (GtkContainer     *container,
405                                               GtkWidget        *child,
406                                               guint             property_id,
407                                               const GValue     *value,
408                                               GParamSpec       *pspec);
409 static void gtk_notebook_get_child_property  (GtkContainer     *container,
410                                               GtkWidget        *child,
411                                               guint             property_id,
412                                               GValue           *value,
413                                               GParamSpec       *pspec);
414 static void gtk_notebook_add                 (GtkContainer     *container,
415                                               GtkWidget        *widget);
416 static void gtk_notebook_remove              (GtkContainer     *container,
417                                               GtkWidget        *widget);
418 static void gtk_notebook_set_focus_child     (GtkContainer     *container,
419                                               GtkWidget        *child);
420 static GType gtk_notebook_child_type       (GtkContainer     *container);
421 static void gtk_notebook_forall              (GtkContainer     *container,
422                                               gboolean          include_internals,
423                                               GtkCallback       callback,
424                                               gpointer          callback_data);
425
426 /*** GtkNotebook Methods ***/
427 static gint gtk_notebook_real_insert_page    (GtkNotebook      *notebook,
428                                               GtkWidget        *child,
429                                               GtkWidget        *tab_label,
430                                               GtkWidget        *menu_label,
431                                               gint              position);
432
433 static GtkNotebook *gtk_notebook_create_window (GtkNotebook    *notebook,
434                                                 GtkWidget      *page,
435                                                 gint            x,
436                                                 gint            y);
437
438 /*** GtkNotebook Private Functions ***/
439 static void gtk_notebook_redraw_tabs         (GtkNotebook      *notebook);
440 static void gtk_notebook_redraw_arrows       (GtkNotebook      *notebook);
441 static void gtk_notebook_real_remove         (GtkNotebook      *notebook,
442                                               GList            *list);
443 static void gtk_notebook_update_labels       (GtkNotebook      *notebook);
444 static gint gtk_notebook_timer               (GtkNotebook      *notebook);
445 static void gtk_notebook_set_scroll_timer    (GtkNotebook *notebook);
446 static gint gtk_notebook_page_compare        (gconstpointer     a,
447                                               gconstpointer     b);
448 static GList* gtk_notebook_find_child        (GtkNotebook      *notebook,
449                                               GtkWidget        *child,
450                                               const gchar      *function);
451 static gint  gtk_notebook_real_page_position (GtkNotebook      *notebook,
452                                               GList            *list);
453 static GList * gtk_notebook_search_page      (GtkNotebook      *notebook,
454                                               GList            *list,
455                                               gint              direction,
456                                               gboolean          find_visible);
457 static void  gtk_notebook_child_reordered    (GtkNotebook      *notebook,
458                                               GtkNotebookPage  *page);
459
460 /*** GtkNotebook Drawing Functions ***/
461 static void gtk_notebook_paint               (GtkWidget        *widget,
462                                               cairo_t          *cr);
463 static void gtk_notebook_draw_tab            (GtkNotebook      *notebook,
464                                               GtkNotebookPage  *page,
465                                               cairo_t          *cr);
466 static void gtk_notebook_draw_arrow          (GtkNotebook      *notebook,
467                                               cairo_t          *cr,
468                                               GtkNotebookArrow  arrow);
469
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,
475                                               GList            *start,
476                                               GList           **end,
477                                               gint             *tab_space,
478                                               guint             direction);
479
480 /*** GtkNotebook Page Switch Methods ***/
481 static void gtk_notebook_real_switch_page    (GtkNotebook      *notebook,
482                                               GtkWidget        *child,
483                                               guint             page_num);
484
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,
491                                               GList            *new_child);
492 static void gtk_notebook_menu_switch_page    (GtkWidget        *widget,
493                                               GtkNotebookPage  *page);
494
495 /*** GtkNotebook Menu Functions ***/
496 static void gtk_notebook_menu_item_create    (GtkNotebook      *notebook,
497                                               GList            *list);
498 static void gtk_notebook_menu_label_unparent (GtkWidget        *widget,
499                                               gpointer          data);
500 static void gtk_notebook_menu_detacher       (GtkWidget        *widget,
501                                               GtkMenu          *menu);
502
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,
506                                                             gboolean overload,
507                                                             gpointer data);
508
509 static gboolean focus_tabs_in  (GtkNotebook      *notebook);
510 static gboolean focus_child_in (GtkNotebook      *notebook,
511                                 GtkDirectionType  direction);
512
513 static void stop_scrolling (GtkNotebook *notebook);
514 static void do_detach_tab  (GtkNotebook *from,
515                             GtkNotebook *to,
516                             GtkWidget   *child,
517                             gint         x,
518                             gint         y);
519
520 /* GtkBuildable */
521 static void gtk_notebook_buildable_init           (GtkBuildableIface *iface);
522 static void gtk_notebook_buildable_add_child      (GtkBuildable *buildable,
523                                                    GtkBuilder   *builder,
524                                                    GObject      *child,
525                                                    const gchar  *type);
526
527 static guint notebook_signals[LAST_SIGNAL] = { 0 };
528
529 G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
530                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
531                                                 gtk_notebook_buildable_init))
532
533 static void
534 add_tab_bindings (GtkBindingSet    *binding_set,
535                   GdkModifierType   modifiers,
536                   GtkDirectionType  direction)
537 {
538   gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
539                                 "move_focus_out", 1,
540                                 GTK_TYPE_DIRECTION_TYPE, direction);
541   gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
542                                 "move_focus_out", 1,
543                                 GTK_TYPE_DIRECTION_TYPE, direction);
544 }
545
546 static void
547 add_arrow_bindings (GtkBindingSet    *binding_set,
548                     guint             keysym,
549                     GtkDirectionType  direction)
550 {
551   guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
552   
553   gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
554                                 "move_focus_out", 1,
555                                 GTK_TYPE_DIRECTION_TYPE, direction);
556   gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
557                                 "move_focus_out", 1,
558                                 GTK_TYPE_DIRECTION_TYPE, direction);
559 }
560
561 static void
562 add_reorder_bindings (GtkBindingSet    *binding_set,
563                       guint             keysym,
564                       GtkDirectionType  direction,
565                       gboolean          move_to_last)
566 {
567   guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
568
569   gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
570                                 "reorder_tab", 2,
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,
574                                 "reorder_tab", 2,
575                                 GTK_TYPE_DIRECTION_TYPE, direction,
576                                 G_TYPE_BOOLEAN, move_to_last);
577 }
578
579 static gboolean
580 gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
581                                 GValue                *return_accu,
582                                 const GValue          *handler_return,
583                                 gpointer               dummy)
584 {
585   gboolean continue_emission;
586   GObject *object;
587
588   object = g_value_get_object (handler_return);
589   g_value_set_object (return_accu, object);
590   continue_emission = !object;
591
592   return continue_emission;
593 }
594
595 static void
596 gtk_notebook_compute_expand (GtkWidget *widget,
597                              gboolean  *hexpand_p,
598                              gboolean  *vexpand_p)
599 {
600   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
601   GtkNotebookPrivate *priv = notebook->priv;
602   gboolean hexpand;
603   gboolean vexpand;
604   GList *list;
605   GtkNotebookPage *page;
606
607   hexpand = FALSE;
608   vexpand = FALSE;
609
610   for (list = priv->children; list; list = list->next)
611     {
612       page = list->data;
613
614       hexpand = hexpand ||
615         gtk_widget_compute_expand (page->child, GTK_ORIENTATION_HORIZONTAL);
616
617       vexpand = vexpand ||
618         gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);
619
620       if (hexpand & vexpand)
621         break;
622     }
623
624   *hexpand_p = hexpand;
625   *vexpand_p = vexpand;
626 }
627
628 static void
629 gtk_notebook_class_init (GtkNotebookClass *class)
630 {
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;
635   
636   gobject_class->set_property = gtk_notebook_set_property;
637   gobject_class->get_property = gtk_notebook_get_property;
638
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->size_allocate = gtk_notebook_size_allocate;
647   widget_class->draw = gtk_notebook_draw;
648   widget_class->button_press_event = gtk_notebook_button_press;
649   widget_class->button_release_event = gtk_notebook_button_release;
650   widget_class->popup_menu = gtk_notebook_popup_menu;
651   widget_class->leave_notify_event = gtk_notebook_leave_notify;
652   widget_class->motion_notify_event = gtk_notebook_motion_notify;
653   widget_class->grab_notify = gtk_notebook_grab_notify;
654   widget_class->state_changed = gtk_notebook_state_changed;
655   widget_class->focus_in_event = gtk_notebook_focus_in;
656   widget_class->focus_out_event = gtk_notebook_focus_out;
657   widget_class->focus = gtk_notebook_focus;
658   widget_class->style_set = gtk_notebook_style_set;
659   widget_class->drag_begin = gtk_notebook_drag_begin;
660   widget_class->drag_end = gtk_notebook_drag_end;
661   widget_class->drag_motion = gtk_notebook_drag_motion;
662   widget_class->drag_leave = gtk_notebook_drag_leave;
663   widget_class->drag_drop = gtk_notebook_drag_drop;
664   widget_class->drag_data_get = gtk_notebook_drag_data_get;
665   widget_class->drag_data_received = gtk_notebook_drag_data_received;
666   widget_class->compute_expand = gtk_notebook_compute_expand;
667
668   container_class->add = gtk_notebook_add;
669   container_class->remove = gtk_notebook_remove;
670   container_class->forall = gtk_notebook_forall;
671   container_class->set_focus_child = gtk_notebook_set_focus_child;
672   container_class->get_child_property = gtk_notebook_get_child_property;
673   container_class->set_child_property = gtk_notebook_set_child_property;
674   container_class->child_type = gtk_notebook_child_type;
675
676   class->switch_page = gtk_notebook_real_switch_page;
677   class->insert_page = gtk_notebook_real_insert_page;
678
679   class->focus_tab = gtk_notebook_focus_tab;
680   class->select_page = gtk_notebook_select_page;
681   class->change_current_page = gtk_notebook_change_current_page;
682   class->move_focus_out = gtk_notebook_move_focus_out;
683   class->reorder_tab = gtk_notebook_reorder_tab;
684   class->create_window = gtk_notebook_create_window;
685   
686   g_object_class_install_property (gobject_class,
687                                    PROP_PAGE,
688                                    g_param_spec_int ("page",
689                                                      P_("Page"),
690                                                      P_("The index of the current page"),
691                                                      -1,
692                                                      G_MAXINT,
693                                                      -1,
694                                                      GTK_PARAM_READWRITE));
695   g_object_class_install_property (gobject_class,
696                                    PROP_TAB_POS,
697                                    g_param_spec_enum ("tab-pos",
698                                                       P_("Tab Position"),
699                                                       P_("Which side of the notebook holds the tabs"),
700                                                       GTK_TYPE_POSITION_TYPE,
701                                                       GTK_POS_TOP,
702                                                       GTK_PARAM_READWRITE));
703   g_object_class_install_property (gobject_class,
704                                    PROP_SHOW_TABS,
705                                    g_param_spec_boolean ("show-tabs",
706                                                          P_("Show Tabs"),
707                                                          P_("Whether tabs should be shown"),
708                                                          TRUE,
709                                                          GTK_PARAM_READWRITE));
710   g_object_class_install_property (gobject_class,
711                                    PROP_SHOW_BORDER,
712                                    g_param_spec_boolean ("show-border",
713                                                          P_("Show Border"),
714                                                          P_("Whether the border should be shown"),
715                                                          TRUE,
716                                                          GTK_PARAM_READWRITE));
717   g_object_class_install_property (gobject_class,
718                                    PROP_SCROLLABLE,
719                                    g_param_spec_boolean ("scrollable",
720                                                          P_("Scrollable"),
721                                                          P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
722                                                          FALSE,
723                                                          GTK_PARAM_READWRITE));
724   g_object_class_install_property (gobject_class,
725                                    PROP_ENABLE_POPUP,
726                                    g_param_spec_boolean ("enable-popup",
727                                                          P_("Enable Popup"),
728                                                          P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
729                                                          FALSE,
730                                                          GTK_PARAM_READWRITE));
731
732   /**
733    * GtkNotebook:group-name:
734    *
735    * Group name for tab drag and drop.
736    *
737    * Since: 2.24
738    */
739   g_object_class_install_property (gobject_class,
740                                    PROP_GROUP_NAME,
741                                    g_param_spec_string ("group-name",
742                                                         P_("Group Name"),
743                                                         P_("Group name for tab drag and drop"),
744                                                         NULL,
745                                                         GTK_PARAM_READWRITE));
746
747   gtk_container_class_install_child_property (container_class,
748                                               CHILD_PROP_TAB_LABEL,
749                                               g_param_spec_string ("tab-label", 
750                                                                    P_("Tab label"),
751                                                                    P_("The string displayed on the child's tab label"),
752                                                                    NULL,
753                                                                    GTK_PARAM_READWRITE));
754   gtk_container_class_install_child_property (container_class,
755                                               CHILD_PROP_MENU_LABEL,
756                                               g_param_spec_string ("menu-label", 
757                                                                    P_("Menu label"), 
758                                                                    P_("The string displayed in the child's menu entry"),
759                                                                    NULL,
760                                                                    GTK_PARAM_READWRITE));
761   gtk_container_class_install_child_property (container_class,
762                                               CHILD_PROP_POSITION,
763                                               g_param_spec_int ("position", 
764                                                                 P_("Position"), 
765                                                                 P_("The index of the child in the parent"),
766                                                                 -1, G_MAXINT, 0,
767                                                                 GTK_PARAM_READWRITE));
768   gtk_container_class_install_child_property (container_class,
769                                               CHILD_PROP_TAB_EXPAND,
770                                               g_param_spec_boolean ("tab-expand", 
771                                                                     P_("Tab expand"), 
772                                                                     P_("Whether to expand the child's tab"),
773                                                                     FALSE,
774                                                                     GTK_PARAM_READWRITE));
775   gtk_container_class_install_child_property (container_class,
776                                               CHILD_PROP_TAB_FILL,
777                                               g_param_spec_boolean ("tab-fill", 
778                                                                     P_("Tab fill"), 
779                                                                     P_("Whether the child's tab should fill the allocated area"),
780                                                                     TRUE,
781                                                                     GTK_PARAM_READWRITE));
782
783   /**
784    * GtkNotebook:tab-pack:
785    *
786    *  Deprecated: 2.20: The tab packing functionality of children should not
787    *  be used anymore and support will be removed in the future.
788    */
789   gtk_container_class_install_child_property (container_class,
790                                               CHILD_PROP_TAB_PACK,
791                                               g_param_spec_enum ("tab-pack", 
792                                                                  P_("Tab pack type"),
793                                                                  P_("A GtkPackType indicating whether the child is packed with reference to the start or end of the parent"),
794                                                                  GTK_TYPE_PACK_TYPE, GTK_PACK_START,
795                                                                  GTK_PARAM_READWRITE));
796   gtk_container_class_install_child_property (container_class,
797                                               CHILD_PROP_REORDERABLE,
798                                               g_param_spec_boolean ("reorderable",
799                                                                     P_("Tab reorderable"),
800                                                                     P_("Whether the tab is reorderable by user action"),
801                                                                     FALSE,
802                                                                     GTK_PARAM_READWRITE));
803   gtk_container_class_install_child_property (container_class,
804                                               CHILD_PROP_DETACHABLE,
805                                               g_param_spec_boolean ("detachable",
806                                                                     P_("Tab detachable"),
807                                                                     P_("Whether the tab is detachable"),
808                                                                     FALSE,
809                                                                     GTK_PARAM_READWRITE));
810
811 /**
812  * GtkNotebook:has-secondary-backward-stepper:
813  *
814  * The "has-secondary-backward-stepper" property determines whether 
815  * a second backward arrow button is displayed on the opposite end 
816  * of the tab area.
817  *
818  * Since: 2.4
819  */  
820   gtk_widget_class_install_style_property (widget_class,
821                                            g_param_spec_boolean ("has-secondary-backward-stepper",
822                                                                  P_("Secondary backward stepper"),
823                                                                  P_("Display a second backward arrow button on the opposite end of the tab area"),
824                                                                  FALSE,
825                                                                  GTK_PARAM_READABLE));
826
827 /**
828  * GtkNotebook:has-secondary-forward-stepper:
829  *
830  * The "has-secondary-forward-stepper" property determines whether 
831  * a second forward arrow button is displayed on the opposite end 
832  * of the tab area.
833  *
834  * Since: 2.4
835  */  
836   gtk_widget_class_install_style_property (widget_class,
837                                            g_param_spec_boolean ("has-secondary-forward-stepper",
838                                                                  P_("Secondary forward stepper"),
839                                                                  P_("Display a second forward arrow button on the opposite end of the tab area"),
840                                                                  FALSE,
841                                                                  GTK_PARAM_READABLE));
842
843 /**
844  * GtkNotebook:has-backward-stepper:
845  *
846  * The "has-backward-stepper" property determines whether 
847  * the standard backward arrow button is displayed.
848  *
849  * Since: 2.4
850  */  
851   gtk_widget_class_install_style_property (widget_class,
852                                            g_param_spec_boolean ("has-backward-stepper",
853                                                                  P_("Backward stepper"),
854                                                                  P_("Display the standard backward arrow button"),
855                                                                  TRUE,
856                                                                  GTK_PARAM_READABLE));
857
858 /**
859  * GtkNotebook:has-forward-stepper:
860  *
861  * The "has-forward-stepper" property determines whether 
862  * the standard forward arrow button is displayed.
863  *
864  * Since: 2.4
865  */  
866   gtk_widget_class_install_style_property (widget_class,
867                                            g_param_spec_boolean ("has-forward-stepper",
868                                                                  P_("Forward stepper"),
869                                                                  P_("Display the standard forward arrow button"),
870                                                                  TRUE,
871                                                                  GTK_PARAM_READABLE));
872   
873 /**
874  * GtkNotebook:tab-overlap:
875  *
876  * The "tab-overlap" property defines size of tab overlap
877  * area.
878  *
879  * Since: 2.10
880  */  
881   gtk_widget_class_install_style_property (widget_class,
882                                            g_param_spec_int ("tab-overlap",
883                                                              P_("Tab overlap"),
884                                                              P_("Size of tab overlap area"),
885                                                              G_MININT,
886                                                              G_MAXINT,
887                                                              2,
888                                                              GTK_PARAM_READABLE));
889
890 /**
891  * GtkNotebook:tab-curvature:
892  *
893  * The "tab-curvature" property defines size of tab curvature.
894  *
895  * Since: 2.10
896  */  
897   gtk_widget_class_install_style_property (widget_class,
898                                            g_param_spec_int ("tab-curvature",
899                                                              P_("Tab curvature"),
900                                                              P_("Size of tab curvature"),
901                                                              0,
902                                                              G_MAXINT,
903                                                              1,
904                                                              GTK_PARAM_READABLE));
905
906   /**
907    * GtkNotebook:arrow-spacing:
908    *
909    * The "arrow-spacing" property defines the spacing between the scroll
910    * arrows and the tabs.
911    *
912    * Since: 2.10
913    */
914   gtk_widget_class_install_style_property (widget_class,
915                                            g_param_spec_int ("arrow-spacing",
916                                                              P_("Arrow spacing"),
917                                                              P_("Scroll arrow spacing"),
918                                                              0,
919                                                              G_MAXINT,
920                                                              0,
921                                                              GTK_PARAM_READABLE));
922
923   /**
924    * GtkNotebook::switch-page:
925    * @notebook: the object which received the signal.
926    * @page: the new current page
927    * @page_num: the index of the page
928    *
929    * Emitted when the user or a function changes the current page.
930    */
931   notebook_signals[SWITCH_PAGE] =
932     g_signal_new (I_("switch-page"),
933                   G_TYPE_FROM_CLASS (gobject_class),
934                   G_SIGNAL_RUN_LAST,
935                   G_STRUCT_OFFSET (GtkNotebookClass, switch_page),
936                   NULL, NULL,
937                   _gtk_marshal_VOID__OBJECT_UINT,
938                   G_TYPE_NONE, 2,
939                   GTK_TYPE_WIDGET,
940                   G_TYPE_UINT);
941   notebook_signals[FOCUS_TAB] = 
942     g_signal_new (I_("focus-tab"),
943                   G_TYPE_FROM_CLASS (gobject_class),
944                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
945                   G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
946                   NULL, NULL,
947                   _gtk_marshal_BOOLEAN__ENUM,
948                   G_TYPE_BOOLEAN, 1,
949                   GTK_TYPE_NOTEBOOK_TAB);
950   notebook_signals[SELECT_PAGE] = 
951     g_signal_new (I_("select-page"),
952                   G_TYPE_FROM_CLASS (gobject_class),
953                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
954                   G_STRUCT_OFFSET (GtkNotebookClass, select_page),
955                   NULL, NULL,
956                   _gtk_marshal_BOOLEAN__BOOLEAN,
957                   G_TYPE_BOOLEAN, 1,
958                   G_TYPE_BOOLEAN);
959   notebook_signals[CHANGE_CURRENT_PAGE] = 
960     g_signal_new (I_("change-current-page"),
961                   G_TYPE_FROM_CLASS (gobject_class),
962                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
963                   G_STRUCT_OFFSET (GtkNotebookClass, change_current_page),
964                   NULL, NULL,
965                   _gtk_marshal_BOOLEAN__INT,
966                   G_TYPE_BOOLEAN, 1,
967                   G_TYPE_INT);
968   notebook_signals[MOVE_FOCUS_OUT] =
969     g_signal_new (I_("move-focus-out"),
970                   G_TYPE_FROM_CLASS (gobject_class),
971                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
972                   G_STRUCT_OFFSET (GtkNotebookClass, move_focus_out),
973                   NULL, NULL,
974                   _gtk_marshal_VOID__ENUM,
975                   G_TYPE_NONE, 1,
976                   GTK_TYPE_DIRECTION_TYPE);
977   notebook_signals[REORDER_TAB] =
978     g_signal_new (I_("reorder-tab"),
979                   G_TYPE_FROM_CLASS (gobject_class),
980                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
981                   G_STRUCT_OFFSET (GtkNotebookClass, reorder_tab),
982                   NULL, NULL,
983                   _gtk_marshal_BOOLEAN__ENUM_BOOLEAN,
984                   G_TYPE_BOOLEAN, 2,
985                   GTK_TYPE_DIRECTION_TYPE,
986                   G_TYPE_BOOLEAN);
987   /**
988    * GtkNotebook::page-reordered:
989    * @notebook: the #GtkNotebook
990    * @child: the child #GtkWidget affected
991    * @page_num: the new page number for @child
992    *
993    * the ::page-reordered signal is emitted in the notebook
994    * right after a page has been reordered.
995    *
996    * Since: 2.10
997    **/
998   notebook_signals[PAGE_REORDERED] =
999     g_signal_new (I_("page-reordered"),
1000                   G_TYPE_FROM_CLASS (gobject_class),
1001                   G_SIGNAL_RUN_LAST,
1002                   G_STRUCT_OFFSET (GtkNotebookClass, page_reordered),
1003                   NULL, NULL,
1004                   _gtk_marshal_VOID__OBJECT_UINT,
1005                   G_TYPE_NONE, 2,
1006                   GTK_TYPE_WIDGET,
1007                   G_TYPE_UINT);
1008   /**
1009    * GtkNotebook::page-removed:
1010    * @notebook: the #GtkNotebook
1011    * @child: the child #GtkWidget affected
1012    * @page_num: the @child page number
1013    *
1014    * the ::page-removed signal is emitted in the notebook
1015    * right after a page is removed from the notebook.
1016    *
1017    * Since: 2.10
1018    **/
1019   notebook_signals[PAGE_REMOVED] =
1020     g_signal_new (I_("page-removed"),
1021                   G_TYPE_FROM_CLASS (gobject_class),
1022                   G_SIGNAL_RUN_LAST,
1023                   G_STRUCT_OFFSET (GtkNotebookClass, page_removed),
1024                   NULL, NULL,
1025                   _gtk_marshal_VOID__OBJECT_UINT,
1026                   G_TYPE_NONE, 2,
1027                   GTK_TYPE_WIDGET,
1028                   G_TYPE_UINT);
1029   /**
1030    * GtkNotebook::page-added:
1031    * @notebook: the #GtkNotebook
1032    * @child: the child #GtkWidget affected
1033    * @page_num: the new page number for @child
1034    *
1035    * the ::page-added signal is emitted in the notebook
1036    * right after a page is added to the notebook.
1037    *
1038    * Since: 2.10
1039    **/
1040   notebook_signals[PAGE_ADDED] =
1041     g_signal_new (I_("page-added"),
1042                   G_TYPE_FROM_CLASS (gobject_class),
1043                   G_SIGNAL_RUN_LAST,
1044                   G_STRUCT_OFFSET (GtkNotebookClass, page_added),
1045                   NULL, NULL,
1046                   _gtk_marshal_VOID__OBJECT_UINT,
1047                   G_TYPE_NONE, 2,
1048                   GTK_TYPE_WIDGET,
1049                   G_TYPE_UINT);
1050
1051   /**
1052    * GtkNotebook::create-window:
1053    * @notebook: the #GtkNotebook emitting the signal
1054    * @page: the tab of @notebook that is being detached
1055    * @x: the X coordinate where the drop happens
1056    * @y: the Y coordinate where the drop happens
1057    *
1058    * The ::create-window signal is emitted when a detachable
1059    * tab is dropped on the root window. 
1060    *
1061    * A handler for this signal can create a window containing 
1062    * a notebook where the tab will be attached. It is also 
1063    * responsible for moving/resizing the window and adding the 
1064    * necessary properties to the notebook (e.g. the 
1065    * #GtkNotebook:group ).
1066    *
1067    * Returns: a #GtkNotebook that @page should be added to, or %NULL.
1068    *
1069    * Since: 2.12
1070    */
1071   notebook_signals[CREATE_WINDOW] = 
1072     g_signal_new (I_("create-window"),
1073                   G_TYPE_FROM_CLASS (gobject_class),
1074                   G_SIGNAL_RUN_LAST,
1075                   G_STRUCT_OFFSET (GtkNotebookClass, create_window),
1076                   gtk_object_handled_accumulator, NULL,
1077                   _gtk_marshal_OBJECT__OBJECT_INT_INT,
1078                   GTK_TYPE_NOTEBOOK, 3,
1079                   GTK_TYPE_WIDGET, G_TYPE_INT, G_TYPE_INT);
1080  
1081   binding_set = gtk_binding_set_by_class (class);
1082   gtk_binding_entry_add_signal (binding_set,
1083                                 GDK_KEY_space, 0,
1084                                 "select-page", 1, 
1085                                 G_TYPE_BOOLEAN, FALSE);
1086   gtk_binding_entry_add_signal (binding_set,
1087                                 GDK_KEY_KP_Space, 0,
1088                                 "select-page", 1, 
1089                                 G_TYPE_BOOLEAN, FALSE);
1090   
1091   gtk_binding_entry_add_signal (binding_set,
1092                                 GDK_KEY_Home, 0,
1093                                 "focus-tab", 1, 
1094                                 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1095   gtk_binding_entry_add_signal (binding_set,
1096                                 GDK_KEY_KP_Home, 0,
1097                                 "focus-tab", 1, 
1098                                 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
1099   gtk_binding_entry_add_signal (binding_set,
1100                                 GDK_KEY_End, 0,
1101                                 "focus-tab", 1, 
1102                                 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1103   gtk_binding_entry_add_signal (binding_set,
1104                                 GDK_KEY_KP_End, 0,
1105                                 "focus-tab", 1, 
1106                                 GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
1107
1108   gtk_binding_entry_add_signal (binding_set,
1109                                 GDK_KEY_Page_Up, GDK_CONTROL_MASK,
1110                                 "change-current-page", 1,
1111                                 G_TYPE_INT, -1);
1112   gtk_binding_entry_add_signal (binding_set,
1113                                 GDK_KEY_Page_Down, GDK_CONTROL_MASK,
1114                                 "change-current-page", 1,
1115                                 G_TYPE_INT, 1);
1116
1117   gtk_binding_entry_add_signal (binding_set,
1118                                 GDK_KEY_Page_Up, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1119                                 "change-current-page", 1,
1120                                 G_TYPE_INT, -1);
1121   gtk_binding_entry_add_signal (binding_set,
1122                                 GDK_KEY_Page_Down, GDK_CONTROL_MASK | GDK_MOD1_MASK,
1123                                 "change-current-page", 1,
1124                                 G_TYPE_INT, 1);
1125
1126   add_arrow_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP);
1127   add_arrow_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN);
1128   add_arrow_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT);
1129   add_arrow_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT);
1130
1131   add_reorder_bindings (binding_set, GDK_KEY_Up, GTK_DIR_UP, FALSE);
1132   add_reorder_bindings (binding_set, GDK_KEY_Down, GTK_DIR_DOWN, FALSE);
1133   add_reorder_bindings (binding_set, GDK_KEY_Left, GTK_DIR_LEFT, FALSE);
1134   add_reorder_bindings (binding_set, GDK_KEY_Right, GTK_DIR_RIGHT, FALSE);
1135   add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_LEFT, TRUE);
1136   add_reorder_bindings (binding_set, GDK_KEY_Home, GTK_DIR_UP, TRUE);
1137   add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_RIGHT, TRUE);
1138   add_reorder_bindings (binding_set, GDK_KEY_End, GTK_DIR_DOWN, TRUE);
1139
1140   add_tab_bindings (binding_set, GDK_CONTROL_MASK, GTK_DIR_TAB_FORWARD);
1141   add_tab_bindings (binding_set, GDK_CONTROL_MASK | GDK_SHIFT_MASK, GTK_DIR_TAB_BACKWARD);
1142
1143   g_type_class_add_private (class, sizeof (GtkNotebookPrivate));
1144 }
1145
1146 static void
1147 gtk_notebook_init (GtkNotebook *notebook)
1148 {
1149   GtkNotebookPrivate *priv;
1150
1151   gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
1152   gtk_widget_set_has_window (GTK_WIDGET (notebook), FALSE);
1153
1154   notebook->priv = G_TYPE_INSTANCE_GET_PRIVATE (notebook,
1155                                                 GTK_TYPE_NOTEBOOK,
1156                                                 GtkNotebookPrivate);
1157   priv = notebook->priv;
1158
1159   priv->cur_page = NULL;
1160   priv->children = NULL;
1161   priv->first_tab = NULL;
1162   priv->focus_tab = NULL;
1163   priv->event_window = NULL;
1164   priv->menu = NULL;
1165
1166   priv->tab_hborder = 2;
1167   priv->tab_vborder = 2;
1168
1169   priv->show_tabs = TRUE;
1170   priv->show_border = TRUE;
1171   priv->tab_pos = GTK_POS_TOP;
1172   priv->scrollable = FALSE;
1173   priv->in_child = 0;
1174   priv->click_child = 0;
1175   priv->button = 0;
1176   priv->need_timer = 0;
1177   priv->child_has_focus = FALSE;
1178   priv->have_visible_child = FALSE;
1179   priv->focus_out = FALSE;
1180
1181   priv->has_before_previous = 1;
1182   priv->has_before_next     = 0;
1183   priv->has_after_previous  = 0;
1184   priv->has_after_next      = 1;
1185
1186   priv->group = 0;
1187   priv->pressed_button = -1;
1188   priv->dnd_timer = 0;
1189   priv->switch_tab_timer = 0;
1190   priv->source_targets = gtk_target_list_new (notebook_targets,
1191                                               G_N_ELEMENTS (notebook_targets));
1192   priv->operation = DRAG_OPERATION_NONE;
1193   priv->detached_tab = NULL;
1194   priv->during_detach = FALSE;
1195   priv->has_scrolled = FALSE;
1196
1197   gtk_drag_dest_set (GTK_WIDGET (notebook), 0,
1198                      notebook_targets, G_N_ELEMENTS (notebook_targets),
1199                      GDK_ACTION_MOVE);
1200
1201   g_signal_connect (G_OBJECT (notebook), "drag-failed",
1202                     G_CALLBACK (gtk_notebook_drag_failed), NULL);
1203
1204   gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
1205 }
1206
1207 static void
1208 gtk_notebook_buildable_init (GtkBuildableIface *iface)
1209 {
1210   iface->add_child = gtk_notebook_buildable_add_child;
1211 }
1212
1213 static void
1214 gtk_notebook_buildable_add_child (GtkBuildable  *buildable,
1215                                   GtkBuilder    *builder,
1216                                   GObject       *child,
1217                                   const gchar   *type)
1218 {
1219   GtkNotebook *notebook = GTK_NOTEBOOK (buildable);
1220
1221   if (type && strcmp (type, "tab") == 0)
1222     {
1223       GtkWidget * page;
1224
1225       page = gtk_notebook_get_nth_page (notebook, -1);
1226       /* To set the tab label widget, we must have already a child
1227        * inside the tab container. */
1228       g_assert (page != NULL);
1229       gtk_notebook_set_tab_label (notebook, page, GTK_WIDGET (child));
1230     }
1231   else if (type && strcmp (type, "action-start") == 0)
1232     {
1233       gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_START);
1234     }
1235   else if (type && strcmp (type, "action-end") == 0)
1236     {
1237       gtk_notebook_set_action_widget (notebook, GTK_WIDGET (child), GTK_PACK_END);
1238     }
1239   else if (!type)
1240     gtk_notebook_append_page (notebook, GTK_WIDGET (child), NULL);
1241   else
1242     GTK_BUILDER_WARN_INVALID_CHILD_TYPE (notebook, type);
1243 }
1244
1245 static gboolean
1246 gtk_notebook_select_page (GtkNotebook *notebook,
1247                           gboolean     move_focus)
1248 {
1249   GtkNotebookPrivate *priv = notebook->priv;
1250
1251   if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1252     {
1253       gtk_notebook_page_select (notebook, move_focus);
1254       return TRUE;
1255     }
1256   else
1257     return FALSE;
1258 }
1259
1260 static gboolean
1261 gtk_notebook_focus_tab (GtkNotebook       *notebook,
1262                         GtkNotebookTab     type)
1263 {
1264   GtkNotebookPrivate *priv = notebook->priv;
1265   GList *list;
1266
1267   if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && priv->show_tabs)
1268     {
1269       switch (type)
1270         {
1271         case GTK_NOTEBOOK_TAB_FIRST:
1272           list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
1273           if (list)
1274             gtk_notebook_switch_focus_tab (notebook, list);
1275           break;
1276         case GTK_NOTEBOOK_TAB_LAST:
1277           list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
1278           if (list)
1279             gtk_notebook_switch_focus_tab (notebook, list);
1280           break;
1281         }
1282
1283       return TRUE;
1284     }
1285   else
1286     return FALSE;
1287 }
1288
1289 static gboolean
1290 gtk_notebook_change_current_page (GtkNotebook *notebook,
1291                                   gint         offset)
1292 {
1293   GtkNotebookPrivate *priv = notebook->priv;
1294   GList *current = NULL;
1295
1296   if (!priv->show_tabs)
1297     return FALSE;
1298
1299   if (priv->cur_page)
1300     current = g_list_find (priv->children, priv->cur_page);
1301
1302   while (offset != 0)
1303     {
1304       current = gtk_notebook_search_page (notebook, current,
1305                                           offset < 0 ? STEP_PREV : STEP_NEXT,
1306                                           TRUE);
1307
1308       if (!current)
1309         {
1310           gboolean wrap_around;
1311
1312           g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
1313                         "gtk-keynav-wrap-around", &wrap_around,
1314                         NULL);
1315
1316           if (wrap_around)
1317             current = gtk_notebook_search_page (notebook, NULL,
1318                                                 offset < 0 ? STEP_PREV : STEP_NEXT,
1319                                                 TRUE);
1320           else
1321             break;
1322         }
1323
1324       offset += offset < 0 ? 1 : -1;
1325     }
1326
1327   if (current)
1328     gtk_notebook_switch_page (notebook, current->data);
1329   else
1330     gtk_widget_error_bell (GTK_WIDGET (notebook));
1331
1332   return TRUE;
1333 }
1334
1335 static GtkDirectionType
1336 get_effective_direction (GtkNotebook      *notebook,
1337                          GtkDirectionType  direction)
1338 {
1339   GtkNotebookPrivate *priv = notebook->priv;
1340
1341   /* Remap the directions into the effective direction it would be for a
1342    * GTK_POS_TOP notebook
1343    */
1344
1345 #define D(rest) GTK_DIR_##rest
1346
1347   static const GtkDirectionType translate_direction[2][4][6] = {
1348     /* LEFT */   {{ D(TAB_FORWARD),  D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP),   D(DOWN) },
1349     /* RIGHT */  { D(TAB_BACKWARD), D(TAB_FORWARD),  D(LEFT), D(RIGHT), D(DOWN), D(UP)   },
1350     /* TOP */    { D(TAB_FORWARD),  D(TAB_BACKWARD), D(UP),   D(DOWN),  D(LEFT), D(RIGHT) },
1351     /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD),  D(DOWN), D(UP),    D(LEFT), D(RIGHT) }},
1352     /* LEFT */  {{ D(TAB_BACKWARD), D(TAB_FORWARD),  D(LEFT), D(RIGHT), D(DOWN), D(UP)   },
1353     /* RIGHT */  { D(TAB_FORWARD),  D(TAB_BACKWARD), D(LEFT), D(RIGHT), D(UP),   D(DOWN) },
1354     /* TOP */    { D(TAB_FORWARD),  D(TAB_BACKWARD), D(UP),   D(DOWN),  D(RIGHT), D(LEFT) },
1355     /* BOTTOM */ { D(TAB_BACKWARD), D(TAB_FORWARD),  D(DOWN), D(UP),    D(RIGHT), D(LEFT) }},
1356   };
1357
1358 #undef D
1359
1360   int text_dir = gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL ? 1 : 0;
1361
1362   return translate_direction[text_dir][priv->tab_pos][direction];
1363 }
1364
1365 static gint
1366 get_effective_tab_pos (GtkNotebook *notebook)
1367 {
1368   GtkNotebookPrivate *priv = notebook->priv;
1369
1370   if (gtk_widget_get_direction (GTK_WIDGET (notebook)) == GTK_TEXT_DIR_RTL)
1371     {
1372       switch (priv->tab_pos)
1373         {
1374         case GTK_POS_LEFT:
1375           return GTK_POS_RIGHT;
1376         case GTK_POS_RIGHT:
1377           return GTK_POS_LEFT;
1378         default: ;
1379         }
1380     }
1381
1382   return priv->tab_pos;
1383 }
1384
1385 static gint
1386 get_tab_gap_pos (GtkNotebook *notebook)
1387 {
1388   gint tab_pos = get_effective_tab_pos (notebook);
1389   gint gap_side = GTK_POS_BOTTOM;
1390   
1391   switch (tab_pos)
1392     {
1393     case GTK_POS_TOP:
1394       gap_side = GTK_POS_BOTTOM;
1395       break;
1396     case GTK_POS_BOTTOM:
1397       gap_side = GTK_POS_TOP;
1398       break;
1399     case GTK_POS_LEFT:
1400       gap_side = GTK_POS_RIGHT;
1401       break;
1402     case GTK_POS_RIGHT:
1403       gap_side = GTK_POS_LEFT;
1404       break;
1405     }
1406
1407   return gap_side;
1408 }
1409
1410 static void
1411 gtk_notebook_move_focus_out (GtkNotebook      *notebook,
1412                              GtkDirectionType  direction_type)
1413 {
1414   GtkNotebookPrivate *priv = notebook->priv;
1415   GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1416   GtkWidget *toplevel;
1417   
1418   if (gtk_container_get_focus_child (GTK_CONTAINER (notebook)) && effective_direction == GTK_DIR_UP)
1419     if (focus_tabs_in (notebook))
1420       return;
1421   if (gtk_widget_is_focus (GTK_WIDGET (notebook)) && effective_direction == GTK_DIR_DOWN)
1422     if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
1423       return;
1424
1425   /* At this point, we know we should be focusing out of the notebook entirely. We
1426    * do this by setting a flag, then propagating the focus motion to the notebook.
1427    */
1428   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (notebook));
1429   if (!gtk_widget_is_toplevel (toplevel))
1430     return;
1431
1432   g_object_ref (notebook);
1433
1434   priv->focus_out = TRUE;
1435   g_signal_emit_by_name (toplevel, "move-focus", direction_type);
1436   priv->focus_out = FALSE;
1437
1438   g_object_unref (notebook);
1439 }
1440
1441 static gint
1442 reorder_tab (GtkNotebook *notebook, GList *position, GList *tab)
1443 {
1444   GtkNotebookPrivate *priv = notebook->priv;
1445   GList *elem;
1446
1447   if (position == tab)
1448     return g_list_position (priv->children, tab);
1449
1450   /* check that we aren't inserting the tab in the
1451    * same relative position, taking packing into account */
1452   elem = (position) ? position->prev : g_list_last (priv->children);
1453
1454   while (elem && elem != tab && GTK_NOTEBOOK_PAGE (elem)->pack != GTK_NOTEBOOK_PAGE (tab)->pack)
1455     elem = elem->prev;
1456
1457   if (elem == tab)
1458     return g_list_position (priv->children, tab);
1459
1460   /* now actually reorder the tab */
1461   if (priv->first_tab == tab)
1462     priv->first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
1463                                                     STEP_NEXT, TRUE);
1464
1465   priv->children = g_list_remove_link (priv->children, tab);
1466
1467   if (!position)
1468     elem = g_list_last (priv->children);
1469   else
1470     {
1471       elem = position->prev;
1472       position->prev = tab;
1473     }
1474
1475   if (elem)
1476     elem->next = tab;
1477   else
1478     priv->children = tab;
1479
1480   tab->prev = elem;
1481   tab->next = position;
1482
1483   return g_list_position (priv->children, tab);
1484 }
1485
1486 static gboolean
1487 gtk_notebook_reorder_tab (GtkNotebook      *notebook,
1488                           GtkDirectionType  direction_type,
1489                           gboolean          move_to_last)
1490 {
1491   GtkNotebookPrivate *priv = notebook->priv;
1492   GtkDirectionType effective_direction = get_effective_direction (notebook, direction_type);
1493   GtkNotebookPage *page;
1494   GList *last, *child;
1495   gint page_num;
1496
1497   if (!gtk_widget_is_focus (GTK_WIDGET (notebook)) || !priv->show_tabs)
1498     return FALSE;
1499
1500   if (!priv->cur_page ||
1501       !priv->cur_page->reorderable)
1502     return FALSE;
1503
1504   if (effective_direction != GTK_DIR_LEFT &&
1505       effective_direction != GTK_DIR_RIGHT)
1506     return FALSE;
1507
1508   if (move_to_last)
1509     {
1510       child = priv->focus_tab;
1511
1512       do
1513         {
1514           last = child;
1515           child = gtk_notebook_search_page (notebook, last, 
1516                                             (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1517                                             TRUE);
1518         }
1519       while (child && GTK_NOTEBOOK_PAGE (last)->pack == GTK_NOTEBOOK_PAGE (child)->pack);
1520
1521       child = last;
1522     }
1523   else
1524     child = gtk_notebook_search_page (notebook, priv->focus_tab,
1525                                       (effective_direction == GTK_DIR_RIGHT) ? STEP_NEXT : STEP_PREV,
1526                                       TRUE);
1527
1528   if (!child || child->data == priv->cur_page)
1529     return FALSE;
1530
1531   page = child->data;
1532
1533   if (page->pack == priv->cur_page->pack)
1534     {
1535       if (effective_direction == GTK_DIR_RIGHT)
1536         page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child->next : child, priv->focus_tab);
1537       else
1538         page_num = reorder_tab (notebook, (page->pack == GTK_PACK_START) ? child : child->next, priv->focus_tab);
1539
1540       gtk_notebook_pages_allocate (notebook);
1541
1542       g_signal_emit (notebook,
1543                      notebook_signals[PAGE_REORDERED],
1544                      0,
1545                      ((GtkNotebookPage *) priv->focus_tab->data)->child,
1546                      page_num);
1547
1548       return TRUE;
1549     }
1550
1551   return FALSE;
1552 }
1553
1554 /**
1555  * gtk_notebook_new:
1556  * 
1557  * Creates a new #GtkNotebook widget with no pages.
1558
1559  * Return value: the newly created #GtkNotebook
1560  **/
1561 GtkWidget*
1562 gtk_notebook_new (void)
1563 {
1564   return g_object_new (GTK_TYPE_NOTEBOOK, NULL);
1565 }
1566
1567 /* Private GObject Methods :
1568  *
1569  * gtk_notebook_set_property
1570  * gtk_notebook_get_property
1571  */
1572 static void
1573 gtk_notebook_set_property (GObject         *object,
1574                            guint            prop_id,
1575                            const GValue    *value,
1576                            GParamSpec      *pspec)
1577 {
1578   GtkNotebook *notebook;
1579
1580   notebook = GTK_NOTEBOOK (object);
1581
1582   switch (prop_id)
1583     {
1584     case PROP_SHOW_TABS:
1585       gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
1586       break;
1587     case PROP_SHOW_BORDER:
1588       gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
1589       break;
1590     case PROP_SCROLLABLE:
1591       gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
1592       break;
1593     case PROP_ENABLE_POPUP:
1594       if (g_value_get_boolean (value))
1595         gtk_notebook_popup_enable (notebook);
1596       else
1597         gtk_notebook_popup_disable (notebook);
1598       break;
1599     case PROP_PAGE:
1600       gtk_notebook_set_current_page (notebook, g_value_get_int (value));
1601       break;
1602     case PROP_TAB_POS:
1603       gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
1604       break;
1605     case PROP_GROUP_NAME:
1606       gtk_notebook_set_group_name (notebook, g_value_get_string (value));
1607       break;
1608     default:
1609       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1610       break;
1611     }
1612 }
1613
1614 static void
1615 gtk_notebook_get_property (GObject         *object,
1616                            guint            prop_id,
1617                            GValue          *value,
1618                            GParamSpec      *pspec)
1619 {
1620   GtkNotebook *notebook = GTK_NOTEBOOK (object);
1621   GtkNotebookPrivate *priv = notebook->priv;
1622
1623   switch (prop_id)
1624     {
1625     case PROP_SHOW_TABS:
1626       g_value_set_boolean (value, priv->show_tabs);
1627       break;
1628     case PROP_SHOW_BORDER:
1629       g_value_set_boolean (value, priv->show_border);
1630       break;
1631     case PROP_SCROLLABLE:
1632       g_value_set_boolean (value, priv->scrollable);
1633       break;
1634     case PROP_ENABLE_POPUP:
1635       g_value_set_boolean (value, priv->menu != NULL);
1636       break;
1637     case PROP_PAGE:
1638       g_value_set_int (value, gtk_notebook_get_current_page (notebook));
1639       break;
1640     case PROP_TAB_POS:
1641       g_value_set_enum (value, priv->tab_pos);
1642       break;
1643     case PROP_GROUP_NAME:
1644       g_value_set_string (value, gtk_notebook_get_group_name (notebook));
1645       break;
1646     default:
1647       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1648       break;
1649     }
1650 }
1651
1652 /* Private GtkWidget Methods :
1653  *
1654  * gtk_notebook_destroy
1655  * gtk_notebook_map
1656  * gtk_notebook_unmap
1657  * gtk_notebook_realize
1658  * gtk_notebook_size_request
1659  * gtk_notebook_size_allocate
1660  * gtk_notebook_draw
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_set
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
1677  */
1678 static void
1679 gtk_notebook_destroy (GtkWidget *widget)
1680 {
1681   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1682   GtkNotebookPrivate *priv = notebook->priv;
1683
1684   if (priv->menu)
1685     gtk_notebook_popup_disable (notebook);
1686
1687   if (priv->source_targets)
1688     {
1689       gtk_target_list_unref (priv->source_targets);
1690       priv->source_targets = NULL;
1691     }
1692
1693   if (priv->switch_tab_timer)
1694     {
1695       g_source_remove (priv->switch_tab_timer);
1696       priv->switch_tab_timer = 0;
1697     }
1698
1699   GTK_WIDGET_CLASS (gtk_notebook_parent_class)->destroy (widget);
1700 }
1701
1702 static gboolean
1703 gtk_notebook_get_event_window_position (GtkNotebook  *notebook,
1704                                         GdkRectangle *rectangle)
1705 {
1706   GtkNotebookPrivate *priv = notebook->priv;
1707   GtkAllocation allocation, action_allocation;
1708   GtkWidget *widget = GTK_WIDGET (notebook);
1709   guint border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
1710   GtkNotebookPage *visible_page = NULL;
1711   GList *tmp_list;
1712   gint tab_pos = get_effective_tab_pos (notebook);
1713   gboolean is_rtl;
1714   gint i;
1715
1716   for (tmp_list = priv->children; tmp_list; tmp_list = tmp_list->next)
1717     {
1718       GtkNotebookPage *page = tmp_list->data;
1719       if (gtk_widget_get_visible (page->child))
1720         {
1721           visible_page = page;
1722           break;
1723         }
1724     }
1725
1726   if (priv->show_tabs && visible_page)
1727     {
1728       if (rectangle)
1729         {
1730           gtk_widget_get_allocation (widget, &allocation);
1731
1732           is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1733           rectangle->x = allocation.x + border_width;
1734           rectangle->y = allocation.y + border_width;
1735
1736           switch (tab_pos)
1737             {
1738             case GTK_POS_TOP:
1739             case GTK_POS_BOTTOM:
1740               rectangle->width = allocation.width - 2 * border_width;
1741               rectangle->height = visible_page->requisition.height;
1742               if (tab_pos == GTK_POS_BOTTOM)
1743                 rectangle->y += allocation.height - 2 * border_width - rectangle->height;
1744
1745               for (i = 0; i < N_ACTION_WIDGETS; i++)
1746                 {
1747                   if (priv->action_widget[i] &&
1748                       gtk_widget_get_visible (priv->action_widget[i]))
1749                     {
1750                       gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1751
1752                       rectangle->width -= action_allocation.width;
1753                       if ((!is_rtl && i == ACTION_WIDGET_START) ||
1754                           (is_rtl && i == ACTION_WIDGET_END))
1755                         rectangle->x += action_allocation.width;
1756                     }
1757                 }
1758               break;
1759             case GTK_POS_LEFT:
1760             case GTK_POS_RIGHT:
1761               rectangle->width = visible_page->requisition.width;
1762               rectangle->height = allocation.height - 2 * border_width;
1763               if (tab_pos == GTK_POS_RIGHT)
1764                 rectangle->x += allocation.width - 2 * border_width - rectangle->width;
1765
1766               for (i = 0; i < N_ACTION_WIDGETS; i++)
1767                 {
1768                   if (priv->action_widget[i] &&
1769                       gtk_widget_get_visible (priv->action_widget[i]))
1770                     {
1771                       gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
1772
1773                       rectangle->height -= action_allocation.height;
1774
1775                       if (i == ACTION_WIDGET_START)
1776                         rectangle->y += action_allocation.height;
1777                     }
1778                 }
1779               break;
1780             }
1781         }
1782
1783       return TRUE;
1784     }
1785   else
1786     {
1787       if (rectangle)
1788         {
1789           rectangle->x = rectangle->y = 0;
1790           rectangle->width = rectangle->height = 10;
1791         }
1792     }
1793
1794   return FALSE;
1795 }
1796
1797 static void
1798 gtk_notebook_map (GtkWidget *widget)
1799 {
1800   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1801   GtkNotebookPrivate *priv = notebook->priv;
1802   GtkNotebookPage *page;
1803   GList *children;
1804   gint i;
1805
1806   gtk_widget_set_mapped (widget, TRUE);
1807
1808   if (priv->cur_page &&
1809       gtk_widget_get_visible (priv->cur_page->child) &&
1810       !gtk_widget_get_mapped (priv->cur_page->child))
1811     gtk_widget_map (priv->cur_page->child);
1812
1813   for (i = 0; i < N_ACTION_WIDGETS; i++)
1814     {
1815       if (priv->action_widget[i] &&
1816           gtk_widget_get_visible (priv->action_widget[i]) &&
1817           gtk_widget_get_child_visible (priv->action_widget[i]) &&
1818           !gtk_widget_get_mapped (priv->action_widget[i]))
1819         gtk_widget_map (priv->action_widget[i]);
1820     }
1821
1822   if (priv->scrollable)
1823     gtk_notebook_pages_allocate (notebook);
1824   else
1825     {
1826       children = priv->children;
1827
1828       while (children)
1829         {
1830           page = children->data;
1831           children = children->next;
1832
1833           if (page->tab_label &&
1834               gtk_widget_get_visible (page->tab_label) &&
1835               !gtk_widget_get_mapped (page->tab_label))
1836             gtk_widget_map (page->tab_label);
1837         }
1838     }
1839
1840   if (gtk_notebook_get_event_window_position (notebook, NULL))
1841     gdk_window_show_unraised (priv->event_window);
1842 }
1843
1844 static void
1845 gtk_notebook_unmap (GtkWidget *widget)
1846 {
1847   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1848   GtkNotebookPrivate *priv = notebook->priv;
1849
1850   stop_scrolling (notebook);
1851
1852   gtk_widget_set_mapped (widget, FALSE);
1853
1854   gdk_window_hide (priv->event_window);
1855
1856   GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unmap (widget);
1857 }
1858
1859 static void
1860 gtk_notebook_realize (GtkWidget *widget)
1861 {
1862   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1863   GtkNotebookPrivate *priv = notebook->priv;
1864   GdkWindow *window;
1865   GdkWindowAttr attributes;
1866   gint attributes_mask;
1867   GdkRectangle event_window_pos;
1868
1869   gtk_widget_set_realized (widget, TRUE);
1870
1871   gtk_notebook_get_event_window_position (notebook, &event_window_pos);
1872
1873   window = gtk_widget_get_parent_window (widget);
1874   gtk_widget_set_window (widget, window);
1875   g_object_ref (window);
1876
1877   attributes.window_type = GDK_WINDOW_CHILD;
1878   attributes.x = event_window_pos.x;
1879   attributes.y = event_window_pos.y;
1880   attributes.width = event_window_pos.width;
1881   attributes.height = event_window_pos.height;
1882   attributes.wclass = GDK_INPUT_ONLY;
1883   attributes.event_mask = gtk_widget_get_events (widget);
1884   attributes.event_mask |= (GDK_BUTTON_PRESS_MASK |
1885                             GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK |
1886                             GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1887   attributes_mask = GDK_WA_X | GDK_WA_Y;
1888
1889   priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
1890                                            &attributes, attributes_mask);
1891   gdk_window_set_user_data (priv->event_window, notebook);
1892
1893   gtk_widget_style_attach (widget);
1894 }
1895
1896 static void
1897 gtk_notebook_unrealize (GtkWidget *widget)
1898 {
1899   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1900   GtkNotebookPrivate *priv = notebook->priv;
1901
1902   gdk_window_set_user_data (priv->event_window, NULL);
1903   gdk_window_destroy (priv->event_window);
1904   priv->event_window = NULL;
1905
1906   if (priv->drag_window)
1907     {
1908       gdk_window_set_user_data (priv->drag_window, NULL);
1909       gdk_window_destroy (priv->drag_window);
1910       priv->drag_window = NULL;
1911     }
1912
1913   GTK_WIDGET_CLASS (gtk_notebook_parent_class)->unrealize (widget);
1914 }
1915
1916 static void
1917 gtk_notebook_size_request (GtkWidget      *widget,
1918                            GtkRequisition *requisition)
1919 {
1920   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
1921   GtkNotebookPrivate *priv = notebook->priv;
1922   GtkNotebookPage *page;
1923   GList *children;
1924   GtkRequisition child_requisition;
1925   GtkRequisition action_widget_requisition[2] = { { 0 }, { 0 } };
1926   gboolean switch_page = FALSE;
1927   gint vis_pages;
1928   gint focus_width;
1929   gint tab_overlap;
1930   gint tab_curvature;
1931   gint arrow_spacing;
1932   gint scroll_arrow_hlength;
1933   gint scroll_arrow_vlength;
1934   guint border_width;
1935
1936   gtk_widget_style_get (widget,
1937                         "focus-line-width", &focus_width,
1938                         "tab-overlap", &tab_overlap,
1939                         "tab-curvature", &tab_curvature,
1940                         "arrow-spacing", &arrow_spacing,
1941                         "scroll-arrow-hlength", &scroll_arrow_hlength,
1942                         "scroll-arrow-vlength", &scroll_arrow_vlength,
1943                         NULL);
1944
1945   requisition->width = 0;
1946   requisition->height = 0;
1947
1948   for (children = priv->children, vis_pages = 0; children;
1949        children = children->next)
1950     {
1951       GtkWidget *parent;
1952       page = children->data;
1953
1954       if (gtk_widget_get_visible (page->child))
1955         {
1956           vis_pages++;
1957           gtk_widget_get_preferred_size (page->child,
1958                                          &child_requisition, NULL);
1959
1960           requisition->width = MAX (requisition->width,
1961                                            child_requisition.width);
1962           requisition->height = MAX (requisition->height,
1963                                             child_requisition.height);
1964
1965           if (priv->menu && page->menu_label)
1966             {
1967               parent = gtk_widget_get_parent (page->menu_label);
1968               if (parent && !gtk_widget_get_visible (parent))
1969                 gtk_widget_show (parent);
1970             }
1971         }
1972       else
1973         {
1974           if (page == priv->cur_page)
1975             switch_page = TRUE;
1976
1977           if (priv->menu && page->menu_label)
1978             {
1979               parent = gtk_widget_get_parent (page->menu_label);
1980               if (parent && gtk_widget_get_visible (parent))
1981                 gtk_widget_hide (parent);
1982             }
1983         }
1984     }
1985
1986   if (priv->show_border || priv->show_tabs)
1987     {
1988       GtkStyle *style;
1989
1990       style = gtk_widget_get_style (widget);
1991
1992       requisition->width += style->xthickness * 2;
1993       requisition->height += style->ythickness * 2;
1994
1995       if (priv->show_tabs)
1996         {
1997           gint tab_width = 0;
1998           gint tab_height = 0;
1999           gint tab_max = 0;
2000           gint padding;
2001           gint i;
2002           gint action_width = 0;
2003           gint action_height = 0;
2004           
2005           for (children = priv->children; children;
2006                children = children->next)
2007             {
2008               page = children->data;
2009               
2010               if (gtk_widget_get_visible (page->child))
2011                 {
2012                   if (!gtk_widget_get_visible (page->tab_label))
2013                     gtk_widget_show (page->tab_label);
2014
2015                   gtk_widget_get_preferred_size (page->tab_label,
2016                                                  &child_requisition, NULL);
2017
2018                   page->requisition.width = child_requisition.width + 2 * style->xthickness;
2019                   page->requisition.height = child_requisition.height + 2 * style->ythickness;
2020
2021                   switch (priv->tab_pos)
2022                     {
2023                     case GTK_POS_TOP:
2024                     case GTK_POS_BOTTOM:
2025                       page->requisition.height += 2 * (priv->tab_vborder +
2026                                                        focus_width);
2027                       tab_height = MAX (tab_height, page->requisition.height);
2028                       tab_max = MAX (tab_max, page->requisition.width);
2029                       break;
2030                     case GTK_POS_LEFT:
2031                     case GTK_POS_RIGHT:
2032                       page->requisition.width += 2 * (priv->tab_hborder +
2033                                                       focus_width);
2034                       tab_width = MAX (tab_width, page->requisition.width);
2035                       tab_max = MAX (tab_max, page->requisition.height);
2036                       break;
2037                     }
2038                 }
2039               else if (gtk_widget_get_visible (page->tab_label))
2040                 gtk_widget_hide (page->tab_label);
2041             }
2042
2043           children = priv->children;
2044
2045           if (vis_pages)
2046             {
2047               for (i = 0; i < N_ACTION_WIDGETS; i++)
2048                 {
2049                   if (priv->action_widget[i])
2050                     {
2051                       gtk_widget_get_preferred_size (priv->action_widget[i],
2052                                                      &action_widget_requisition[i], NULL);
2053                       action_widget_requisition[i].width += style->xthickness;
2054                       action_widget_requisition[i].height += style->ythickness;
2055                     }
2056                 }
2057
2058               switch (priv->tab_pos)
2059                 {
2060                 case GTK_POS_TOP:
2061                 case GTK_POS_BOTTOM:
2062                   if (tab_height == 0)
2063                     break;
2064
2065                   if (priv->scrollable && vis_pages > 1 &&
2066                       requisition->width < tab_width)
2067                     tab_height = MAX (tab_height, scroll_arrow_hlength);
2068
2069                   tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_START].height);
2070                   tab_height = MAX (tab_height, action_widget_requisition[ACTION_WIDGET_END].height);
2071
2072                   padding = 2 * (tab_curvature + focus_width +
2073                                  priv->tab_hborder) - tab_overlap;
2074                   tab_max += padding;
2075                   while (children)
2076                     {
2077                       page = children->data;
2078                       children = children->next;
2079                   
2080                       if (!gtk_widget_get_visible (page->child))
2081                         continue;
2082
2083                       if (priv->homogeneous)
2084                         page->requisition.width = tab_max;
2085                       else
2086                         page->requisition.width += padding;
2087
2088                       tab_width += page->requisition.width;
2089                       page->requisition.height = tab_height;
2090                     }
2091
2092                   if (priv->scrollable && vis_pages > 1 &&
2093                       requisition->width < tab_width)
2094                     tab_width = tab_max + 2 * (scroll_arrow_hlength + arrow_spacing);
2095
2096                   action_width += action_widget_requisition[ACTION_WIDGET_START].width;
2097                   action_width += action_widget_requisition[ACTION_WIDGET_END].width;
2098                   if (priv->homogeneous && !priv->scrollable)
2099                     requisition->width = MAX (requisition->width,
2100                                                      vis_pages * tab_max +
2101                                                      tab_overlap + action_width);
2102                   else
2103                     requisition->width = MAX (requisition->width,
2104                                                      tab_width + tab_overlap + action_width);
2105
2106                   requisition->height += tab_height;
2107                   break;
2108                 case GTK_POS_LEFT:
2109                 case GTK_POS_RIGHT:
2110                   if (tab_width == 0)
2111                     break;
2112
2113                   if (priv->scrollable && vis_pages > 1 &&
2114                       requisition->height < tab_height)
2115                     tab_width = MAX (tab_width,
2116                                      arrow_spacing + 2 * scroll_arrow_vlength);
2117
2118                   tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_START].width);
2119                   tab_width = MAX (tab_width, action_widget_requisition[ACTION_WIDGET_END].width);
2120
2121                   padding = 2 * (tab_curvature + focus_width +
2122                                  priv->tab_vborder) - tab_overlap;
2123                   tab_max += padding;
2124
2125                   while (children)
2126                     {
2127                       page = children->data;
2128                       children = children->next;
2129
2130                       if (!gtk_widget_get_visible (page->child))
2131                         continue;
2132
2133                       page->requisition.width = tab_width;
2134
2135                       if (priv->homogeneous)
2136                         page->requisition.height = tab_max;
2137                       else
2138                         page->requisition.height += padding;
2139
2140                       tab_height += page->requisition.height;
2141                     }
2142
2143                   if (priv->scrollable && vis_pages > 1 &&
2144                       requisition->height < tab_height)
2145                     tab_height = tab_max + (2 * scroll_arrow_vlength + arrow_spacing);
2146                   action_height += action_widget_requisition[ACTION_WIDGET_START].height;
2147                   action_height += action_widget_requisition[ACTION_WIDGET_END].height;
2148
2149                   if (priv->homogeneous && !priv->scrollable)
2150                     requisition->height =
2151                       MAX (requisition->height,
2152                            vis_pages * tab_max + tab_overlap + action_height);
2153                   else
2154                     requisition->height =
2155                       MAX (requisition->height,
2156                            tab_height + tab_overlap + action_height);
2157
2158                   if (!priv->homogeneous || priv->scrollable)
2159                     vis_pages = 1;
2160                   requisition->height = MAX (requisition->height,
2161                                              vis_pages * tab_max +
2162                                              tab_overlap);
2163
2164                   requisition->width += tab_width;
2165                   break;
2166                 }
2167             }
2168         }
2169       else
2170         {
2171           for (children = priv->children; children;
2172                children = children->next)
2173             {
2174               page = children->data;
2175               
2176               if (page->tab_label && gtk_widget_get_visible (page->tab_label))
2177                 gtk_widget_hide (page->tab_label);
2178             }
2179         }
2180     }
2181
2182   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2183
2184   requisition->width += border_width * 2;
2185   requisition->height += border_width * 2;
2186
2187   if (switch_page)
2188     {
2189       if (vis_pages)
2190         {
2191           for (children = priv->children; children;
2192                children = children->next)
2193             {
2194               page = children->data;
2195               if (gtk_widget_get_visible (page->child))
2196                 {
2197                   gtk_notebook_switch_page (notebook, page);
2198                   break;
2199                 }
2200             }
2201         }
2202       else if (gtk_widget_get_visible (widget))
2203         {
2204           requisition->width  = border_width * 2;
2205           requisition->height = border_width * 2;
2206         }
2207     }
2208   if (vis_pages && !priv->cur_page)
2209     {
2210       children = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
2211       if (children)
2212         {
2213           priv->first_tab = children;
2214           gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (children));
2215         }
2216     }
2217 }
2218
2219
2220 static void
2221 gtk_notebook_get_preferred_width (GtkWidget *widget,
2222                                   gint      *minimum,
2223                                   gint      *natural)
2224 {
2225   GtkRequisition requisition;
2226
2227   gtk_notebook_size_request (widget, &requisition);
2228
2229   *minimum = *natural = requisition.width;
2230 }
2231
2232 static void
2233 gtk_notebook_get_preferred_height (GtkWidget *widget,
2234                                    gint      *minimum,
2235                                    gint      *natural)
2236 {
2237   GtkRequisition requisition;
2238
2239   gtk_notebook_size_request (widget, &requisition);
2240
2241   *minimum = *natural = requisition.height;
2242 }
2243
2244 static void
2245 gtk_notebook_size_allocate (GtkWidget     *widget,
2246                             GtkAllocation *allocation)
2247 {
2248   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2249   GtkNotebookPrivate *priv = notebook->priv;
2250   GtkStyle *style;
2251   gint tab_pos = get_effective_tab_pos (notebook);
2252   gboolean is_rtl;
2253   gint focus_width;
2254
2255   style = gtk_widget_get_style (widget);
2256
2257   gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2258
2259   gtk_widget_set_allocation (widget, allocation);
2260
2261   if (gtk_widget_get_realized (widget))
2262     {
2263       GdkRectangle position;
2264
2265       if (gtk_notebook_get_event_window_position (notebook, &position))
2266         {
2267           gdk_window_move_resize (priv->event_window,
2268                                   position.x, position.y,
2269                                   position.width, position.height);
2270           if (gtk_widget_get_mapped (GTK_WIDGET (notebook)))
2271             gdk_window_show_unraised (priv->event_window);
2272         }
2273       else
2274         gdk_window_hide (priv->event_window);
2275     }
2276
2277   if (priv->children)
2278     {
2279       guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2280       GtkNotebookPage *page;
2281       GtkAllocation child_allocation;
2282       GList *children;
2283       gint i;
2284
2285       child_allocation.x = allocation->x + border_width;
2286       child_allocation.y = allocation->y + border_width;
2287       child_allocation.width = MAX (1, allocation->width - border_width * 2);
2288       child_allocation.height = MAX (1, allocation->height - border_width * 2);
2289
2290       if (priv->show_tabs || priv->show_border)
2291         {
2292           child_allocation.x += style->xthickness;
2293           child_allocation.y += style->ythickness;
2294           child_allocation.width = MAX (1, child_allocation.width - style->xthickness * 2);
2295           child_allocation.height = MAX (1, child_allocation.height - style->ythickness * 2);
2296
2297           if (priv->show_tabs && priv->children && priv->cur_page)
2298             {
2299               switch (tab_pos)
2300                 {
2301                 case GTK_POS_TOP:
2302                   child_allocation.y += priv->cur_page->requisition.height;
2303                 case GTK_POS_BOTTOM:
2304                   child_allocation.height =
2305                     MAX (1, child_allocation.height -
2306                          priv->cur_page->requisition.height);
2307                   break;
2308                 case GTK_POS_LEFT:
2309                   child_allocation.x += priv->cur_page->requisition.width;
2310                 case GTK_POS_RIGHT:
2311                   child_allocation.width =
2312                     MAX (1, child_allocation.width -
2313                          priv->cur_page->requisition.width);
2314                   break;
2315                 }
2316
2317               for (i = 0; i < N_ACTION_WIDGETS; i++)
2318                 {
2319                   GtkAllocation widget_allocation;
2320                   GtkRequisition requisition;
2321                   
2322                   if (!priv->action_widget[i])
2323                     continue;
2324
2325                   widget_allocation.x = allocation->x + border_width;
2326                   widget_allocation.y = allocation->y + border_width;
2327                   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2328
2329                   gtk_widget_get_preferred_size (priv->action_widget[i],
2330                                                  &requisition, NULL);
2331
2332                   switch (tab_pos)
2333                     {
2334                     case GTK_POS_BOTTOM:
2335                       widget_allocation.y += allocation->height - 2 * border_width - priv->cur_page->requisition.height;
2336                       /* fall through */
2337                     case GTK_POS_TOP:
2338                       widget_allocation.width = requisition.width;
2339                       widget_allocation.height = priv->cur_page->requisition.height - style->ythickness;
2340
2341                       if ((i == ACTION_WIDGET_START && is_rtl) ||
2342                           (i == ACTION_WIDGET_END && !is_rtl))
2343                         widget_allocation.x += allocation->width - 2 * border_width - requisition.width;
2344                       if (tab_pos == GTK_POS_TOP) /* no fall through */
2345                           widget_allocation.y += 2 * focus_width;
2346                       break;
2347                     case GTK_POS_RIGHT:
2348                       widget_allocation.x += allocation->width - 2 * border_width - priv->cur_page->requisition.width;
2349                       /* fall through */
2350                     case GTK_POS_LEFT:
2351                       widget_allocation.height = requisition.height;
2352                       widget_allocation.width = priv->cur_page->requisition.width - style->xthickness;
2353
2354                       if (i == ACTION_WIDGET_END)
2355                         widget_allocation.y += allocation->height - 2 * border_width - requisition.height;
2356                       if (tab_pos == GTK_POS_LEFT) /* no fall through */  
2357                         widget_allocation.x += 2 * focus_width;
2358                       break;
2359                     }
2360
2361                   gtk_widget_size_allocate (priv->action_widget[i], &widget_allocation);
2362                 }
2363             }
2364         }
2365
2366       children = priv->children;
2367       while (children)
2368         {
2369           page = children->data;
2370           children = children->next;
2371           
2372           if (gtk_widget_get_visible (page->child))
2373             gtk_widget_size_allocate (page->child, &child_allocation);
2374         }
2375
2376       gtk_notebook_pages_allocate (notebook);
2377     }
2378 }
2379
2380 static gint
2381 gtk_notebook_draw (GtkWidget *widget,
2382                    cairo_t   *cr)
2383 {
2384   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2385   GtkNotebookPrivate *priv = notebook->priv;
2386   GtkAllocation allocation;
2387   GdkWindow *window;
2388   gint i;
2389
2390   gtk_widget_get_allocation (widget, &allocation);
2391
2392   window = gtk_widget_get_window (widget);
2393   if (gtk_cairo_should_draw_window (cr, window))
2394     {
2395       cairo_save (cr);
2396
2397       cairo_translate (cr, -allocation.x, -allocation.y);
2398       gtk_notebook_paint (widget, cr);
2399
2400       cairo_restore (cr);
2401
2402       if (priv->show_tabs)
2403         {
2404           GtkNotebookPage *page;
2405           GList *pages;
2406
2407           for (pages = priv->children; pages; pages = pages->next)
2408             {
2409               page = GTK_NOTEBOOK_PAGE (pages);
2410
2411               if (gtk_widget_get_parent (page->tab_label) == widget)
2412                 gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2413                                               page->tab_label, cr);
2414             }
2415         }
2416
2417       if (priv->cur_page && priv->operation != DRAG_OPERATION_REORDER)
2418         gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2419                                       priv->cur_page->child,
2420                                       cr);
2421       if (priv->show_tabs)
2422       {
2423         for (i = 0; i < N_ACTION_WIDGETS; i++)
2424         {
2425           if (priv->action_widget[i])
2426             gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2427                                           priv->action_widget[i], cr);
2428         }
2429       }
2430     }
2431
2432   if (priv->operation == DRAG_OPERATION_REORDER &&
2433       gtk_cairo_should_draw_window (cr, priv->drag_window))
2434     {
2435       cairo_save (cr);
2436       gtk_cairo_transform_to_window (cr, widget, priv->drag_window);
2437
2438       /* FIXME: This is a workaround to make tabs reordering work better
2439        * with engines with rounded tabs. If the drag window background
2440        * isn't set, the rounded corners would be black.
2441        *
2442        * Ideally, these corners should be made transparent, Either by using
2443        * ARGB visuals or shape windows.
2444        */
2445       gdk_cairo_set_source_color (cr, &gtk_widget_get_style (widget)->bg [GTK_STATE_NORMAL]);
2446       cairo_paint (cr);
2447
2448       gtk_notebook_draw_tab (notebook,
2449                              priv->cur_page,
2450                              cr);
2451
2452       cairo_restore (cr);
2453
2454       gtk_container_propagate_draw (GTK_CONTAINER (notebook),
2455                                     priv->cur_page->tab_label, cr);
2456     }
2457   
2458   return FALSE;
2459 }
2460
2461 static gboolean
2462 gtk_notebook_show_arrows (GtkNotebook *notebook)
2463 {
2464   GtkNotebookPrivate *priv = notebook->priv;
2465   gboolean show_arrow = FALSE;
2466   GList *children;
2467
2468   if (!priv->scrollable)
2469     return FALSE;
2470
2471   children = priv->children;
2472   while (children)
2473     {
2474       GtkNotebookPage *page = children->data;
2475
2476       if (page->tab_label && !gtk_widget_get_child_visible (page->tab_label))
2477         show_arrow = TRUE;
2478
2479       children = children->next;
2480     }
2481
2482   return show_arrow;
2483 }
2484
2485 static void
2486 gtk_notebook_get_arrow_rect (GtkNotebook     *notebook,
2487                              GdkRectangle    *rectangle,
2488                              GtkNotebookArrow arrow)
2489 {
2490   GtkNotebookPrivate *priv = notebook->priv;
2491   GdkRectangle event_window_pos;
2492   gboolean before = ARROW_IS_BEFORE (arrow);
2493   gboolean left = ARROW_IS_LEFT (arrow);
2494
2495   if (gtk_notebook_get_event_window_position (notebook, &event_window_pos))
2496     {
2497       gint scroll_arrow_hlength;
2498       gint scroll_arrow_vlength;
2499
2500       gtk_widget_style_get (GTK_WIDGET (notebook),
2501                             "scroll-arrow-hlength", &scroll_arrow_hlength,
2502                             "scroll-arrow-vlength", &scroll_arrow_vlength,
2503                             NULL);
2504
2505       switch (priv->tab_pos)
2506         {
2507         case GTK_POS_LEFT:
2508         case GTK_POS_RIGHT:
2509           rectangle->width = scroll_arrow_vlength;
2510           rectangle->height = scroll_arrow_vlength;
2511
2512           if ((before && (priv->has_before_previous != priv->has_before_next)) ||
2513               (!before && (priv->has_after_previous != priv->has_after_next)))
2514           rectangle->x = event_window_pos.x + (event_window_pos.width - rectangle->width) / 2;
2515           else if (left)
2516             rectangle->x = event_window_pos.x + event_window_pos.width / 2 - rectangle->width;
2517           else 
2518             rectangle->x = event_window_pos.x + event_window_pos.width / 2;
2519           rectangle->y = event_window_pos.y;
2520           if (!before)
2521             rectangle->y += event_window_pos.height - rectangle->height;
2522           break;
2523
2524         case GTK_POS_TOP:
2525         case GTK_POS_BOTTOM:
2526           rectangle->width = scroll_arrow_hlength;
2527           rectangle->height = scroll_arrow_hlength;
2528
2529           if (before)
2530             {
2531               if (left || !priv->has_before_previous)
2532                 rectangle->x = event_window_pos.x;
2533               else
2534                 rectangle->x = event_window_pos.x + rectangle->width;
2535             }
2536           else
2537             {
2538               if (!left || !priv->has_after_next)
2539                 rectangle->x = event_window_pos.x + event_window_pos.width - rectangle->width;
2540               else
2541                 rectangle->x = event_window_pos.x + event_window_pos.width - 2 * rectangle->width;
2542             }
2543           rectangle->y = event_window_pos.y + (event_window_pos.height - rectangle->height) / 2;
2544           break;
2545         }
2546     }
2547 }
2548
2549 static GtkNotebookArrow
2550 gtk_notebook_get_arrow (GtkNotebook *notebook,
2551                         gint         x,
2552                         gint         y)
2553 {
2554   GtkNotebookPrivate *priv = notebook->priv;
2555   GdkRectangle arrow_rect;
2556   GdkRectangle event_window_pos;
2557   gint i;
2558   gint x0, y0;
2559   GtkNotebookArrow arrow[4];
2560
2561   arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
2562   arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
2563   arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
2564   arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
2565
2566   if (gtk_notebook_show_arrows (notebook))
2567     {
2568       gtk_notebook_get_event_window_position (notebook, &event_window_pos);
2569       for (i = 0; i < 4; i++) 
2570         { 
2571           if (arrow[i] == ARROW_NONE)
2572             continue;
2573       
2574           gtk_notebook_get_arrow_rect (notebook, &arrow_rect, arrow[i]);
2575       
2576           x0 = x - arrow_rect.x;
2577           y0 = y - arrow_rect.y;
2578
2579           if (y0 >= 0 && y0 < arrow_rect.height &&
2580               x0 >= 0 && x0 < arrow_rect.width)
2581             return arrow[i];
2582         }
2583     }
2584
2585   return ARROW_NONE;
2586 }
2587
2588 static void
2589 gtk_notebook_do_arrow (GtkNotebook     *notebook,
2590                        GtkNotebookArrow arrow)
2591 {
2592   GtkNotebookPrivate *priv = notebook->priv;
2593   GtkWidget *widget = GTK_WIDGET (notebook);
2594   gboolean is_rtl, left;
2595
2596   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2597   left = (ARROW_IS_LEFT (arrow) && !is_rtl) || 
2598          (!ARROW_IS_LEFT (arrow) && is_rtl);
2599
2600   if (!priv->focus_tab ||
2601       gtk_notebook_search_page (notebook, priv->focus_tab,
2602                                 left ? STEP_PREV : STEP_NEXT,
2603                                 TRUE))
2604     {
2605       gtk_notebook_change_current_page (notebook, left ? -1 : 1);
2606       gtk_widget_grab_focus (widget);
2607     }
2608 }
2609
2610 static gboolean
2611 gtk_notebook_arrow_button_press (GtkNotebook      *notebook,
2612                                  GtkNotebookArrow  arrow,
2613                                  gint              button)
2614 {
2615   GtkNotebookPrivate *priv = notebook->priv;
2616   GtkWidget *widget = GTK_WIDGET (notebook);
2617   gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2618   gboolean left = (ARROW_IS_LEFT (arrow) && !is_rtl) || 
2619                   (!ARROW_IS_LEFT (arrow) && is_rtl);
2620
2621   if (!gtk_widget_has_focus (widget))
2622     gtk_widget_grab_focus (widget);
2623
2624   priv->button = button;
2625   priv->click_child = arrow;
2626
2627   if (button == 1)
2628     {
2629       gtk_notebook_do_arrow (notebook, arrow);
2630       gtk_notebook_set_scroll_timer (notebook);
2631     }
2632   else if (button == 2)
2633     gtk_notebook_page_select (notebook, TRUE);
2634   else if (button == 3)
2635     gtk_notebook_switch_focus_tab (notebook,
2636                                    gtk_notebook_search_page (notebook,
2637                                                              NULL,
2638                                                              left ? STEP_NEXT : STEP_PREV,
2639                                                              TRUE));
2640   gtk_notebook_redraw_arrows (notebook);
2641
2642   return TRUE;
2643 }
2644
2645 static gboolean
2646 get_widget_coordinates (GtkWidget *widget,
2647                         GdkEvent  *event,
2648                         gint      *x,
2649                         gint      *y)
2650 {
2651   GdkWindow *window = ((GdkEventAny *)event)->window;
2652   gdouble tx, ty;
2653
2654   if (!gdk_event_get_coords (event, &tx, &ty))
2655     return FALSE;
2656
2657   while (window && window != gtk_widget_get_window (widget))
2658     {
2659       gint window_x, window_y;
2660
2661       gdk_window_get_position (window, &window_x, &window_y);
2662       tx += window_x;
2663       ty += window_y;
2664
2665       window = gdk_window_get_parent (window);
2666     }
2667
2668   if (window)
2669     {
2670       *x = tx;
2671       *y = ty;
2672
2673       return TRUE;
2674     }
2675   else
2676     return FALSE;
2677 }
2678
2679 static GList*
2680 get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
2681 {
2682    GtkNotebookPrivate *priv = notebook->priv;
2683   GtkNotebookPage *page;
2684   GList *children;
2685
2686   children = priv->children;
2687   while (children)
2688     {
2689       page = children->data;
2690       
2691       if (gtk_widget_get_visible (page->child) &&
2692           page->tab_label && gtk_widget_get_mapped (page->tab_label) &&
2693           (x >= page->allocation.x) &&
2694           (y >= page->allocation.y) &&
2695           (x <= (page->allocation.x + page->allocation.width)) &&
2696           (y <= (page->allocation.y + page->allocation.height)))
2697         return children;
2698
2699       children = children->next;
2700     }
2701
2702   return NULL;
2703 }
2704
2705 static gboolean
2706 gtk_notebook_button_press (GtkWidget      *widget,
2707                            GdkEventButton *event)
2708 {
2709   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2710   GtkNotebookPrivate *priv = notebook->priv;
2711   GtkNotebookPage *page;
2712   GList *tab;
2713   GtkNotebookArrow arrow;
2714   gint x, y;
2715
2716   if (event->type != GDK_BUTTON_PRESS || !priv->children ||
2717       priv->button)
2718     return FALSE;
2719
2720   if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
2721     return FALSE;
2722
2723   arrow = gtk_notebook_get_arrow (notebook, x, y);
2724   if (arrow)
2725     return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
2726
2727   if (event->button == 3 && priv->menu)
2728     {
2729       gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2730                       NULL, NULL, 3, event->time);
2731       return TRUE;
2732     }
2733
2734   if (event->button != 1)
2735     return FALSE;
2736
2737   priv->button = event->button;
2738
2739   if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
2740     {
2741       gboolean page_changed, was_focus;
2742
2743       page = tab->data;
2744       page_changed = page != priv->cur_page;
2745       was_focus = gtk_widget_is_focus (widget);
2746
2747       gtk_notebook_switch_focus_tab (notebook, tab);
2748       gtk_widget_grab_focus (widget);
2749
2750       if (page_changed && !was_focus)
2751         gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
2752
2753       /* save press to possibly begin a drag */
2754       if (page->reorderable || page->detachable)
2755         {
2756           priv->during_detach = FALSE;
2757           priv->during_reorder = FALSE;
2758           priv->pressed_button = event->button;
2759
2760           priv->mouse_x = x;
2761           priv->mouse_y = y;
2762
2763           priv->drag_begin_x = priv->mouse_x;
2764           priv->drag_begin_y = priv->mouse_y;
2765           priv->drag_offset_x = priv->drag_begin_x - page->allocation.x;
2766           priv->drag_offset_y = priv->drag_begin_y - page->allocation.y;
2767         }
2768     }
2769
2770   return TRUE;
2771 }
2772
2773 static void
2774 popup_position_func (GtkMenu  *menu,
2775                      gint     *x,
2776                      gint     *y,
2777                      gboolean *push_in,
2778                      gpointer  data)
2779 {
2780   GtkNotebook *notebook = data;
2781   GtkNotebookPrivate *priv = notebook->priv;
2782   GtkAllocation allocation;
2783   GtkWidget *w;
2784   GtkRequisition requisition;
2785
2786   if (priv->focus_tab)
2787     {
2788       GtkNotebookPage *page;
2789
2790       page = priv->focus_tab->data;
2791       w = page->tab_label;
2792     }
2793   else
2794    {
2795      w = GTK_WIDGET (notebook);
2796    }
2797
2798   gdk_window_get_origin (gtk_widget_get_window (w), x, y);
2799
2800   gtk_widget_get_allocation (w, &allocation);
2801   gtk_widget_get_preferred_size (GTK_WIDGET (menu),
2802                                  &requisition, NULL);
2803
2804   if (gtk_widget_get_direction (w) == GTK_TEXT_DIR_RTL)
2805     *x += allocation.x + allocation.width - requisition.width;
2806   else
2807     *x += allocation.x;
2808
2809   *y += allocation.y + allocation.height;
2810
2811   *push_in = FALSE;
2812 }
2813
2814 static gboolean
2815 gtk_notebook_popup_menu (GtkWidget *widget)
2816 {
2817   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
2818   GtkNotebookPrivate *priv = notebook->priv;
2819
2820   if (priv->menu)
2821     {
2822       gtk_menu_popup (GTK_MENU (priv->menu), NULL, NULL,
2823                       popup_position_func, notebook,
2824                       0, gtk_get_current_event_time ());
2825       gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->menu), FALSE);
2826       return TRUE;
2827     }
2828
2829   return FALSE;
2830 }
2831
2832 static void 
2833 stop_scrolling (GtkNotebook *notebook)
2834 {
2835   GtkNotebookPrivate *priv = notebook->priv;
2836
2837   if (priv->timer)
2838     {
2839       g_source_remove (priv->timer);
2840       priv->timer = 0;
2841       priv->need_timer = FALSE;
2842     }
2843   priv->click_child = 0;
2844   priv->button = 0;
2845   gtk_notebook_redraw_arrows (notebook);
2846 }
2847
2848 static GList*
2849 get_drop_position (GtkNotebook *notebook,
2850                    guint        pack)
2851 {
2852   GtkNotebookPrivate *priv = notebook->priv;
2853   GList *children, *last_child;
2854   GtkNotebookPage *page;
2855   gboolean is_rtl;
2856   gint x, y;
2857
2858   x = priv->mouse_x;
2859   y = priv->mouse_y;
2860
2861   is_rtl = gtk_widget_get_direction ((GtkWidget *) notebook) == GTK_TEXT_DIR_RTL;
2862   children = priv->children;
2863   last_child = NULL;
2864
2865   while (children)
2866     {
2867       page = children->data;
2868
2869       if ((priv->operation != DRAG_OPERATION_REORDER || page != priv->cur_page) &&
2870           gtk_widget_get_visible (page->child) &&
2871           page->tab_label &&
2872           gtk_widget_get_mapped (page->tab_label) &&
2873           page->pack == pack)
2874         {
2875           switch (priv->tab_pos)
2876             {
2877             case GTK_POS_TOP:
2878             case GTK_POS_BOTTOM:
2879               if (!is_rtl)
2880                 {
2881                   if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_X (page) > x) ||
2882                       (page->pack == GTK_PACK_END && PAGE_MIDDLE_X (page) < x))
2883                     return children;
2884                 }
2885               else
2886                 {
2887                   if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_X (page) < x) ||
2888                       (page->pack == GTK_PACK_END && PAGE_MIDDLE_X (page) > x))
2889                     return children;
2890                 }
2891
2892               break;
2893             case GTK_POS_LEFT:
2894             case GTK_POS_RIGHT:
2895               if ((page->pack == GTK_PACK_START && PAGE_MIDDLE_Y (page) > y) ||
2896                   (page->pack == GTK_PACK_END && PAGE_MIDDLE_Y (page) < y))
2897                 return children;
2898
2899               break;
2900             }
2901
2902           last_child = children->next;
2903         }
2904
2905       children = children->next;
2906     }
2907
2908   return last_child;
2909 }
2910
2911 static void
2912 show_drag_window (GtkNotebook        *notebook,
2913                   GtkNotebookPrivate    *priv,
2914                   GtkNotebookPage    *page,
2915                   GdkDevice          *device)
2916 {
2917   GtkWidget *widget = GTK_WIDGET (notebook);
2918
2919   if (!priv->drag_window)
2920     {
2921       GdkWindowAttr attributes;
2922       guint attributes_mask;
2923
2924       attributes.x = page->allocation.x;
2925       attributes.y = page->allocation.y;
2926       attributes.width = page->allocation.width;
2927       attributes.height = page->allocation.height;
2928       attributes.window_type = GDK_WINDOW_CHILD;
2929       attributes.wclass = GDK_INPUT_OUTPUT;
2930       attributes.visual = gtk_widget_get_visual (widget);
2931       attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK;
2932       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
2933
2934       priv->drag_window = gdk_window_new (gtk_widget_get_parent_window (widget),
2935                                           &attributes,
2936                                           attributes_mask);
2937       gdk_window_set_user_data (priv->drag_window, widget);
2938     }
2939
2940   g_object_ref (page->tab_label);
2941   gtk_widget_unparent (page->tab_label);
2942   gtk_widget_set_parent_window (page->tab_label, priv->drag_window);
2943   gtk_widget_set_parent (page->tab_label, widget);
2944   g_object_unref (page->tab_label);
2945
2946   gdk_window_show (priv->drag_window);
2947
2948   /* the grab will dissapear when the window is hidden */
2949   gdk_device_grab (device, priv->drag_window,
2950                    GDK_OWNERSHIP_WINDOW, FALSE,
2951                    GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
2952                    NULL, GDK_CURRENT_TIME);
2953 }
2954
2955 /* This function undoes the reparenting that happens both when drag_window
2956  * is shown for reordering and when the DnD icon is shown for detaching
2957  */
2958 static void
2959 hide_drag_window (GtkNotebook        *notebook,
2960                   GtkNotebookPrivate    *priv,
2961                   GtkNotebookPage    *page)
2962 {
2963   GtkWidget *widget = GTK_WIDGET (notebook);
2964   GtkWidget *parent = gtk_widget_get_parent (page->tab_label);
2965
2966   if (gtk_widget_get_window (page->tab_label) != gtk_widget_get_window (widget) ||
2967       !NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
2968     {
2969       g_object_ref (page->tab_label);
2970
2971       if (GTK_IS_WINDOW (parent))
2972         {
2973           /* parent widget is the drag window */
2974           gtk_container_remove (GTK_CONTAINER (parent), page->tab_label);
2975         }
2976       else
2977         gtk_widget_unparent (page->tab_label);
2978
2979       gtk_widget_set_parent (page->tab_label, widget);
2980       g_object_unref (page->tab_label);
2981     }
2982
2983   if (priv->drag_window &&
2984       gdk_window_is_visible (priv->drag_window))
2985     gdk_window_hide (priv->drag_window);
2986 }
2987
2988 static void
2989 gtk_notebook_stop_reorder (GtkNotebook *notebook)
2990 {
2991   GtkNotebookPrivate *priv = notebook->priv;
2992   GtkNotebookPage *page;
2993
2994   if (priv->operation == DRAG_OPERATION_DETACH)
2995     page = priv->detached_tab;
2996   else
2997     page = priv->cur_page;
2998
2999   if (!page || !page->tab_label)
3000     return;
3001
3002   priv->pressed_button = -1;
3003
3004   if (page->reorderable || page->detachable)
3005     {
3006       if (priv->during_reorder)
3007         {
3008           gint old_page_num, page_num;
3009           GList *element;
3010
3011           element = get_drop_position (notebook, page->pack);
3012           old_page_num = g_list_position (priv->children, priv->focus_tab);
3013           page_num = reorder_tab (notebook, element, priv->focus_tab);
3014           gtk_notebook_child_reordered (notebook, page);
3015           
3016           if (priv->has_scrolled || old_page_num != page_num)
3017             g_signal_emit (notebook,
3018                            notebook_signals[PAGE_REORDERED], 0,
3019                            page->child, page_num);
3020
3021           priv->has_scrolled = FALSE;
3022           priv->during_reorder = FALSE; 
3023         }
3024
3025       hide_drag_window (notebook, priv, page);
3026
3027       priv->operation = DRAG_OPERATION_NONE;
3028       gtk_notebook_pages_allocate (notebook);
3029
3030       if (priv->dnd_timer)
3031         {
3032           g_source_remove (priv->dnd_timer);
3033           priv->dnd_timer = 0;
3034         }
3035     }
3036 }
3037
3038 static gint
3039 gtk_notebook_button_release (GtkWidget      *widget,
3040                              GdkEventButton *event)
3041 {
3042   GtkNotebook *notebook;
3043   GtkNotebookPrivate *priv;
3044   GtkNotebookPage *page;
3045
3046   if (event->type != GDK_BUTTON_RELEASE)
3047     return FALSE;
3048
3049   notebook = GTK_NOTEBOOK (widget);
3050   priv = notebook->priv;
3051
3052   page = priv->cur_page;
3053
3054   if (!priv->during_detach &&
3055       page->reorderable &&
3056       event->button == priv->pressed_button)
3057     gtk_notebook_stop_reorder (notebook);
3058
3059   if (event->button == priv->button)
3060     {
3061       stop_scrolling (notebook);
3062       return TRUE;
3063     }
3064   else
3065     return FALSE;
3066 }
3067
3068 static gint
3069 gtk_notebook_leave_notify (GtkWidget        *widget,
3070                            GdkEventCrossing *event)
3071 {
3072   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3073   GtkNotebookPrivate *priv = notebook->priv;
3074   gint x, y;
3075
3076   if (!get_widget_coordinates (widget, (GdkEvent *)event, &x, &y))
3077     return FALSE;
3078
3079   if (priv->in_child)
3080     {
3081       priv->in_child = 0;
3082       gtk_notebook_redraw_arrows (notebook);
3083     }
3084
3085   return TRUE;
3086 }
3087
3088 static GtkNotebookPointerPosition
3089 get_pointer_position (GtkNotebook *notebook)
3090 {
3091   GtkNotebookPrivate *priv = notebook->priv;
3092   GtkWidget *widget = GTK_WIDGET (notebook);
3093   gint wx, wy, width, height;
3094   gboolean is_rtl;
3095
3096   if (!priv->scrollable)
3097     return POINTER_BETWEEN;
3098
3099   gdk_window_get_position (priv->event_window, &wx, &wy);
3100   width = gdk_window_get_width (priv->event_window);
3101   height = gdk_window_get_height (priv->event_window);
3102
3103   if (priv->tab_pos == GTK_POS_TOP ||
3104       priv->tab_pos == GTK_POS_BOTTOM)
3105     {
3106       gint x;
3107
3108       is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
3109       x = priv->mouse_x - wx;
3110
3111       if (x > width - SCROLL_THRESHOLD)
3112         return (is_rtl) ? POINTER_BEFORE : POINTER_AFTER;
3113       else if (x < SCROLL_THRESHOLD)
3114         return (is_rtl) ? POINTER_AFTER : POINTER_BEFORE;
3115       else
3116         return POINTER_BETWEEN;
3117     }
3118   else
3119     {
3120       gint y;
3121
3122       y = priv->mouse_y - wy;
3123       if (y > height - SCROLL_THRESHOLD)
3124         return POINTER_AFTER;
3125       else if (y < SCROLL_THRESHOLD)
3126         return POINTER_BEFORE;
3127       else
3128         return POINTER_BETWEEN;
3129     }
3130 }
3131
3132 static gboolean
3133 scroll_notebook_timer (gpointer data)
3134 {
3135   GtkNotebook *notebook = GTK_NOTEBOOK (data);
3136   GtkNotebookPrivate *priv = notebook->priv;
3137   GtkNotebookPointerPosition pointer_position;
3138   GList *element, *first_tab;
3139
3140   pointer_position = get_pointer_position (notebook);
3141
3142   element = get_drop_position (notebook, priv->cur_page->pack);
3143   reorder_tab (notebook, element, priv->focus_tab);
3144   first_tab = gtk_notebook_search_page (notebook, priv->first_tab,
3145                                         (pointer_position == POINTER_BEFORE) ? STEP_PREV : STEP_NEXT,
3146                                         TRUE);
3147   if (first_tab)
3148     {
3149       priv->first_tab = first_tab;
3150       gtk_notebook_pages_allocate (notebook);
3151
3152       gdk_window_move_resize (priv->drag_window,
3153                               priv->drag_window_x,
3154                               priv->drag_window_y,
3155                               priv->cur_page->allocation.width,
3156                               priv->cur_page->allocation.height);
3157       gdk_window_raise (priv->drag_window);
3158     }
3159
3160   return TRUE;
3161 }
3162
3163 static gboolean
3164 check_threshold (GtkNotebook *notebook,
3165                  gint         current_x,
3166                  gint         current_y)
3167 {
3168   GtkNotebookPrivate *priv = notebook->priv;
3169   GtkWidget *widget;
3170   gint dnd_threshold;
3171   GdkRectangle rectangle = { 0, }; /* shut up gcc */
3172   GtkSettings *settings;
3173   
3174   widget = GTK_WIDGET (notebook);
3175   settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3176   g_object_get (G_OBJECT (settings), "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
3177
3178   /* we want a large threshold */
3179   dnd_threshold *= DND_THRESHOLD_MULTIPLIER;
3180
3181   gdk_window_get_position (priv->event_window, &rectangle.x, &rectangle.y);
3182   rectangle.width = gdk_window_get_width (priv->event_window);
3183   rectangle.height = gdk_window_get_height (priv->event_window);
3184
3185   rectangle.x -= dnd_threshold;
3186   rectangle.width += 2 * dnd_threshold;
3187   rectangle.y -= dnd_threshold;
3188   rectangle.height += 2 * dnd_threshold;
3189
3190   return (current_x < rectangle.x ||
3191           current_x > rectangle.x + rectangle.width ||
3192           current_y < rectangle.y ||
3193           current_y > rectangle.y + rectangle.height);
3194 }
3195
3196 static gint
3197 gtk_notebook_motion_notify (GtkWidget      *widget,
3198                             GdkEventMotion *event)
3199 {
3200   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3201   GtkNotebookPrivate *priv = notebook->priv;
3202   GtkNotebookPage *page;
3203   GtkNotebookArrow arrow;
3204   GtkNotebookPointerPosition pointer_position;
3205   GtkSettings *settings;
3206   guint timeout;
3207   gint x_win, y_win;
3208
3209   page = priv->cur_page;
3210
3211   if (!page)
3212     return FALSE;
3213
3214   if (!(event->state & GDK_BUTTON1_MASK) &&
3215       priv->pressed_button != -1)
3216     {
3217       gtk_notebook_stop_reorder (notebook);
3218       stop_scrolling (notebook);
3219     }
3220
3221   if (event->time < priv->timestamp + MSECS_BETWEEN_UPDATES)
3222     return FALSE;
3223
3224   priv->timestamp = event->time;
3225
3226   /* While animating the move, event->x is relative to the flying tab
3227    * (priv->drag_window has a pointer grab), but we need coordinates relative to
3228    * the notebook widget.
3229    */
3230   gdk_window_get_origin (gtk_widget_get_window (widget), &x_win, &y_win);
3231   priv->mouse_x = event->x_root - x_win;
3232   priv->mouse_y = event->y_root - y_win;
3233
3234   arrow = gtk_notebook_get_arrow (notebook, priv->mouse_x, priv->mouse_y);
3235   if (arrow != priv->in_child)
3236     {
3237       priv->in_child = arrow;
3238       gtk_notebook_redraw_arrows (notebook);
3239     }
3240
3241   if (priv->pressed_button == -1)
3242     return FALSE;
3243
3244   if (page->detachable &&
3245       check_threshold (notebook, priv->mouse_x, priv->mouse_y))
3246     {
3247       priv->detached_tab = priv->cur_page;
3248       priv->during_detach = TRUE;
3249
3250       gtk_drag_begin (widget, priv->source_targets, GDK_ACTION_MOVE,
3251                       priv->pressed_button, (GdkEvent*) event);
3252       return TRUE;
3253     }
3254
3255   if (page->reorderable &&
3256       (priv->during_reorder ||
3257        gtk_drag_check_threshold (widget, priv->drag_begin_x, priv->drag_begin_y, priv->mouse_x, priv->mouse_y)))
3258     {
3259       priv->during_reorder = TRUE;
3260       pointer_position = get_pointer_position (notebook);
3261
3262       if (event->window == priv->drag_window &&
3263           pointer_position != POINTER_BETWEEN &&
3264           gtk_notebook_show_arrows (notebook))
3265         {
3266           /* scroll tabs */
3267           if (!priv->dnd_timer)
3268             {
3269               priv->has_scrolled = TRUE;
3270               settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
3271               g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
3272
3273               priv->dnd_timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
3274                                                scroll_notebook_timer, 
3275                                                (gpointer) notebook);
3276             }
3277         }
3278       else
3279         {
3280           if (priv->dnd_timer)
3281             {
3282               g_source_remove (priv->dnd_timer);
3283               priv->dnd_timer = 0;
3284             }
3285         }
3286
3287       if (event->window == priv->drag_window ||
3288           priv->operation != DRAG_OPERATION_REORDER)
3289         {
3290           /* the drag operation is beginning, create the window */
3291           if (priv->operation != DRAG_OPERATION_REORDER)
3292             {
3293               priv->operation = DRAG_OPERATION_REORDER;
3294               show_drag_window (notebook, priv, page, event->device);
3295             }
3296
3297           gtk_notebook_pages_allocate (notebook);
3298           gdk_window_move_resize (priv->drag_window,
3299                                   priv->drag_window_x,
3300                                   priv->drag_window_y,
3301                                   page->allocation.width,
3302                                   page->allocation.height);
3303         }
3304     }
3305
3306   return TRUE;
3307 }
3308
3309 static void
3310 gtk_notebook_grab_notify (GtkWidget *widget,
3311                           gboolean   was_grabbed)
3312 {
3313   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3314
3315   if (!was_grabbed)
3316     {
3317       gtk_notebook_stop_reorder (notebook);
3318       stop_scrolling (notebook);
3319     }
3320 }
3321
3322 static void
3323 gtk_notebook_state_changed (GtkWidget    *widget,
3324                             GtkStateType  previous_state)
3325 {
3326   if (!gtk_widget_is_sensitive (widget))
3327     stop_scrolling (GTK_NOTEBOOK (widget));
3328 }
3329
3330 static gint
3331 gtk_notebook_focus_in (GtkWidget     *widget,
3332                        GdkEventFocus *event)
3333 {
3334   gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3335   
3336   return FALSE;
3337 }
3338
3339 static gint
3340 gtk_notebook_focus_out (GtkWidget     *widget,
3341                         GdkEventFocus *event)
3342 {
3343   gtk_notebook_redraw_tabs (GTK_NOTEBOOK (widget));
3344
3345   return FALSE;
3346 }
3347
3348 static void
3349 gtk_notebook_style_set  (GtkWidget *widget,
3350                          GtkStyle  *previous)
3351 {
3352   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3353   GtkNotebookPrivate *priv = notebook->priv;
3354
3355   gboolean has_before_previous;
3356   gboolean has_before_next;
3357   gboolean has_after_previous;
3358   gboolean has_after_next;
3359
3360   gtk_widget_style_get (widget,
3361                         "has-backward-stepper", &has_before_previous,
3362                         "has-secondary-forward-stepper", &has_before_next,
3363                         "has-secondary-backward-stepper", &has_after_previous,
3364                         "has-forward-stepper", &has_after_next,
3365                         NULL);
3366
3367   priv->has_before_previous = has_before_previous;
3368   priv->has_before_next = has_before_next;
3369   priv->has_after_previous = has_after_previous;
3370   priv->has_after_next = has_after_next;
3371
3372   GTK_WIDGET_CLASS (gtk_notebook_parent_class)->style_set (widget, previous);
3373 }
3374
3375 static gboolean
3376 on_drag_icon_draw (GtkWidget *widget,
3377                    cairo_t   *cr,
3378                    gpointer   data)
3379 {
3380   GtkWidget *notebook, *child;
3381   GtkRequisition requisition;
3382   gint gap_pos;
3383
3384   notebook = GTK_WIDGET (data);
3385   child = gtk_bin_get_child (GTK_BIN (widget));
3386
3387   gtk_widget_get_preferred_size (widget,
3388                                  &requisition, NULL);
3389   gap_pos = get_tab_gap_pos (GTK_NOTEBOOK (notebook));
3390
3391   gtk_paint_extension (gtk_widget_get_style (notebook),
3392                        cr,
3393                        GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3394                        widget, "tab",
3395                        0, 0,
3396                        requisition.width, requisition.height,
3397                        gap_pos);
3398   if (child)
3399     gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
3400
3401   return TRUE;
3402 }
3403
3404 static void
3405 gtk_notebook_drag_begin (GtkWidget        *widget,
3406                          GdkDragContext   *context)
3407 {
3408   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3409   GtkNotebookPrivate *priv = notebook->priv;
3410   GtkWidget *tab_label;
3411
3412   if (priv->dnd_timer)
3413     {
3414       g_source_remove (priv->dnd_timer);
3415       priv->dnd_timer = 0;
3416     }
3417
3418   priv->operation = DRAG_OPERATION_DETACH;
3419   gtk_notebook_pages_allocate (notebook);
3420
3421   tab_label = priv->detached_tab->tab_label;
3422
3423   hide_drag_window (notebook, priv, priv->cur_page);
3424   g_object_ref (tab_label);
3425   gtk_widget_unparent (tab_label);
3426
3427   priv->dnd_window = gtk_window_new (GTK_WINDOW_POPUP);
3428   gtk_window_set_screen (GTK_WINDOW (priv->dnd_window),
3429                          gtk_widget_get_screen (widget));
3430   gtk_container_add (GTK_CONTAINER (priv->dnd_window), tab_label);
3431   gtk_widget_set_size_request (priv->dnd_window,
3432                                priv->detached_tab->allocation.width,
3433                                priv->detached_tab->allocation.height);
3434   g_object_unref (tab_label);
3435
3436   g_signal_connect (G_OBJECT (priv->dnd_window), "draw",
3437                     G_CALLBACK (on_drag_icon_draw), notebook);
3438
3439   gtk_drag_set_icon_widget (context, priv->dnd_window, -2, -2);
3440 }
3441
3442 static void
3443 gtk_notebook_drag_end (GtkWidget      *widget,
3444                        GdkDragContext *context)
3445 {
3446   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3447   GtkNotebookPrivate *priv = notebook->priv;
3448
3449   gtk_notebook_stop_reorder (notebook);
3450
3451   if (priv->detached_tab)
3452     gtk_notebook_switch_page (notebook, priv->detached_tab);
3453
3454   _gtk_bin_set_child (GTK_BIN (priv->dnd_window), NULL);
3455   gtk_widget_destroy (priv->dnd_window);
3456   priv->dnd_window = NULL;
3457
3458   priv->operation = DRAG_OPERATION_NONE;
3459 }
3460
3461 static GtkNotebook *
3462 gtk_notebook_create_window (GtkNotebook *notebook,
3463                             GtkWidget   *page,
3464                             gint         x,
3465                             gint         y)
3466 {
3467   return NULL;
3468 }
3469
3470 static gboolean
3471 gtk_notebook_drag_failed (GtkWidget      *widget,
3472                           GdkDragContext *context,
3473                           GtkDragResult   result,
3474                           gpointer        data)
3475 {
3476   if (result == GTK_DRAG_RESULT_NO_TARGET)
3477     {
3478       GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3479       GtkNotebookPrivate *priv = notebook->priv;
3480       GtkNotebook *dest_notebook = NULL;
3481       GdkDisplay *display;
3482       gint x, y;
3483
3484       display = gtk_widget_get_display (widget);
3485       gdk_display_get_pointer (display, NULL, &x, &y, NULL);
3486
3487       g_signal_emit (notebook, notebook_signals[CREATE_WINDOW], 0,
3488                      priv->detached_tab->child, x, y, &dest_notebook);
3489
3490       if (dest_notebook)
3491         do_detach_tab (notebook, dest_notebook, priv->detached_tab->child, 0, 0);
3492
3493       return TRUE;
3494     }
3495
3496   return FALSE;
3497 }
3498
3499 static gboolean
3500 gtk_notebook_switch_tab_timeout (gpointer data)
3501 {
3502   GtkNotebook *notebook = GTK_NOTEBOOK (data);
3503   GtkNotebookPrivate *priv = notebook->priv;
3504   GList *tab;
3505   gint x, y;
3506
3507   priv->switch_tab_timer = 0;
3508   x = priv->mouse_x;
3509   y = priv->mouse_y;
3510
3511   if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
3512     {
3513       /* FIXME: hack, we don't want the
3514        * focus to move fom the source widget
3515        */
3516       priv->child_has_focus = FALSE;
3517       gtk_notebook_switch_focus_tab (notebook, tab);
3518     }
3519
3520   return FALSE;
3521 }
3522
3523 static gboolean
3524 gtk_notebook_drag_motion (GtkWidget      *widget,
3525                           GdkDragContext *context,
3526                           gint            x,
3527                           gint            y,
3528                           guint           time)
3529 {
3530   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3531   GtkNotebookPrivate *priv = notebook->priv;
3532   GtkAllocation allocation;
3533   GdkRectangle position;
3534   GtkSettings *settings;
3535   GtkNotebookArrow arrow;
3536   guint timeout;
3537   GdkAtom target, tab_target;
3538
3539   gtk_widget_get_allocation (widget, &allocation);
3540
3541   arrow = gtk_notebook_get_arrow (notebook,
3542                                   x + allocation.x,
3543                                   y + allocation.y);
3544   if (arrow)
3545     {
3546       priv->click_child = arrow;
3547       gtk_notebook_set_scroll_timer (notebook);
3548       gdk_drag_status (context, 0, time);
3549       return TRUE;
3550     }
3551
3552   stop_scrolling (notebook);
3553   target = gtk_drag_dest_find_target (widget, context, NULL);
3554   tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3555
3556   if (target == tab_target)
3557     {
3558       GQuark group, source_group;
3559       GtkNotebook *source;
3560       GtkWidget *source_child;
3561
3562       source = GTK_NOTEBOOK (gtk_drag_get_source_widget (context));
3563       source_child = source->priv->cur_page->child;
3564
3565       group = notebook->priv->group;
3566       source_group = source->priv->group;
3567
3568       if (group != 0 && group == source_group &&
3569           !(widget == source_child ||
3570             gtk_widget_is_ancestor (widget, source_child)))
3571         {
3572           gdk_drag_status (context, GDK_ACTION_MOVE, time);
3573           return TRUE;
3574         }
3575       else
3576         {
3577           /* it's a tab, but doesn't share
3578            * ID with this notebook */
3579           gdk_drag_status (context, 0, time);
3580         }
3581     }
3582
3583   x += allocation.x;
3584   y += allocation.y;
3585
3586   if (gtk_notebook_get_event_window_position (notebook, &position) &&
3587       x >= position.x && x <= position.x + position.width &&
3588       y >= position.y && y <= position.y + position.height)
3589     {
3590       priv->mouse_x = x;
3591       priv->mouse_y = y;
3592
3593       if (!priv->switch_tab_timer)
3594         {
3595           settings = gtk_widget_get_settings (widget);
3596
3597           g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
3598           priv->switch_tab_timer = gdk_threads_add_timeout (timeout,
3599                                                   gtk_notebook_switch_tab_timeout,
3600                                                   widget);
3601         }
3602     }
3603   else
3604     {
3605       if (priv->switch_tab_timer)
3606         {
3607           g_source_remove (priv->switch_tab_timer);
3608           priv->switch_tab_timer = 0;
3609         }
3610     }
3611
3612   return (target == tab_target) ? TRUE : FALSE;
3613 }
3614
3615 static void
3616 gtk_notebook_drag_leave (GtkWidget      *widget,
3617                          GdkDragContext *context,
3618                          guint           time)
3619 {
3620   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3621   GtkNotebookPrivate *priv = notebook->priv;
3622
3623   if (priv->switch_tab_timer)
3624     {
3625       g_source_remove (priv->switch_tab_timer);
3626       priv->switch_tab_timer = 0;
3627     }
3628
3629   stop_scrolling (GTK_NOTEBOOK (widget));
3630 }
3631
3632 static gboolean
3633 gtk_notebook_drag_drop (GtkWidget        *widget,
3634                         GdkDragContext   *context,
3635                         gint              x,
3636                         gint              y,
3637                         guint             time)
3638 {
3639   GdkAtom target, tab_target;
3640
3641   target = gtk_drag_dest_find_target (widget, context, NULL);
3642   tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
3643
3644   if (target == tab_target)
3645     {
3646       gtk_drag_get_data (widget, context, target, time);
3647       return TRUE;
3648     }
3649
3650   return FALSE;
3651 }
3652
3653 static void
3654 do_detach_tab (GtkNotebook     *from,
3655                GtkNotebook     *to,
3656                GtkWidget       *child,
3657                gint             x,
3658                gint             y)
3659 {
3660   GtkNotebookPrivate *to_priv = to->priv;
3661   GtkAllocation to_allocation;
3662   GtkWidget *tab_label, *menu_label;
3663   gboolean tab_expand, tab_fill, reorderable, detachable;
3664   GList *element;
3665   guint tab_pack;
3666   gint page_num;
3667
3668   menu_label = gtk_notebook_get_menu_label (from, child);
3669
3670   if (menu_label)
3671     g_object_ref (menu_label);
3672
3673   tab_label = gtk_notebook_get_tab_label (from, child);
3674   
3675   if (tab_label)
3676     g_object_ref (tab_label);
3677
3678   g_object_ref (child);
3679
3680   gtk_container_child_get (GTK_CONTAINER (from),
3681                            child,
3682                            "tab-expand", &tab_expand,
3683                            "tab-fill", &tab_fill,
3684                            "tab-pack", &tab_pack,
3685                            "reorderable", &reorderable,
3686                            "detachable", &detachable,
3687                            NULL);
3688
3689   gtk_container_remove (GTK_CONTAINER (from), child);
3690
3691   gtk_widget_get_allocation (GTK_WIDGET (to), &to_allocation);
3692   to_priv->mouse_x = x + to_allocation.x;
3693   to_priv->mouse_y = y + to_allocation.y;
3694
3695   element = get_drop_position (to, tab_pack);
3696   page_num = g_list_position (to_priv->children, element);
3697   gtk_notebook_insert_page_menu (to, child, tab_label, menu_label, page_num);
3698
3699   gtk_container_child_set (GTK_CONTAINER (to), child,
3700                            "tab-pack", tab_pack,
3701                            "tab-expand", tab_expand,
3702                            "tab-fill", tab_fill,
3703                            "reorderable", reorderable,
3704                            "detachable", detachable,
3705                            NULL);
3706   if (child)
3707     g_object_unref (child);
3708
3709   if (tab_label)
3710     g_object_unref (tab_label);
3711
3712   if (menu_label)
3713     g_object_unref (menu_label);
3714
3715   gtk_notebook_set_current_page (to, page_num);
3716 }
3717
3718 static void
3719 gtk_notebook_drag_data_get (GtkWidget        *widget,
3720                             GdkDragContext   *context,
3721                             GtkSelectionData *data,
3722                             guint             info,
3723                             guint             time)
3724 {
3725   if (data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3726     {
3727       GtkNotebook *notebook = GTK_NOTEBOOK (widget);
3728       GtkNotebookPrivate *priv = notebook->priv;
3729
3730       gtk_selection_data_set (data,
3731                               data->target,
3732                               8,
3733                               (void*) &priv->detached_tab->child,
3734                               sizeof (gpointer));
3735     }
3736 }
3737
3738 static void
3739 gtk_notebook_drag_data_received (GtkWidget        *widget,
3740                                  GdkDragContext   *context,
3741                                  gint              x,
3742                                  gint              y,
3743                                  GtkSelectionData *data,
3744                                  guint             info,
3745                                  guint             time)
3746 {
3747   GtkNotebook *notebook;
3748   GtkWidget *source_widget;
3749   GtkWidget **child;
3750
3751   notebook = GTK_NOTEBOOK (widget);
3752   source_widget = gtk_drag_get_source_widget (context);
3753
3754   if (source_widget &&
3755       data->target == gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB"))
3756     {
3757       child = (void*) data->data;
3758
3759       do_detach_tab (GTK_NOTEBOOK (source_widget), notebook, *child, x, y);
3760       gtk_drag_finish (context, TRUE, FALSE, time);
3761     }
3762   else
3763     gtk_drag_finish (context, FALSE, FALSE, time);
3764 }
3765
3766 /* Private GtkContainer Methods :
3767  * 
3768  * gtk_notebook_set_child_arg
3769  * gtk_notebook_get_child_arg
3770  * gtk_notebook_add
3771  * gtk_notebook_remove
3772  * gtk_notebook_focus
3773  * gtk_notebook_set_focus_child
3774  * gtk_notebook_child_type
3775  * gtk_notebook_forall
3776  */
3777 static void
3778 gtk_notebook_set_child_property (GtkContainer    *container,
3779                                  GtkWidget       *child,
3780                                  guint            property_id,
3781                                  const GValue    *value,
3782                                  GParamSpec      *pspec)
3783 {
3784   gboolean expand;
3785   gboolean fill;
3786   GtkPackType pack_type;
3787
3788   /* not finding child's page is valid for menus or labels */
3789   if (!gtk_notebook_find_child (GTK_NOTEBOOK (container), child, NULL))
3790     return;
3791
3792   switch (property_id)
3793     {
3794     case CHILD_PROP_TAB_LABEL:
3795       /* a NULL pointer indicates a default_tab setting, otherwise
3796        * we need to set the associated label
3797        */
3798       gtk_notebook_set_tab_label_text (GTK_NOTEBOOK (container), child,
3799                                        g_value_get_string (value));
3800       break;
3801     case CHILD_PROP_MENU_LABEL:
3802       gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (container), child,
3803                                         g_value_get_string (value));
3804       break;
3805     case CHILD_PROP_POSITION:
3806       gtk_notebook_reorder_child (GTK_NOTEBOOK (container), child,
3807                                   g_value_get_int (value));
3808       break;
3809     case CHILD_PROP_TAB_EXPAND:
3810       gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3811                                             &expand, &fill, &pack_type);
3812       gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3813                                           g_value_get_boolean (value),
3814                                           fill, pack_type);
3815       break;
3816     case CHILD_PROP_TAB_FILL:
3817       gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3818                                             &expand, &fill, &pack_type);
3819       gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3820                                           expand,
3821                                           g_value_get_boolean (value),
3822                                           pack_type);
3823       break;
3824     case CHILD_PROP_TAB_PACK:
3825       gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3826                                             &expand, &fill, &pack_type);
3827       gtk_notebook_set_tab_label_packing (GTK_NOTEBOOK (container), child,
3828                                           expand, fill,
3829                                           g_value_get_enum (value));
3830       break;
3831     case CHILD_PROP_REORDERABLE:
3832       gtk_notebook_set_tab_reorderable (GTK_NOTEBOOK (container), child,
3833                                         g_value_get_boolean (value));
3834       break;
3835     case CHILD_PROP_DETACHABLE:
3836       gtk_notebook_set_tab_detachable (GTK_NOTEBOOK (container), child,
3837                                        g_value_get_boolean (value));
3838       break;
3839     default:
3840       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3841       break;
3842     }
3843 }
3844
3845 static void
3846 gtk_notebook_get_child_property (GtkContainer    *container,
3847                                  GtkWidget       *child,
3848                                  guint            property_id,
3849                                  GValue          *value,
3850                                  GParamSpec      *pspec)
3851 {
3852   GtkNotebook *notebook = GTK_NOTEBOOK (container);
3853   GtkNotebookPrivate *priv = notebook->priv;
3854   GList *list;
3855   GtkWidget *label;
3856   gboolean expand;
3857   gboolean fill;
3858   GtkPackType pack_type;
3859
3860   /* not finding child's page is valid for menus or labels */
3861   list = gtk_notebook_find_child (notebook, child, NULL);
3862   if (!list)
3863     {
3864       /* nothing to set on labels or menus */
3865       g_param_value_set_default (pspec, value);
3866       return;
3867     }
3868
3869   switch (property_id)
3870     {
3871     case CHILD_PROP_TAB_LABEL:
3872       label = gtk_notebook_get_tab_label (notebook, child);
3873
3874       if (GTK_IS_LABEL (label))
3875         g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
3876       else
3877         g_value_set_string (value, NULL);
3878       break;
3879     case CHILD_PROP_MENU_LABEL:
3880       label = gtk_notebook_get_menu_label (notebook, child);
3881
3882       if (GTK_IS_LABEL (label))
3883         g_value_set_string (value, gtk_label_get_label (GTK_LABEL (label)));
3884       else
3885         g_value_set_string (value, NULL);
3886       break;
3887     case CHILD_PROP_POSITION:
3888       g_value_set_int (value, g_list_position (priv->children, list));
3889       break;
3890     case CHILD_PROP_TAB_EXPAND:
3891         gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3892                                               &expand, NULL, NULL);
3893         g_value_set_boolean (value, expand);
3894       break;
3895     case CHILD_PROP_TAB_FILL:
3896         gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3897                                               NULL, &fill, NULL);
3898         g_value_set_boolean (value, fill);
3899       break;
3900     case CHILD_PROP_TAB_PACK:
3901         gtk_notebook_query_tab_label_packing (GTK_NOTEBOOK (container), child,
3902                                               NULL, NULL, &pack_type);
3903         g_value_set_enum (value, pack_type);
3904       break;
3905     case CHILD_PROP_REORDERABLE:
3906       g_value_set_boolean (value,
3907                            gtk_notebook_get_tab_reorderable (GTK_NOTEBOOK (container), child));
3908       break;
3909     case CHILD_PROP_DETACHABLE:
3910       g_value_set_boolean (value,
3911                            gtk_notebook_get_tab_detachable (GTK_NOTEBOOK (container), child));
3912       break;
3913     default:
3914       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
3915       break;
3916     }
3917 }
3918
3919 static void
3920 gtk_notebook_add (GtkContainer *container,
3921                   GtkWidget    *widget)
3922 {
3923   gtk_notebook_insert_page_menu (GTK_NOTEBOOK (container), widget, 
3924                                  NULL, NULL, -1);
3925 }
3926
3927 static void
3928 gtk_notebook_remove (GtkContainer *container,
3929                      GtkWidget    *widget)
3930 {
3931   GtkNotebook *notebook = GTK_NOTEBOOK (container);
3932   GtkNotebookPrivate *priv = notebook->priv;
3933   GtkNotebookPage *page;
3934   GList *children;
3935   gint page_num = 0;
3936
3937   children = priv->children;
3938   while (children)
3939     {
3940       page = children->data;
3941
3942       if (page->child == widget)
3943         break;
3944
3945       page_num++;
3946       children = children->next;
3947     }
3948  
3949   if (children == NULL)
3950     return;
3951   
3952   g_object_ref (widget);
3953
3954   gtk_notebook_real_remove (notebook, children);
3955
3956   g_signal_emit (notebook,
3957                  notebook_signals[PAGE_REMOVED],
3958                  0,
3959                  widget,
3960                  page_num);
3961   
3962   g_object_unref (widget);
3963 }
3964
3965 static gboolean
3966 focus_tabs_in (GtkNotebook *notebook)
3967 {
3968   GtkNotebookPrivate *priv = notebook->priv;
3969
3970   if (priv->show_tabs && priv->cur_page)
3971     {
3972       gtk_widget_grab_focus (GTK_WIDGET (notebook));
3973
3974       gtk_notebook_switch_focus_tab (notebook,
3975                                      g_list_find (priv->children,
3976                                                   priv->cur_page));
3977
3978       return TRUE;
3979     }
3980   else
3981     return FALSE;
3982 }
3983
3984 static gboolean
3985 focus_tabs_move (GtkNotebook     *notebook,
3986                  GtkDirectionType direction,
3987                  gint             search_direction)
3988 {
3989   GtkNotebookPrivate *priv = notebook->priv;
3990   GList *new_page;
3991
3992   new_page = gtk_notebook_search_page (notebook, priv->focus_tab,
3993                                        search_direction, TRUE);
3994   if (!new_page)
3995     {
3996       gboolean wrap_around;
3997
3998       g_object_get (gtk_widget_get_settings (GTK_WIDGET (notebook)),
3999                     "gtk-keynav-wrap-around", &wrap_around,
4000                     NULL);
4001
4002       if (wrap_around)
4003         new_page = gtk_notebook_search_page (notebook, NULL,
4004                                              search_direction, TRUE);
4005     }
4006
4007   if (new_page)
4008     gtk_notebook_switch_focus_tab (notebook, new_page);
4009   else
4010     gtk_widget_error_bell (GTK_WIDGET (notebook));
4011
4012   return TRUE;
4013 }
4014
4015 static gboolean
4016 focus_child_in (GtkNotebook      *notebook,
4017                 GtkDirectionType  direction)
4018 {
4019   GtkNotebookPrivate *priv = notebook->priv;
4020
4021   if (priv->cur_page)
4022     return gtk_widget_child_focus (priv->cur_page->child, direction);
4023   else
4024     return FALSE;
4025 }
4026
4027 static gboolean
4028 focus_action_in (GtkNotebook      *notebook,
4029                  gint              action,
4030                  GtkDirectionType  direction)
4031 {
4032   GtkNotebookPrivate *priv = notebook->priv;
4033
4034   if (priv->action_widget[action] &&
4035       gtk_widget_get_visible (priv->action_widget[action]))
4036     return gtk_widget_child_focus (priv->action_widget[action], direction);
4037   else
4038     return FALSE;
4039 }
4040
4041 /* Focus in the notebook can either be on the pages, or on
4042  * the tabs or on the action_widgets.
4043  */
4044 static gint
4045 gtk_notebook_focus (GtkWidget        *widget,
4046                     GtkDirectionType  direction)
4047 {
4048   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
4049   GtkNotebookPrivate *priv = notebook->priv;
4050   GtkWidget *old_focus_child;
4051   GtkDirectionType effective_direction;
4052   gint first_action;
4053   gint last_action;
4054
4055   gboolean widget_is_focus;
4056   GtkContainer *container;
4057
4058   container = GTK_CONTAINER (widget);
4059
4060   if (priv->tab_pos == GTK_POS_TOP ||
4061       priv->tab_pos == GTK_POS_LEFT)
4062     {
4063       first_action = ACTION_WIDGET_START;
4064       last_action = ACTION_WIDGET_END;
4065     }
4066   else
4067     {
4068       first_action = ACTION_WIDGET_END;
4069       last_action = ACTION_WIDGET_START;
4070     }
4071
4072   if (priv->focus_out)
4073     {
4074       priv->focus_out = FALSE; /* Clear this to catch the wrap-around case */
4075       return FALSE;
4076     }
4077
4078   widget_is_focus = gtk_widget_is_focus (widget);
4079   old_focus_child = gtk_container_get_focus_child (container);
4080
4081   effective_direction = get_effective_direction (notebook, direction);
4082
4083   if (old_focus_child)          /* Focus on page child or action widget */
4084     {
4085       if (gtk_widget_child_focus (old_focus_child, direction))
4086         return TRUE;
4087
4088       if (old_focus_child == priv->action_widget[ACTION_WIDGET_START])
4089         {
4090           switch (effective_direction)
4091             {
4092             case GTK_DIR_DOWN:
4093               return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4094             case GTK_DIR_RIGHT:
4095               return focus_tabs_in (notebook);
4096             case GTK_DIR_LEFT:
4097               return FALSE;
4098             case GTK_DIR_UP:
4099               return FALSE;
4100             default:
4101               switch (direction)
4102                 {
4103                 case GTK_DIR_TAB_FORWARD:
4104                   if ((priv->tab_pos == GTK_POS_RIGHT || priv->tab_pos == GTK_POS_BOTTOM) &&
4105                       focus_child_in (notebook, direction))
4106                     return TRUE;
4107                   return focus_tabs_in (notebook);
4108                 case GTK_DIR_TAB_BACKWARD:
4109                   return FALSE;
4110                 default:
4111                   g_assert_not_reached ();
4112                 }
4113             }
4114         }
4115       else if (old_focus_child == priv->action_widget[ACTION_WIDGET_END])
4116         {
4117           switch (effective_direction)
4118             {
4119             case GTK_DIR_DOWN:
4120               return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4121             case GTK_DIR_RIGHT:
4122               return FALSE;
4123             case GTK_DIR_LEFT:
4124               return focus_tabs_in (notebook);
4125             case GTK_DIR_UP:
4126               return FALSE;
4127             default:
4128               switch (direction)
4129                 {
4130                 case GTK_DIR_TAB_FORWARD:
4131                   return FALSE;
4132                 case GTK_DIR_TAB_BACKWARD:
4133                   if ((priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_LEFT) &&
4134                       focus_child_in (notebook, direction))
4135                     return TRUE;
4136                   return focus_tabs_in (notebook);
4137                 default:
4138                   g_assert_not_reached ();
4139                 }
4140             }
4141         }
4142       else
4143         {
4144           switch (effective_direction)
4145             {
4146             case GTK_DIR_TAB_BACKWARD:
4147             case GTK_DIR_UP:
4148               /* Focus onto the tabs */
4149               return focus_tabs_in (notebook);
4150             case GTK_DIR_DOWN:
4151             case GTK_DIR_LEFT:
4152             case GTK_DIR_RIGHT:
4153               return FALSE;
4154             case GTK_DIR_TAB_FORWARD:
4155               return focus_action_in (notebook, last_action, direction);
4156             }
4157         }
4158     }
4159   else if (widget_is_focus)     /* Focus was on tabs */
4160     {
4161       switch (effective_direction)
4162         {
4163         case GTK_DIR_TAB_BACKWARD:
4164               return focus_action_in (notebook, first_action, direction);
4165         case GTK_DIR_UP:
4166           return FALSE;
4167         case GTK_DIR_TAB_FORWARD:
4168           if (focus_child_in (notebook, GTK_DIR_TAB_FORWARD))
4169             return TRUE;
4170           return focus_action_in (notebook, last_action, direction);
4171         case GTK_DIR_DOWN:
4172           /* We use TAB_FORWARD rather than direction so that we focus a more
4173            * predictable widget for the user; users may be using arrow focusing
4174            * in this situation even if they don't usually use arrow focusing.
4175            */
4176           return focus_child_in (notebook, GTK_DIR_TAB_FORWARD);
4177         case GTK_DIR_LEFT:
4178           return focus_tabs_move (notebook, direction, STEP_PREV);
4179         case GTK_DIR_RIGHT:
4180           return focus_tabs_move (notebook, direction, STEP_NEXT);
4181         }
4182     }
4183   else /* Focus was not on widget */
4184     {
4185       switch (effective_direction)
4186         {
4187         case GTK_DIR_TAB_FORWARD:
4188         case GTK_DIR_DOWN:
4189           if (focus_action_in (notebook, first_action, direction))
4190             return TRUE;
4191           if (focus_tabs_in (notebook))
4192             return TRUE;
4193           if (focus_action_in (notebook, last_action, direction))
4194             return TRUE;
4195           if (focus_child_in (notebook, direction))
4196             return TRUE;
4197           return FALSE;
4198         case GTK_DIR_TAB_BACKWARD:
4199           if (focus_action_in (notebook, last_action, direction))
4200             return TRUE;
4201           if (focus_child_in (notebook, direction))
4202             return TRUE;
4203           if (focus_tabs_in (notebook))
4204             return TRUE;
4205           if (focus_action_in (notebook, first_action, direction))
4206             return TRUE;
4207         case GTK_DIR_UP:
4208         case GTK_DIR_LEFT:
4209         case GTK_DIR_RIGHT:
4210           return focus_child_in (notebook, direction);
4211         }
4212     }
4213
4214   g_assert_not_reached ();
4215   return FALSE;
4216 }
4217
4218 static void
4219 gtk_notebook_set_focus_child (GtkContainer *container,
4220                               GtkWidget    *child)
4221 {
4222   GtkNotebook *notebook = GTK_NOTEBOOK (container);
4223   GtkNotebookPrivate *priv = notebook->priv;
4224   GtkWidget *page_child;
4225   GtkWidget *toplevel;
4226
4227   /* If the old focus widget was within a page of the notebook,
4228    * (child may either be NULL or not in this case), record it
4229    * for future use if we switch to the page with a mnemonic.
4230    */
4231
4232   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (container));
4233   if (toplevel && gtk_widget_is_toplevel (toplevel))
4234     {
4235       page_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4236       while (page_child)
4237         {
4238           if (gtk_widget_get_parent (page_child) == GTK_WIDGET (container))
4239             {
4240               GList *list = gtk_notebook_find_child (notebook, page_child, NULL);
4241               if (list != NULL) 
4242                 {
4243                   GtkNotebookPage *page = list->data;
4244               
4245                   if (page->last_focus_child)
4246                     g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4247
4248                   page->last_focus_child = gtk_window_get_focus (GTK_WINDOW (toplevel));
4249                   g_object_add_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4250               
4251                   break;
4252                 }
4253             }
4254
4255           page_child = gtk_widget_get_parent (page_child);
4256         }
4257     }
4258   
4259   if (child)
4260     {
4261       g_return_if_fail (GTK_IS_WIDGET (child));
4262
4263       priv->child_has_focus = TRUE;
4264       if (!priv->focus_tab)
4265         {
4266           GList *children;
4267           GtkNotebookPage *page;
4268
4269           children = priv->children;
4270           while (children)
4271             {
4272               page = children->data;
4273               if (page->child == child || page->tab_label == child)
4274                 gtk_notebook_switch_focus_tab (notebook, children);
4275               children = children->next;
4276             }
4277         }
4278     }
4279   else
4280     priv->child_has_focus = FALSE;
4281
4282   GTK_CONTAINER_CLASS (gtk_notebook_parent_class)->set_focus_child (container, child);
4283 }
4284
4285 static void
4286 gtk_notebook_forall (GtkContainer *container,
4287                      gboolean      include_internals,
4288                      GtkCallback   callback,
4289                      gpointer      callback_data)
4290 {
4291   GtkNotebook *notebook = GTK_NOTEBOOK (container);
4292   GtkNotebookPrivate *priv = notebook->priv;
4293   GList *children;
4294   gint i;
4295
4296   children = priv->children;
4297   while (children)
4298     {
4299       GtkNotebookPage *page;
4300       
4301       page = children->data;
4302       children = children->next;
4303       (* callback) (page->child, callback_data);
4304
4305       if (include_internals)
4306         {
4307           if (page->tab_label)
4308             (* callback) (page->tab_label, callback_data);
4309         }
4310     }
4311
4312   if (include_internals) {
4313     for (i = 0; i < N_ACTION_WIDGETS; i++)
4314       {
4315         if (priv->action_widget[i])
4316           (* callback) (priv->action_widget[i], callback_data);
4317       }
4318   }
4319 }
4320
4321 static GType
4322 gtk_notebook_child_type (GtkContainer     *container)
4323 {
4324   return GTK_TYPE_WIDGET;
4325 }
4326
4327 /* Private GtkNotebook Methods:
4328  *
4329  * gtk_notebook_real_insert_page
4330  */
4331 static void
4332 page_visible_cb (GtkWidget  *page,
4333                  GParamSpec *arg,
4334                  gpointer    data)
4335 {
4336   GtkNotebook *notebook = GTK_NOTEBOOK (data);
4337   GtkNotebookPrivate *priv = notebook->priv;
4338   GList *list;
4339   GList *next = NULL;
4340
4341   if (priv->cur_page &&
4342       priv->cur_page->child == page &&
4343       !gtk_widget_get_visible (page))
4344     {
4345       list = g_list_find (priv->children, priv->cur_page);
4346       if (list)
4347         {
4348           next = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4349           if (!next)
4350             next = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4351         }
4352
4353       if (next)
4354         gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next));
4355     }
4356 }
4357
4358 static gint
4359 gtk_notebook_real_insert_page (GtkNotebook *notebook,
4360                                GtkWidget   *child,
4361                                GtkWidget   *tab_label,
4362                                GtkWidget   *menu_label,
4363                                gint         position)
4364 {
4365   GtkNotebookPrivate *priv = notebook->priv;
4366   GtkNotebookPage *page;
4367   gint nchildren;
4368
4369   gtk_widget_freeze_child_notify (child);
4370
4371   page = g_slice_new0 (GtkNotebookPage);
4372   page->child = child;
4373
4374   nchildren = g_list_length (priv->children);
4375   if ((position < 0) || (position > nchildren))
4376     position = nchildren;
4377
4378   priv->children = g_list_insert (priv->children, page, position);
4379
4380   if (!tab_label)
4381     {
4382       page->default_tab = TRUE;
4383       if (priv->show_tabs)
4384         tab_label = gtk_label_new (NULL);
4385     }
4386   page->tab_label = tab_label;
4387   page->menu_label = menu_label;
4388   page->expand = FALSE;
4389   page->fill = TRUE;
4390   page->pack = GTK_PACK_START; 
4391
4392   if (!menu_label)
4393     page->default_menu = TRUE;
4394   else  
4395     g_object_ref_sink (page->menu_label);
4396
4397   if (priv->menu)
4398     gtk_notebook_menu_item_create (notebook,
4399                                    g_list_find (priv->children, page));
4400
4401   gtk_widget_set_parent (child, GTK_WIDGET (notebook));
4402   if (tab_label)
4403     gtk_widget_set_parent (tab_label, GTK_WIDGET (notebook));
4404
4405   gtk_notebook_update_labels (notebook);
4406
4407   if (!priv->first_tab)
4408     priv->first_tab = priv->children;
4409
4410   /* child visible will be turned on by switch_page below */
4411   if (priv->cur_page != page)
4412     gtk_widget_set_child_visible (child, FALSE);
4413
4414   if (tab_label)
4415     {
4416       if (priv->show_tabs && gtk_widget_get_visible (child))
4417         gtk_widget_show (tab_label);
4418       else
4419         gtk_widget_hide (tab_label);
4420
4421     page->mnemonic_activate_signal =
4422       g_signal_connect (tab_label,
4423                         "mnemonic-activate",
4424                         G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
4425                         notebook);
4426     }
4427
4428   page->notify_visible_handler = g_signal_connect (child, "notify::visible",
4429                                                    G_CALLBACK (page_visible_cb), notebook);
4430
4431   g_signal_emit (notebook,
4432                  notebook_signals[PAGE_ADDED],
4433                  0,
4434                  child,
4435                  position);
4436
4437   if (!priv->cur_page)
4438     {
4439       gtk_notebook_switch_page (notebook, page);
4440       /* focus_tab is set in the switch_page method */
4441       gtk_notebook_switch_focus_tab (notebook, priv->focus_tab);
4442     }
4443
4444   gtk_notebook_update_tab_states (notebook);
4445
4446   if (priv->scrollable)
4447     gtk_notebook_redraw_arrows (notebook);
4448
4449   gtk_widget_child_notify (child, "tab-expand");
4450   gtk_widget_child_notify (child, "tab-fill");
4451   gtk_widget_child_notify (child, "tab-pack");
4452   gtk_widget_child_notify (child, "tab-label");
4453   gtk_widget_child_notify (child, "menu-label");
4454   gtk_widget_child_notify (child, "position");
4455   gtk_widget_thaw_child_notify (child);
4456
4457   /* The page-added handler might have reordered the pages, re-get the position */
4458   return gtk_notebook_page_num (notebook, child);
4459 }
4460
4461 /* Private GtkNotebook Functions:
4462  *
4463  * gtk_notebook_redraw_tabs
4464  * gtk_notebook_real_remove
4465  * gtk_notebook_update_labels
4466  * gtk_notebook_timer
4467  * gtk_notebook_set_scroll_timer
4468  * gtk_notebook_page_compare
4469  * gtk_notebook_real_page_position
4470  * gtk_notebook_search_page
4471  */
4472 static void
4473 gtk_notebook_redraw_tabs (GtkNotebook *notebook)
4474 {
4475   GtkNotebookPrivate *priv = notebook->priv;
4476   GtkAllocation allocation;
4477   GtkWidget *widget;
4478   GtkNotebookPage *page;
4479   GtkStyle *style;
4480   GdkRectangle redraw_rect;
4481   gint border;
4482   gint tab_pos = get_effective_tab_pos (notebook);
4483
4484   widget = GTK_WIDGET (notebook);
4485   border = gtk_container_get_border_width (GTK_CONTAINER (notebook));
4486
4487   if (!gtk_widget_get_mapped (widget) || !priv->first_tab)
4488     return;
4489
4490   page = priv->first_tab->data;
4491
4492   redraw_rect.x = border;
4493   redraw_rect.y = border;
4494
4495   style = gtk_widget_get_style (widget);
4496   gtk_widget_get_allocation (widget, &allocation);
4497
4498   switch (tab_pos)
4499     {
4500     case GTK_POS_BOTTOM:
4501       redraw_rect.y = allocation.height - border -
4502         page->allocation.height - style->ythickness;
4503
4504       if (page != priv->cur_page)
4505         redraw_rect.y -= style->ythickness;
4506       /* fall through */
4507     case GTK_POS_TOP:
4508       redraw_rect.width = allocation.width - 2 * border;
4509       redraw_rect.height = page->allocation.height + style->ythickness;
4510
4511       if (page != priv->cur_page)
4512         redraw_rect.height += style->ythickness;
4513       break;
4514     case GTK_POS_RIGHT:
4515       redraw_rect.x = allocation.width - border -
4516         page->allocation.width - style->xthickness;
4517
4518       if (page != priv->cur_page)
4519         redraw_rect.x -= style->xthickness;
4520       /* fall through */
4521     case GTK_POS_LEFT:
4522       redraw_rect.width = page->allocation.width + style->xthickness;
4523       redraw_rect.height = allocation.height - 2 * border;
4524
4525       if (page != priv->cur_page)
4526         redraw_rect.width += style->xthickness;
4527       break;
4528     }
4529
4530   redraw_rect.x += allocation.x;
4531   redraw_rect.y += allocation.y;
4532
4533   gdk_window_invalidate_rect (gtk_widget_get_window (widget),
4534                               &redraw_rect, TRUE);
4535 }
4536
4537 static void
4538 gtk_notebook_redraw_arrows (GtkNotebook *notebook)
4539 {
4540   GtkNotebookPrivate *priv = notebook->priv;
4541
4542   if (gtk_widget_get_mapped (GTK_WIDGET (notebook)) &&
4543       gtk_notebook_show_arrows (notebook))
4544     {
4545       GdkRectangle rect;
4546       gint i;
4547       GtkNotebookArrow arrow[4];
4548
4549       arrow[0] = priv->has_before_previous ? ARROW_LEFT_BEFORE : ARROW_NONE;
4550       arrow[1] = priv->has_before_next ? ARROW_RIGHT_BEFORE : ARROW_NONE;
4551       arrow[2] = priv->has_after_previous ? ARROW_LEFT_AFTER : ARROW_NONE;
4552       arrow[3] = priv->has_after_next ? ARROW_RIGHT_AFTER : ARROW_NONE;
4553
4554       for (i = 0; i < 4; i++) 
4555         {
4556           if (arrow[i] == ARROW_NONE)
4557             continue;
4558
4559           gtk_notebook_get_arrow_rect (notebook, &rect, arrow[i]);
4560           gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (notebook)),
4561                                       &rect, FALSE);
4562         }
4563     }
4564 }
4565
4566 static gboolean
4567 gtk_notebook_timer (GtkNotebook *notebook)
4568 {
4569   GtkNotebookPrivate *priv = notebook->priv;
4570   gboolean retval = FALSE;
4571
4572   if (priv->timer)
4573     {
4574       gtk_notebook_do_arrow (notebook, priv->click_child);
4575
4576       if (priv->need_timer)
4577         {
4578           GtkSettings *settings;
4579           guint        timeout;
4580
4581           settings = gtk_widget_get_settings (GTK_WIDGET (notebook));
4582           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
4583
4584           priv->need_timer = FALSE;
4585           priv->timer = gdk_threads_add_timeout (timeout * SCROLL_DELAY_FACTOR,
4586                                            (GSourceFunc) gtk_notebook_timer,
4587                                            (gpointer) notebook);
4588         }
4589       else
4590         retval = TRUE;
4591     }
4592
4593   return retval;
4594 }
4595
4596 static void
4597 gtk_notebook_set_scroll_timer (GtkNotebook *notebook)
4598 {
4599   GtkNotebookPrivate *priv = notebook->priv;
4600   GtkWidget *widget = GTK_WIDGET (notebook);
4601
4602   if (!priv->timer)
4603     {
4604       GtkSettings *settings = gtk_widget_get_settings (widget);
4605       guint timeout;
4606
4607       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
4608
4609       priv->timer = gdk_threads_add_timeout (timeout,
4610                                        (GSourceFunc) gtk_notebook_timer,
4611                                        (gpointer) notebook);
4612       priv->need_timer = TRUE;
4613     }
4614 }
4615
4616 static gint
4617 gtk_notebook_page_compare (gconstpointer a,
4618                            gconstpointer b)
4619 {
4620   return (((GtkNotebookPage *) a)->child != b);
4621 }
4622
4623 static GList*
4624 gtk_notebook_find_child (GtkNotebook *notebook,
4625                          GtkWidget   *child,
4626                          const gchar *function)
4627 {
4628   GtkNotebookPrivate *priv = notebook->priv;
4629   GList *list = g_list_find_custom (priv->children, child,
4630                                     gtk_notebook_page_compare);
4631
4632 #ifndef G_DISABLE_CHECKS
4633   if (!list && function)
4634     g_warning ("%s: unable to find child %p in notebook %p",
4635                function, child, notebook);
4636 #endif
4637
4638   return list;
4639 }
4640
4641 static void
4642 gtk_notebook_remove_tab_label (GtkNotebook     *notebook,
4643                                GtkNotebookPage *page)
4644 {
4645   if (page->tab_label)
4646     {
4647       if (page->mnemonic_activate_signal)
4648         g_signal_handler_disconnect (page->tab_label,
4649                                      page->mnemonic_activate_signal);
4650       page->mnemonic_activate_signal = 0;
4651
4652       gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
4653       gtk_widget_unparent (page->tab_label);
4654       page->tab_label = NULL;
4655     }
4656 }
4657
4658 static void
4659 gtk_notebook_real_remove (GtkNotebook *notebook,
4660                           GList       *list)
4661 {
4662   GtkNotebookPrivate *priv = notebook->priv;
4663   GtkNotebookPage *page;
4664   GList * next_list;
4665   gint need_resize = FALSE;
4666   GtkWidget *tab_label;
4667   gboolean destroying;
4668
4669   destroying = gtk_widget_in_destruction (GTK_WIDGET (notebook));
4670
4671   next_list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
4672   if (!next_list)
4673     next_list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
4674
4675   priv->children = g_list_remove_link (priv->children, list);
4676
4677   if (priv->cur_page == list->data)
4678     { 
4679       priv->cur_page = NULL;
4680       if (next_list && !destroying)
4681         gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (next_list));
4682     }
4683
4684   if (priv->detached_tab == list->data)
4685     priv->detached_tab = NULL;
4686
4687   if (list == priv->first_tab)
4688     priv->first_tab = next_list;
4689   if (list == priv->focus_tab && !destroying)
4690     gtk_notebook_switch_focus_tab (notebook, next_list);
4691
4692   page = list->data;
4693   
4694   g_signal_handler_disconnect (page->child, page->notify_visible_handler); 
4695
4696   if (gtk_widget_get_visible (page->child) &&
4697       gtk_widget_get_visible (GTK_WIDGET (notebook)))
4698     need_resize = TRUE;
4699
4700   gtk_widget_unparent (page->child);
4701
4702   tab_label = page->tab_label;
4703   if (tab_label)
4704     {
4705       g_object_ref (tab_label);
4706       gtk_notebook_remove_tab_label (notebook, page);
4707       if (destroying)
4708         gtk_widget_destroy (tab_label);
4709       g_object_unref (tab_label);
4710     }
4711
4712   if (priv->menu)
4713     {
4714       GtkWidget *parent = gtk_widget_get_parent (page->menu_label);
4715
4716       gtk_notebook_menu_label_unparent (parent, NULL);
4717       gtk_container_remove (GTK_CONTAINER (priv->menu), parent);
4718
4719       gtk_widget_queue_resize (priv->menu);
4720     }
4721   if (!page->default_menu)
4722     g_object_unref (page->menu_label);
4723
4724   g_list_free (list);
4725
4726   if (page->last_focus_child)
4727     {
4728       g_object_remove_weak_pointer (G_OBJECT (page->last_focus_child), (gpointer *)&page->last_focus_child);
4729       page->last_focus_child = NULL;
4730     }
4731   
4732   g_slice_free (GtkNotebookPage, page);
4733
4734   gtk_notebook_update_labels (notebook);
4735   if (need_resize)
4736     gtk_widget_queue_resize (GTK_WIDGET (notebook));
4737 }
4738
4739 static void
4740 gtk_notebook_update_labels (GtkNotebook *notebook)
4741 {
4742   GtkNotebookPrivate *priv = notebook->priv;
4743   GtkNotebookPage *page;
4744   GList *list;
4745   gchar string[32];
4746   gint page_num = 1;
4747
4748   if (!priv->show_tabs && !priv->menu)
4749     return;
4750
4751   for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
4752        list;
4753        list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
4754     {
4755       page = list->data;
4756       g_snprintf (string, sizeof(string), _("Page %u"), page_num++);
4757       if (priv->show_tabs)
4758         {
4759           if (page->default_tab)
4760             {
4761               if (!page->tab_label)
4762                 {
4763                   page->tab_label = gtk_label_new (string);
4764                   gtk_widget_set_parent (page->tab_label,
4765                                          GTK_WIDGET (notebook));
4766                 }
4767               else
4768                 gtk_label_set_text (GTK_LABEL (page->tab_label), string);
4769             }
4770
4771           if (gtk_widget_get_visible (page->child) &&
4772               !gtk_widget_get_visible (page->tab_label))
4773             gtk_widget_show (page->tab_label);
4774           else if (!gtk_widget_get_visible (page->child) &&
4775                    gtk_widget_get_visible (page->tab_label))
4776             gtk_widget_hide (page->tab_label);
4777         }
4778       if (priv->menu && page->default_menu)
4779         {
4780           if (GTK_IS_LABEL (page->tab_label))
4781             gtk_label_set_text (GTK_LABEL (page->menu_label),
4782                                 gtk_label_get_label (GTK_LABEL (page->tab_label)));
4783           else
4784             gtk_label_set_text (GTK_LABEL (page->menu_label), string);
4785         }
4786     }  
4787 }
4788
4789 static gint
4790 gtk_notebook_real_page_position (GtkNotebook *notebook,
4791                                  GList       *list)
4792 {
4793   GtkNotebookPrivate *priv = notebook->priv;
4794   GList *work;
4795   gint count_start;
4796
4797   for (work = priv->children, count_start = 0;
4798        work && work != list; work = work->next)
4799     if (GTK_NOTEBOOK_PAGE (work)->pack == GTK_PACK_START)
4800       count_start++;
4801
4802   if (!work)
4803     return -1;
4804
4805   if (GTK_NOTEBOOK_PAGE (list)->pack == GTK_PACK_START)
4806     return count_start;
4807
4808   return (count_start + g_list_length (list) - 1);
4809 }
4810
4811 static GList *
4812 gtk_notebook_search_page (GtkNotebook *notebook,
4813                           GList       *list,
4814                           gint         direction,
4815                           gboolean     find_visible)
4816 {
4817   GtkNotebookPrivate *priv = notebook->priv;
4818   GtkNotebookPage *page = NULL;
4819   GList *old_list = NULL;
4820   gint flag = 0;
4821
4822   switch (direction)
4823     {
4824     case STEP_PREV:
4825       flag = GTK_PACK_END;
4826       break;
4827
4828     case STEP_NEXT:
4829       flag = GTK_PACK_START;
4830       break;
4831     }
4832
4833   if (list)
4834     page = list->data;
4835
4836   if (!page || page->pack == flag)
4837     {
4838       if (list)
4839         {
4840           old_list = list;
4841           list = list->next;
4842         }
4843       else
4844         list = priv->children;
4845
4846       while (list)
4847         {
4848           page = list->data;
4849           if (page->pack == flag &&
4850               (!find_visible ||
4851                (gtk_widget_get_visible (page->child) &&
4852                 (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4853             return list;
4854           old_list = list;
4855           list = list->next;
4856         }
4857       list = old_list;
4858     }
4859   else
4860     {
4861       old_list = list;
4862       list = list->prev;
4863     }
4864   while (list)
4865     {
4866       page = list->data;
4867       if (page->pack != flag &&
4868           (!find_visible ||
4869            (gtk_widget_get_visible (page->child) &&
4870             (!page->tab_label || NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page)))))
4871         return list;
4872       old_list = list;
4873       list = list->prev;
4874     }
4875   return NULL;
4876 }
4877
4878 /* Private GtkNotebook Drawing Functions:
4879  *
4880  * gtk_notebook_paint
4881  * gtk_notebook_draw_tab
4882  * gtk_notebook_draw_arrow
4883  */
4884 static void
4885 gtk_notebook_paint (GtkWidget    *widget,
4886                     cairo_t      *cr)
4887 {
4888   GtkNotebook *notebook;
4889   GtkNotebookPrivate *priv;
4890   GtkNotebookPage *page;
4891   GtkAllocation allocation;
4892   GList *children;
4893   gboolean showarrow;
4894   gint width, height;
4895   gint x, y;
4896   guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
4897   gint gap_x = 0, gap_width = 0, step = STEP_PREV;
4898   gboolean is_rtl;
4899   gint tab_pos;
4900    
4901   notebook = GTK_NOTEBOOK (widget);
4902   priv = notebook->priv;
4903   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
4904   tab_pos = get_effective_tab_pos (notebook);
4905
4906   if ((!priv->show_tabs && !priv->show_border) ||
4907       !priv->cur_page || !gtk_widget_get_visible (priv->cur_page->child))
4908     return;
4909
4910   gtk_widget_get_allocation (widget, &allocation);
4911
4912   x = allocation.x + border_width;
4913   y = allocation.y + border_width;
4914   width = allocation.width - border_width * 2;
4915   height = allocation.height - border_width * 2;
4916
4917   if (priv->show_border && (!priv->show_tabs || !priv->children))
4918     {
4919       gtk_paint_box (gtk_widget_get_style (widget), cr,
4920                      GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4921                      widget, "notebook",
4922                      x, y, width, height);
4923       return;
4924     }
4925
4926   if (!priv->first_tab)
4927     priv->first_tab = priv->children;
4928
4929   if (!gtk_widget_get_mapped (priv->cur_page->tab_label))
4930     page = GTK_NOTEBOOK_PAGE (priv->first_tab);
4931   else
4932     page = priv->cur_page;
4933
4934   switch (tab_pos)
4935     {
4936     case GTK_POS_TOP:
4937       y += page->allocation.height;
4938       /* fall thru */
4939     case GTK_POS_BOTTOM:
4940       height -= page->allocation.height;
4941       break;
4942     case GTK_POS_LEFT:
4943       x += page->allocation.width;
4944       /* fall thru */
4945     case GTK_POS_RIGHT:
4946       width -= page->allocation.width;
4947       break;
4948     }
4949
4950   if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) ||
4951       !gtk_widget_get_mapped (priv->cur_page->tab_label))
4952     {
4953       gap_x = 0;
4954       gap_width = 0;
4955     }
4956   else
4957     {
4958       switch (tab_pos)
4959         {
4960         case GTK_POS_TOP:
4961         case GTK_POS_BOTTOM:
4962           if (priv->operation == DRAG_OPERATION_REORDER)
4963             gap_x = priv->drag_window_x - allocation.x - border_width;
4964           else
4965             gap_x = priv->cur_page->allocation.x - allocation.x - border_width;
4966
4967           gap_width = priv->cur_page->allocation.width;
4968           step = is_rtl ? STEP_NEXT : STEP_PREV;
4969           break;
4970         case GTK_POS_LEFT:
4971         case GTK_POS_RIGHT:
4972           if (priv->operation == DRAG_OPERATION_REORDER)
4973             gap_x = priv->drag_window_y - border_width - allocation.y;
4974           else
4975             gap_x = priv->cur_page->allocation.y - allocation.y - border_width;
4976
4977           gap_width = priv->cur_page->allocation.height;
4978           step = STEP_PREV;
4979           break;
4980         }
4981     }
4982   gtk_paint_box_gap (gtk_widget_get_style (widget), cr,
4983                      GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4984                      widget, "notebook",
4985                      x, y, width, height,
4986                      tab_pos, gap_x, gap_width);
4987
4988   showarrow = FALSE;
4989   children = gtk_notebook_search_page (notebook, NULL, step, TRUE);
4990   while (children)
4991     {
4992       page = children->data;
4993       children = gtk_notebook_search_page (notebook, children,
4994                                            step, TRUE);
4995       if (!gtk_widget_get_visible (page->child))
4996         continue;
4997       if (!gtk_widget_get_mapped (page->tab_label))
4998         showarrow = TRUE;
4999       else if (page != priv->cur_page)
5000         gtk_notebook_draw_tab (notebook, page, cr);
5001     }
5002
5003   if (showarrow && priv->scrollable)
5004     {
5005       if (priv->has_before_previous)
5006         gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_BEFORE);
5007       if (priv->has_before_next)
5008         gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_BEFORE);
5009       if (priv->has_after_previous)
5010         gtk_notebook_draw_arrow (notebook, cr, ARROW_LEFT_AFTER);
5011       if (priv->has_after_next)
5012         gtk_notebook_draw_arrow (notebook, cr, ARROW_RIGHT_AFTER);
5013     }
5014
5015   if (priv->operation != DRAG_OPERATION_REORDER)
5016     gtk_notebook_draw_tab (notebook, priv->cur_page, cr);
5017 }
5018
5019 static void
5020 gtk_notebook_draw_tab (GtkNotebook     *notebook,
5021                        GtkNotebookPage *page,
5022                        cairo_t         *cr)
5023 {
5024   GtkNotebookPrivate *priv;
5025   GtkStateType state_type;
5026   GtkWidget *widget;
5027   
5028   if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5029       !gtk_widget_get_mapped (page->tab_label) ||
5030       (page->allocation.width == 0) || (page->allocation.height == 0))
5031     return;
5032
5033   widget = GTK_WIDGET (notebook);
5034   priv = notebook->priv;
5035
5036   if (priv->cur_page == page)
5037     state_type = GTK_STATE_NORMAL;
5038   else 
5039     state_type = GTK_STATE_ACTIVE;
5040
5041   gtk_paint_extension (gtk_widget_get_style (widget), cr,
5042                        state_type, GTK_SHADOW_OUT,
5043                        widget, "tab",
5044                        page->allocation.x,
5045                        page->allocation.y,
5046                        page->allocation.width,
5047                        page->allocation.height,
5048                        get_tab_gap_pos (notebook));
5049
5050   if (gtk_widget_has_focus (widget) &&
5051       priv->cur_page == page)
5052     {
5053       gint focus_width;
5054       GtkAllocation allocation;
5055
5056       gtk_widget_get_allocation (page->tab_label, &allocation);
5057       gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
5058
5059       gtk_paint_focus (gtk_widget_get_style (widget), cr,
5060                        gtk_widget_get_state (widget), widget, "tab",
5061                        allocation.x - focus_width,
5062                        allocation.y - focus_width,
5063                        allocation.width + 2 * focus_width,
5064                        allocation.height + 2 * focus_width);
5065     }
5066 }
5067
5068 static void
5069 gtk_notebook_draw_arrow (GtkNotebook      *notebook,
5070                          cairo_t          *cr,
5071                          GtkNotebookArrow  nbarrow)
5072 {
5073   GtkNotebookPrivate *priv = notebook->priv;
5074   GtkStateType state_type;
5075   GtkShadowType shadow_type;
5076   GtkWidget *widget;
5077   GdkRectangle arrow_rect;
5078   GtkArrowType arrow;
5079   gboolean is_rtl, left;
5080   gint scroll_arrow_hlength;
5081   gint scroll_arrow_vlength;
5082   gint arrow_size;
5083
5084   widget = GTK_WIDGET (notebook);
5085
5086   gtk_notebook_get_arrow_rect (notebook, &arrow_rect, nbarrow);
5087
5088   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5089   left = (ARROW_IS_LEFT (nbarrow) && !is_rtl) ||
5090          (!ARROW_IS_LEFT (nbarrow) && is_rtl); 
5091
5092   gtk_widget_style_get (widget,
5093                         "scroll-arrow-hlength", &scroll_arrow_hlength,
5094                         "scroll-arrow-vlength", &scroll_arrow_vlength,
5095                         NULL);
5096
5097   if (priv->in_child == nbarrow)
5098     {
5099       if (priv->click_child == nbarrow)
5100         state_type = GTK_STATE_ACTIVE;
5101       else
5102         state_type = GTK_STATE_PRELIGHT;
5103     }
5104   else
5105     state_type = gtk_widget_get_state (widget);
5106
5107   if (priv->click_child == nbarrow)
5108     shadow_type = GTK_SHADOW_IN;
5109   else
5110     shadow_type = GTK_SHADOW_OUT;
5111
5112   if (priv->focus_tab &&
5113       !gtk_notebook_search_page (notebook, priv->focus_tab,
5114                                  left ? STEP_PREV : STEP_NEXT, TRUE))
5115     {
5116       shadow_type = GTK_SHADOW_ETCHED_IN;
5117       state_type = GTK_STATE_INSENSITIVE;
5118     }
5119   
5120   if (priv->tab_pos == GTK_POS_LEFT ||
5121       priv->tab_pos == GTK_POS_RIGHT)
5122     {
5123       arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_UP : GTK_ARROW_DOWN);
5124       arrow_size = scroll_arrow_vlength;
5125     }
5126   else
5127     {
5128       arrow = (ARROW_IS_LEFT (nbarrow) ? GTK_ARROW_LEFT : GTK_ARROW_RIGHT);
5129       arrow_size = scroll_arrow_hlength;
5130     }
5131  
5132   gtk_paint_arrow (gtk_widget_get_style (widget),
5133                    cr, state_type, 
5134                    shadow_type, widget, "notebook",
5135                    arrow, TRUE, arrow_rect.x, arrow_rect.y, 
5136                    arrow_size, arrow_size);
5137 }
5138
5139 /* Private GtkNotebook Size Allocate Functions:
5140  *
5141  * gtk_notebook_tab_space
5142  * gtk_notebook_calculate_shown_tabs
5143  * gtk_notebook_calculate_tabs_allocation
5144  * gtk_notebook_pages_allocate
5145  * gtk_notebook_page_allocate
5146  * gtk_notebook_calc_tabs
5147  */
5148 static void
5149 gtk_notebook_tab_space (GtkNotebook *notebook,
5150                         gboolean    *show_arrows,
5151                         gint        *min,
5152                         gint        *max,
5153                         gint        *tab_space)
5154 {
5155   GtkNotebookPrivate *priv = notebook->priv;
5156   GtkAllocation allocation, action_allocation;
5157   GtkWidget *widget;
5158   GtkStyle *style;
5159   GList *children;
5160   gint tab_pos = get_effective_tab_pos (notebook);
5161   gint tab_overlap;
5162   gint arrow_spacing;
5163   gint scroll_arrow_hlength;
5164   gint scroll_arrow_vlength;
5165   gboolean is_rtl;
5166   gint i;
5167   guint border_width;
5168
5169   widget = GTK_WIDGET (notebook);
5170   children = priv->children;
5171   is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
5172
5173   style = gtk_widget_get_style (widget);
5174
5175   gtk_widget_style_get (GTK_WIDGET (notebook),
5176                         "arrow-spacing", &arrow_spacing,
5177                         "scroll-arrow-hlength", &scroll_arrow_hlength,
5178                         "scroll-arrow-vlength", &scroll_arrow_vlength,
5179                         NULL);
5180
5181   border_width = gtk_container_get_border_width (GTK_CONTAINER (notebook));
5182
5183   gtk_widget_get_allocation (widget, &allocation);
5184
5185   switch (tab_pos)
5186     {
5187     case GTK_POS_TOP:
5188     case GTK_POS_BOTTOM:
5189       *min = allocation.x + border_width;
5190       *max = allocation.x + allocation.width - border_width;
5191
5192       for (i = 0; i < N_ACTION_WIDGETS; i++)
5193         {
5194           if (priv->action_widget[i])
5195             {
5196               gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5197
5198               if ((i == ACTION_WIDGET_START && !is_rtl) ||
5199                   (i == ACTION_WIDGET_END && is_rtl))
5200                 *min += action_allocation.width + style->xthickness;
5201               else
5202                 *max -= action_allocation.width + style->xthickness;
5203             }
5204         }
5205
5206       while (children)
5207         {
5208           GtkNotebookPage *page;
5209
5210           page = children->data;
5211           children = children->next;
5212
5213           if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5214               gtk_widget_get_visible (page->child))
5215             *tab_space += page->requisition.width;
5216         }
5217       break;
5218     case GTK_POS_RIGHT:
5219     case GTK_POS_LEFT:
5220       *min = allocation.y + border_width;
5221       *max = allocation.y + allocation.height - border_width;
5222
5223       for (i = 0; i < N_ACTION_WIDGETS; i++)
5224         {
5225           if (priv->action_widget[i])
5226             {
5227               gtk_widget_get_allocation (priv->action_widget[i], &action_allocation);
5228
5229               if (i == ACTION_WIDGET_START)
5230                 *min += action_allocation.height + style->ythickness;
5231               else
5232                 *max -= action_allocation.height + style->ythickness;
5233             }
5234         }
5235
5236       while (children)
5237         {
5238           GtkNotebookPage *page;
5239
5240           page = children->data;
5241           children = children->next;
5242
5243           if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5244               gtk_widget_get_visible (page->child))
5245             *tab_space += page->requisition.height;
5246         }
5247       break;
5248     }
5249
5250   if (!priv->scrollable)
5251     *show_arrows = FALSE;
5252   else
5253     {
5254       gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5255
5256       switch (tab_pos)
5257         {
5258         case GTK_POS_TOP:
5259         case GTK_POS_BOTTOM:
5260           if (*tab_space > *max - *min - tab_overlap)
5261             {
5262               *show_arrows = TRUE;
5263
5264               /* take arrows into account */
5265               *tab_space = *max - *min - tab_overlap;
5266
5267               if (priv->has_after_previous)
5268                 {
5269                   *tab_space -= arrow_spacing + scroll_arrow_hlength;
5270                   *max -= arrow_spacing + scroll_arrow_hlength;
5271                 }
5272
5273               if (priv->has_after_next)
5274                 {
5275                   *tab_space -= arrow_spacing + scroll_arrow_hlength;
5276                   *max -= arrow_spacing + scroll_arrow_hlength;
5277                 }
5278
5279               if (priv->has_before_previous)
5280                 {
5281                   *tab_space -= arrow_spacing + scroll_arrow_hlength;
5282                   *min += arrow_spacing + scroll_arrow_hlength;
5283                 }
5284
5285               if (priv->has_before_next)
5286                 {
5287                   *tab_space -= arrow_spacing + scroll_arrow_hlength;
5288                   *min += arrow_spacing + scroll_arrow_hlength;
5289                 }
5290             }
5291           break;
5292         case GTK_POS_LEFT:
5293         case GTK_POS_RIGHT:
5294           if (*tab_space > *max - *min - tab_overlap)
5295             {
5296               *show_arrows = TRUE;
5297
5298               /* take arrows into account */
5299               *tab_space = *max - *min - tab_overlap;
5300
5301               if (priv->has_after_previous || priv->has_after_next)
5302                 {
5303                   *tab_space -= arrow_spacing + scroll_arrow_vlength;
5304                   *max -= arrow_spacing + scroll_arrow_vlength;
5305                 }
5306
5307               if (priv->has_before_previous || priv->has_before_next)
5308                 {
5309                   *tab_space -= arrow_spacing + scroll_arrow_vlength;
5310                   *min += arrow_spacing + scroll_arrow_vlength;
5311                 }
5312             }
5313           break;
5314         }
5315     }
5316 }
5317
5318 static void
5319 gtk_notebook_calculate_shown_tabs (GtkNotebook *notebook,
5320                                    gboolean     show_arrows,
5321                                    gint         min,
5322                                    gint         max,
5323                                    gint         tab_space,
5324                                    GList      **last_child,
5325                                    gint        *n,
5326                                    gint        *remaining_space)
5327 {
5328   GtkNotebookPrivate *priv = notebook->priv;
5329   GtkWidget *widget;
5330   GtkContainer *container;
5331   GList *children;
5332   GtkNotebookPage *page;
5333   gint tab_pos, tab_overlap;
5334   
5335   widget = GTK_WIDGET (notebook);
5336   container = GTK_CONTAINER (notebook);
5337   gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5338   tab_pos = get_effective_tab_pos (notebook);
5339
5340   if (show_arrows) /* first_tab <- focus_tab */
5341     {
5342       *remaining_space = tab_space;
5343
5344       if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page) &&
5345           gtk_widget_get_visible (priv->cur_page->child))
5346         {
5347           gtk_notebook_calc_tabs (notebook,
5348                                   priv->focus_tab,
5349                                   &(priv->focus_tab),
5350                                   remaining_space, STEP_NEXT);
5351         }
5352
5353       if (tab_space <= 0 || *remaining_space <= 0)
5354         {
5355           /* show 1 tab */
5356           priv->first_tab = priv->focus_tab;
5357           *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5358                                                   STEP_NEXT, TRUE);
5359           page = priv->first_tab->data;
5360           *remaining_space = tab_space - page->requisition.width;
5361           *n = 1;
5362         }
5363       else
5364         {
5365           children = NULL;
5366
5367           if (priv->first_tab && priv->first_tab != priv->focus_tab)
5368             {
5369               /* Is first_tab really predecessor of focus_tab? */
5370               page = priv->first_tab->data;
5371               if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
5372                   gtk_widget_get_visible (page->child))
5373                 for (children = priv->focus_tab;
5374                      children && children != priv->first_tab;
5375                      children = gtk_notebook_search_page (notebook,
5376                                                           children,
5377                                                           STEP_PREV,
5378                                                           TRUE));
5379             }
5380
5381           if (!children)
5382             {
5383               if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, priv->cur_page))
5384                 priv->first_tab = priv->focus_tab;
5385               else
5386                 priv->first_tab = gtk_notebook_search_page (notebook, priv->focus_tab,
5387                                                                 STEP_NEXT, TRUE);
5388             }
5389           else
5390             /* calculate shown tabs counting backwards from the focus tab */
5391             gtk_notebook_calc_tabs (notebook,
5392                                     gtk_notebook_search_page (notebook,
5393                                                               priv->focus_tab,
5394                                                               STEP_PREV,
5395                                                               TRUE),
5396                                     &(priv->first_tab), remaining_space,
5397                                     STEP_PREV);
5398
5399           if (*remaining_space < 0)
5400             {
5401               priv->first_tab =
5402                 gtk_notebook_search_page (notebook, priv->first_tab,
5403                                           STEP_NEXT, TRUE);
5404               if (!priv->first_tab)
5405                 priv->first_tab = priv->focus_tab;
5406
5407               *last_child = gtk_notebook_search_page (notebook, priv->focus_tab,
5408                                                       STEP_NEXT, TRUE); 
5409             }
5410           else /* focus_tab -> end */   
5411             {
5412               if (!priv->first_tab)
5413                 priv->first_tab = gtk_notebook_search_page (notebook,
5414                                                                 NULL,
5415                                                                 STEP_NEXT,
5416                                                                 TRUE);
5417               children = NULL;
5418               gtk_notebook_calc_tabs (notebook,
5419                                       gtk_notebook_search_page (notebook,
5420                                                                 priv->focus_tab,
5421                                                                 STEP_NEXT,
5422                                                                 TRUE),
5423                                       &children, remaining_space, STEP_NEXT);
5424
5425               if (*remaining_space <= 0) 
5426                 *last_child = children;
5427               else /* start <- first_tab */
5428                 {
5429                   *last_child = NULL;
5430                   children = NULL;
5431
5432                   gtk_notebook_calc_tabs (notebook,
5433                                           gtk_notebook_search_page (notebook,
5434                                                                     priv->first_tab,
5435                                                                     STEP_PREV,
5436                                                                     TRUE),
5437                                           &children, remaining_space, STEP_PREV);
5438
5439                   if (*remaining_space == 0)
5440                     priv->first_tab = children;
5441                   else
5442                     priv->first_tab = gtk_notebook_search_page(notebook,
5443                                                                    children,
5444                                                                    STEP_NEXT,
5445                                                                    TRUE);
5446                 }
5447             }
5448
5449           if (*remaining_space < 0)
5450             {
5451               /* calculate number of tabs */
5452               *remaining_space = - (*remaining_space);
5453               *n = 0;
5454
5455               for (children = priv->first_tab;
5456                    children && children != *last_child;
5457                    children = gtk_notebook_search_page (notebook, children,
5458                                                         STEP_NEXT, TRUE))
5459                 (*n)++;
5460             }
5461           else
5462             *remaining_space = 0;
5463         }
5464
5465       /* unmap all non-visible tabs */
5466       for (children = gtk_notebook_search_page (notebook, NULL,
5467                                                 STEP_NEXT, TRUE);
5468            children && children != priv->first_tab;
5469            children = gtk_notebook_search_page (notebook, children,
5470                                                 STEP_NEXT, TRUE))
5471         {
5472           page = children->data;
5473
5474           if (page->tab_label &&
5475               NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5476             gtk_widget_set_child_visible (page->tab_label, FALSE);
5477         }
5478
5479       for (children = *last_child; children;
5480            children = gtk_notebook_search_page (notebook, children,
5481                                                 STEP_NEXT, TRUE))
5482         {
5483           page = children->data;
5484
5485           if (page->tab_label &&
5486               NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5487             gtk_widget_set_child_visible (page->tab_label, FALSE);
5488         }
5489     }
5490   else /* !show_arrows */
5491     {
5492       GtkOrientation tab_expand_orientation;
5493       gint c = 0;
5494       *n = 0;
5495
5496       if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5497         tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5498       else
5499         tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5500       *remaining_space = max - min - tab_overlap - tab_space;
5501       children = priv->children;
5502       priv->first_tab = gtk_notebook_search_page (notebook, NULL,
5503                                                       STEP_NEXT, TRUE);
5504       while (children)
5505         {
5506           page = children->data;
5507           children = children->next;
5508
5509           if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) ||
5510               !gtk_widget_get_visible (page->child))
5511             continue;
5512
5513           c++;
5514
5515           if (page->expand ||
5516               (gtk_widget_compute_expand (page->tab_label, tab_expand_orientation)))
5517             (*n)++;
5518         }
5519
5520       /* if notebook is homogeneous, all tabs are expanded */
5521       if (priv->homogeneous && *n)
5522         *n = c;
5523     }
5524 }
5525
5526 static gboolean
5527 get_allocate_at_bottom (GtkWidget *widget,
5528                         gint       search_direction)
5529 {
5530   gboolean is_rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
5531   gboolean tab_pos = get_effective_tab_pos (GTK_NOTEBOOK (widget));
5532
5533   switch (tab_pos)
5534     {
5535     case GTK_POS_TOP:
5536     case GTK_POS_BOTTOM:
5537       if (!is_rtl)
5538         return (search_direction == STEP_PREV);
5539       else
5540         return (search_direction == STEP_NEXT);
5541
5542       break;
5543     case GTK_POS_RIGHT:
5544     case GTK_POS_LEFT:
5545       return (search_direction == STEP_PREV);
5546       break;
5547     }
5548
5549   return FALSE;
5550 }
5551
5552 static void
5553 gtk_notebook_calculate_tabs_allocation (GtkNotebook  *notebook,
5554                                         GList       **children,
5555                                         GList        *last_child,
5556                                         gboolean      showarrow,
5557                                         gint          direction,
5558                                         gint         *remaining_space,
5559                                         gint         *expanded_tabs,
5560                                         gint          min,
5561                                         gint          max)
5562 {
5563   GtkNotebookPrivate *priv = notebook->priv;
5564   GtkAllocation allocation;
5565   GtkWidget *widget;
5566   GtkContainer *container;
5567   GtkNotebookPage *page;
5568   GtkStyle *style;
5569   gboolean allocate_at_bottom;
5570   gint tab_overlap, tab_pos, tab_extra_space;
5571   gint left_x, right_x, top_y, bottom_y, anchor;
5572   gint xthickness, ythickness;
5573   guint border_width;
5574   gboolean gap_left, packing_changed;
5575   GtkAllocation child_allocation = { 0, };
5576   GtkOrientation tab_expand_orientation;
5577
5578   widget = GTK_WIDGET (notebook);
5579   container = GTK_CONTAINER (notebook);
5580   gtk_widget_style_get (widget, "tab-overlap", &tab_overlap, NULL);
5581   tab_pos = get_effective_tab_pos (notebook);
5582   allocate_at_bottom = get_allocate_at_bottom (widget, direction);
5583   anchor = 0;
5584
5585   gtk_widget_get_allocation (widget, &allocation);
5586
5587   border_width = gtk_container_get_border_width (container);
5588   child_allocation.x = allocation.x + border_width;
5589   child_allocation.y = allocation.y + border_width;
5590
5591   style = gtk_widget_get_style (widget);
5592   xthickness = style->xthickness;
5593   ythickness = style->ythickness;
5594
5595   switch (tab_pos)
5596     {
5597     case GTK_POS_BOTTOM:
5598       child_allocation.y = allocation.y + allocation.height -
5599         priv->cur_page->requisition.height - border_width;
5600       /* fall through */
5601     case GTK_POS_TOP:
5602       child_allocation.x = (allocate_at_bottom) ? max : min;
5603       child_allocation.height = priv->cur_page->requisition.height;
5604       anchor = child_allocation.x;
5605       break;
5606       
5607     case GTK_POS_RIGHT:
5608       child_allocation.x = allocation.x + allocation.width -
5609         priv->cur_page->requisition.width - border_width;
5610       /* fall through */
5611     case GTK_POS_LEFT:
5612       child_allocation.y = (allocate_at_bottom) ? max : min;
5613       child_allocation.width = priv->cur_page->requisition.width;
5614       anchor = child_allocation.y;
5615       break;
5616     }
5617
5618   left_x   = CLAMP (priv->mouse_x - priv->drag_offset_x,
5619                     min, max - priv->cur_page->allocation.width);
5620   top_y    = CLAMP (priv->mouse_y - priv->drag_offset_y,
5621                     min, max - priv->cur_page->allocation.height);
5622   right_x  = left_x + priv->cur_page->allocation.width;
5623   bottom_y = top_y + priv->cur_page->allocation.height;
5624   gap_left = packing_changed = FALSE;
5625
5626   if (priv->tab_pos == GTK_POS_TOP || priv->tab_pos == GTK_POS_BOTTOM)
5627     tab_expand_orientation = GTK_ORIENTATION_HORIZONTAL;
5628   else
5629     tab_expand_orientation = GTK_ORIENTATION_VERTICAL;
5630
5631   while (*children && *children != last_child)
5632     {
5633       page = (*children)->data;
5634
5635       if (direction == STEP_NEXT && page->pack != GTK_PACK_START)
5636         {
5637           if (!showarrow)
5638             break;
5639           else if (priv->operation == DRAG_OPERATION_REORDER)
5640             packing_changed = TRUE;
5641         }
5642
5643       if (direction == STEP_NEXT)
5644         *children = gtk_notebook_search_page (notebook, *children, direction, TRUE);
5645       else
5646         {
5647           *children = (*children)->next;
5648
5649           if (page->pack != GTK_PACK_END || !gtk_widget_get_visible (page->child))
5650             continue;
5651         }
5652
5653       if (!NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page))
5654         continue;
5655
5656       tab_extra_space = 0;
5657       if (*expanded_tabs && (showarrow || page->expand || gtk_widget_compute_expand (page->tab_label, tab_expand_orientation) || priv->homogeneous))
5658         {
5659           tab_extra_space = *remaining_space / *expanded_tabs;
5660           *remaining_space -= tab_extra_space;
5661           (*expanded_tabs)--;
5662         }
5663
5664       switch (tab_pos)
5665         {
5666         case GTK_POS_TOP:
5667         case GTK_POS_BOTTOM:
5668           child_allocation.width = page->requisition.width + tab_overlap + tab_extra_space;
5669
5670           /* make sure that the reordered tab doesn't go past the last position */
5671           if (priv->operation == DRAG_OPERATION_REORDER &&
5672               !gap_left && packing_changed)
5673             {
5674               if (!allocate_at_bottom)
5675                 {
5676                   if ((priv->cur_page->pack == GTK_PACK_START && left_x >= anchor) ||
5677                       (priv->cur_page->pack == GTK_PACK_END && left_x < anchor))
5678                     {
5679                       left_x = priv->drag_window_x = anchor;
5680                       anchor += priv->cur_page->allocation.width - tab_overlap;
5681                     }
5682                 }
5683               else
5684                 {
5685                   if ((priv->cur_page->pack == GTK_PACK_START && right_x <= anchor) ||
5686                       (priv->cur_page->pack == GTK_PACK_END && right_x > anchor))
5687                     {
5688                       anchor -= priv->cur_page->allocation.width;
5689                       left_x = priv->drag_window_x = anchor;
5690                       anchor += tab_overlap;
5691                     }
5692                 }
5693
5694               gap_left = TRUE;
5695             }
5696
5697           if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5698             {
5699               priv->drag_window_x = left_x;
5700               priv->drag_window_y = child_allocation.y;
5701             }
5702           else
5703             {
5704               if (allocate_at_bottom)
5705                 anchor -= child_allocation.width;
5706
5707               if (priv->operation == DRAG_OPERATION_REORDER && page->pack == priv->cur_page->pack)
5708                 {
5709                   if (!allocate_at_bottom &&
5710                       left_x >= anchor &&
5711                       left_x <= anchor + child_allocation.width / 2)
5712                     anchor += priv->cur_page->allocation.width - tab_overlap;
5713                   else if (allocate_at_bottom &&
5714                            right_x >= anchor + child_allocation.width / 2 &&
5715                            right_x <= anchor + child_allocation.width)
5716                     anchor -= priv->cur_page->allocation.width - tab_overlap;
5717                 }
5718
5719               child_allocation.x = anchor;
5720             }
5721
5722           break;
5723         case GTK_POS_LEFT:
5724         case GTK_POS_RIGHT:
5725           child_allocation.height = page->requisition.height + tab_overlap + tab_extra_space;
5726
5727           /* make sure that the reordered tab doesn't go past the last position */
5728           if (priv->operation == DRAG_OPERATION_REORDER &&
5729               !gap_left && packing_changed)
5730             {
5731               if (!allocate_at_bottom &&
5732                   ((priv->cur_page->pack == GTK_PACK_START && top_y >= anchor) ||
5733                    (priv->cur_page->pack == GTK_PACK_END && top_y < anchor)))
5734                 {
5735                   top_y = priv->drag_window_y = anchor;
5736                   anchor += priv->cur_page->allocation.height - tab_overlap;
5737                 }
5738  
5739               gap_left = TRUE;
5740             }
5741
5742           if (priv->operation == DRAG_OPERATION_REORDER && page == priv->cur_page)
5743             {
5744               priv->drag_window_x = child_allocation.x;
5745               priv->drag_window_y = top_y;
5746             }
5747           else
5748             {
5749               if (allocate_at_bottom)
5750                 anchor -= child_allocation.height;
5751
5752               if (priv->operation == DRAG_OPERATION_REORDER && page->pack == priv->cur_page->pack)
5753                 {
5754                   if (!allocate_at_bottom &&
5755                       top_y >= anchor &&
5756                       top_y <= anchor + child_allocation.height / 2)
5757                     anchor += priv->cur_page->allocation.height - tab_overlap;
5758                   else if (allocate_at_bottom &&
5759                            bottom_y >= anchor + child_allocation.height / 2 &&
5760                            bottom_y <= anchor + child_allocation.height)
5761                     anchor -= priv->cur_page->allocation.height - tab_overlap;
5762                 }
5763
5764               child_allocation.y = anchor;
5765             }
5766
5767           break;
5768         }
5769
5770       page->allocation = child_allocation;
5771
5772       if ((page == priv->detached_tab && priv->operation == DRAG_OPERATION_DETACH) ||
5773           (page == priv->cur_page && priv->operation == DRAG_OPERATION_REORDER))
5774         {
5775           /* needs to be allocated at 0,0
5776            * to be shown in the drag window */
5777           page->allocation.x = 0;
5778           page->allocation.y = 0;
5779         }
5780       
5781       if (page != priv->cur_page)
5782         {
5783           switch (tab_pos)
5784             {
5785             case GTK_POS_TOP:
5786               page->allocation.y += ythickness;
5787               /* fall through */
5788             case GTK_POS_BOTTOM:
5789               page->allocation.height = MAX (1, page->allocation.height - ythickness);
5790               break;
5791             case GTK_POS_LEFT:
5792               page->allocation.x += xthickness;
5793               /* fall through */
5794             case GTK_POS_RIGHT:
5795               page->allocation.width = MAX (1, page->allocation.width - xthickness);
5796               break;
5797             }
5798         }
5799
5800       /* calculate whether to leave a gap based on reorder operation or not */
5801       switch (tab_pos)
5802         {
5803         case GTK_POS_TOP:
5804         case GTK_POS_BOTTOM:
5805           if (priv->operation != DRAG_OPERATION_REORDER ||
5806               (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
5807             {
5808               if (priv->operation == DRAG_OPERATION_REORDER)
5809                 {
5810                   if (page->pack == priv->cur_page->pack &&
5811                       !allocate_at_bottom &&
5812                       left_x >  anchor + child_allocation.width / 2 &&
5813                       left_x <= anchor + child_allocation.width)
5814                     anchor += priv->cur_page->allocation.width - tab_overlap;
5815                   else if (page->pack == priv->cur_page->pack &&
5816                            allocate_at_bottom &&
5817                            right_x >= anchor &&
5818                            right_x <= anchor + child_allocation.width / 2)
5819                     anchor -= priv->cur_page->allocation.width - tab_overlap;
5820                 }
5821  
5822               if (!allocate_at_bottom)
5823                 anchor += child_allocation.width - tab_overlap;
5824               else
5825                 anchor += tab_overlap;
5826             }
5827
5828           break;
5829         case GTK_POS_LEFT:
5830         case GTK_POS_RIGHT:
5831           if (priv->operation != DRAG_OPERATION_REORDER  ||
5832               (priv->operation == DRAG_OPERATION_REORDER && page != priv->cur_page))
5833             {
5834               if (priv->operation == DRAG_OPERATION_REORDER)
5835                 {
5836                   if (page->pack == priv->cur_page->pack &&
5837                       !allocate_at_bottom &&
5838                       top_y >= anchor + child_allocation.height / 2 &&
5839                       top_y <= anchor + child_allocation.height)
5840                     anchor += priv->cur_page->allocation.height - tab_overlap;
5841                   else if (page->pack == priv->cur_page->pack &&
5842                            allocate_at_bottom &&
5843                            bottom_y >= anchor &&
5844                            bottom_y <= anchor + child_allocation.height / 2)
5845                     anchor -= priv->cur_page->allocation.height - tab_overlap;
5846                 }
5847
5848               if (!allocate_at_bottom)
5849                 anchor += child_allocation.height - tab_overlap;
5850               else
5851                 anchor += tab_overlap;
5852             }
5853
5854           break;
5855         }
5856
5857       /* set child visible */
5858       if (page->tab_label)
5859         gtk_widget_set_child_visible (page->tab_label, TRUE);
5860     }
5861
5862   /* Don't move the current tab past the last position during tabs reordering */
5863   if (children &&
5864       priv->operation == DRAG_OPERATION_REORDER &&
5865       ((direction == STEP_NEXT && priv->cur_page->pack == GTK_PACK_START) ||
5866        ((direction == STEP_PREV || packing_changed) && priv->cur_page->pack == GTK_PACK_END)))
5867     {
5868       switch (tab_pos)
5869         {
5870         case GTK_POS_TOP:
5871         case GTK_POS_BOTTOM:
5872           if (allocate_at_bottom)
5873             anchor -= priv->cur_page->allocation.width;
5874
5875           if ((!allocate_at_bottom && priv->drag_window_x > anchor) ||
5876               (allocate_at_bottom && priv->drag_window_x < anchor))
5877             priv->drag_window_x = anchor;
5878           break;
5879         case GTK_POS_LEFT:
5880         case GTK_POS_RIGHT:
5881           if (allocate_at_bottom)
5882             anchor -= priv->cur_page->allocation.height;
5883
5884           if ((!allocate_at_bottom && priv->drag_window_y > anchor) ||
5885               (allocate_at_bottom && priv->drag_window_y < anchor))
5886             priv->drag_window_y = anchor;
5887           break;
5888         }
5889     }
5890 }
5891
5892 static void
5893 gtk_notebook_pages_allocate (GtkNotebook *notebook)
5894 {
5895   GtkNotebookPrivate *priv = notebook->priv;
5896   GList *children = NULL;
5897   GList *last_child = NULL;
5898   gboolean showarrow = FALSE;
5899   gint tab_space, min, max, remaining_space;
5900   gint expanded_tabs;
5901   gboolean tab_allocations_changed = FALSE;
5902
5903   if (!priv->show_tabs || !priv->children || !priv->cur_page)
5904     return;
5905
5906   min = max = tab_space = remaining_space = 0;
5907   expanded_tabs = 1;
5908
5909   gtk_notebook_tab_space (notebook, &showarrow,
5910                           &min, &max, &tab_space);
5911
5912   gtk_notebook_calculate_shown_tabs (notebook, showarrow,
5913                                      min, max, tab_space, &last_child,
5914                                      &expanded_tabs, &remaining_space);
5915
5916   children = priv->first_tab;
5917   gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5918                                           showarrow, STEP_NEXT,
5919                                           &remaining_space, &expanded_tabs, min, max);
5920   if (children && children != last_child)
5921     {
5922       children = priv->children;
5923       gtk_notebook_calculate_tabs_allocation (notebook, &children, last_child,
5924                                               showarrow, STEP_PREV,
5925                                               &remaining_space, &expanded_tabs, min, max);
5926     }
5927
5928   children = priv->children;
5929
5930   while (children)
5931     {
5932       if (gtk_notebook_page_allocate (notebook, GTK_NOTEBOOK_PAGE (children)))
5933         tab_allocations_changed = TRUE;
5934       children = children->next;
5935     }
5936
5937   if (!priv->first_tab)
5938     priv->first_tab = priv->children;
5939
5940   if (tab_allocations_changed)
5941     gtk_notebook_redraw_tabs (notebook);
5942 }
5943
5944 static gboolean
5945 gtk_notebook_page_allocate (GtkNotebook     *notebook,
5946                             GtkNotebookPage *page)
5947 {
5948   GtkWidget *widget = GTK_WIDGET (notebook);
5949   GtkNotebookPrivate *priv = notebook->priv;
5950   GtkAllocation child_allocation, label_allocation;
5951   GtkRequisition tab_requisition;
5952   GtkStyle *style;
5953   gint xthickness;
5954   gint ythickness;
5955   gint padding;
5956   gint focus_width;
5957   gint tab_curvature;
5958   gint tab_pos = get_effective_tab_pos (notebook);
5959   gboolean tab_allocation_changed;
5960   gboolean was_visible = page->tab_allocated_visible;
5961
5962   if (!page->tab_label ||
5963       !gtk_widget_get_visible (page->tab_label) ||
5964       !gtk_widget_get_child_visible (page->tab_label))
5965     {
5966       page->tab_allocated_visible = FALSE;
5967       return was_visible;
5968     }
5969
5970   style = gtk_widget_get_style (widget);
5971   xthickness = style->xthickness;
5972   ythickness = style->ythickness;
5973
5974   gtk_widget_get_preferred_size (page->tab_label, &tab_requisition, NULL);
5975   gtk_widget_style_get (widget,
5976                         "focus-line-width", &focus_width,
5977                         "tab-curvature", &tab_curvature,
5978                         NULL);
5979   switch (tab_pos)
5980     {
5981     case GTK_POS_TOP:
5982     case GTK_POS_BOTTOM:
5983       padding = tab_curvature + focus_width + priv->tab_hborder;
5984       if (page->fill)
5985         {
5986           child_allocation.x = xthickness + focus_width + priv->tab_hborder;
5987           child_allocation.width = MAX (1, page->allocation.width - 2 * child_allocation.x);
5988           child_allocation.x += page->allocation.x;
5989         }
5990       else
5991         {
5992           child_allocation.x = page->allocation.x +
5993             (page->allocation.width - tab_requisition.width) / 2;
5994
5995           child_allocation.width = tab_requisition.width;
5996         }
5997
5998       child_allocation.y = priv->tab_vborder + focus_width + page->allocation.y;
5999
6000       if (tab_pos == GTK_POS_TOP)
6001         child_allocation.y += ythickness;
6002
6003       child_allocation.height = MAX (1, (page->allocation.height - ythickness -
6004                                          2 * (priv->tab_vborder + focus_width)));
6005       break;
6006     case GTK_POS_LEFT:
6007     case GTK_POS_RIGHT:
6008       padding = tab_curvature + focus_width + priv->tab_vborder;
6009       if (page->fill)
6010         {
6011           child_allocation.y = ythickness + padding;
6012           child_allocation.height = MAX (1, (page->allocation.height -
6013                                              2 * child_allocation.y));
6014           child_allocation.y += page->allocation.y;
6015         }
6016       else
6017         {
6018           child_allocation.y = page->allocation.y +
6019             (page->allocation.height - tab_requisition.height) / 2;
6020
6021           child_allocation.height = tab_requisition.height;
6022         }
6023
6024       child_allocation.x = priv->tab_hborder + focus_width + page->allocation.x;
6025
6026       if (tab_pos == GTK_POS_LEFT)
6027         child_allocation.x += xthickness;
6028
6029       child_allocation.width = MAX (1, (page->allocation.width - xthickness -
6030                                         2 * (priv->tab_hborder + focus_width)));
6031       break;
6032     }
6033
6034   gtk_widget_get_allocation (page->tab_label, &label_allocation);
6035   tab_allocation_changed = (child_allocation.x != label_allocation.x ||
6036                             child_allocation.y != label_allocation.y ||
6037                             child_allocation.width != label_allocation.width ||
6038                             child_allocation.height != label_allocation.height);
6039
6040   gtk_widget_size_allocate (page->tab_label, &child_allocation);
6041
6042   if (!was_visible)
6043     {
6044       page->tab_allocated_visible = TRUE;
6045       tab_allocation_changed = TRUE;
6046     }
6047
6048   return tab_allocation_changed;
6049 }
6050
6051 static void 
6052 gtk_notebook_calc_tabs (GtkNotebook  *notebook,
6053                         GList        *start,
6054                         GList       **end,
6055                         gint         *tab_space,
6056                         guint         direction)
6057 {
6058   GtkNotebookPage *page = NULL;
6059   GList *children;
6060   GList *last_list = NULL;
6061   GList *last_calculated_child = NULL;
6062   gboolean pack;
6063   gint tab_pos = get_effective_tab_pos (notebook);
6064   guint real_direction;
6065
6066   if (!start)
6067     return;
6068
6069   children = start;
6070   pack = GTK_NOTEBOOK_PAGE (start)->pack;
6071   if (pack == GTK_PACK_END)
6072     real_direction = (direction == STEP_PREV) ? STEP_NEXT : STEP_PREV;
6073   else
6074     real_direction = direction;
6075
6076   while (1)
6077     {
6078       switch (tab_pos)
6079         {
6080         case GTK_POS_TOP:
6081         case GTK_POS_BOTTOM:
6082           while (children)
6083             {
6084               page = children->data;
6085               if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6086                   gtk_widget_get_visible (page->child))
6087                 {
6088                   if (page->pack == pack)
6089                     {
6090                       *tab_space -= page->requisition.width;
6091                       if (*tab_space < 0 || children == *end)
6092                         {
6093                           if (*tab_space < 0) 
6094                             {
6095                               *tab_space = - (*tab_space +
6096                                               page->requisition.width);
6097
6098                               if (*tab_space == 0 && direction == STEP_PREV)
6099                                 children = last_calculated_child;
6100
6101                               *end = children;
6102                             }
6103                           return;
6104                         }
6105
6106                       last_calculated_child = children;
6107                     }
6108                   last_list = children;
6109                 }
6110               if (real_direction == STEP_NEXT)
6111                 children = children->next;
6112               else
6113                 children = children->prev;
6114             }
6115           break;
6116         case GTK_POS_LEFT:
6117         case GTK_POS_RIGHT:
6118           while (children)
6119             {
6120               page = children->data;
6121               if (NOTEBOOK_IS_TAB_LABEL_PARENT (notebook, page) &&
6122                   gtk_widget_get_visible (page->child))
6123                 {
6124                   if (page->pack == pack)
6125                     {
6126                       *tab_space -= page->requisition.height;
6127                       if (*tab_space < 0 || children == *end)
6128                         {
6129                           if (*tab_space < 0)
6130                             {
6131                               *tab_space = - (*tab_space +
6132                                               page->requisition.height);
6133
6134                               if (*tab_space == 0 && direction == STEP_PREV)
6135                                 children = last_calculated_child;
6136
6137                               *end = children;
6138                             }
6139                           return;
6140                         }
6141
6142                       last_calculated_child = children;
6143                     }
6144                   last_list = children;
6145                 }
6146               if (real_direction == STEP_NEXT)
6147                 children = children->next;
6148               else
6149                 children = children->prev;
6150             }
6151           break;
6152         }
6153       if (real_direction == STEP_PREV)
6154         return;
6155       pack = (pack == GTK_PACK_END) ? GTK_PACK_START : GTK_PACK_END;
6156       real_direction = STEP_PREV;
6157       children = last_list;
6158     }
6159 }
6160
6161 static void
6162 gtk_notebook_update_tab_states (GtkNotebook *notebook)
6163 {
6164   GtkNotebookPrivate *priv = notebook->priv;
6165   GList *list;
6166
6167   for (list = priv->children; list != NULL; list = list->next)
6168     {
6169       GtkNotebookPage *page = list->data;
6170       
6171       if (page->tab_label)
6172         {
6173           if (page == priv->cur_page)
6174             gtk_widget_set_state (page->tab_label, GTK_STATE_NORMAL);
6175           else
6176             gtk_widget_set_state (page->tab_label, GTK_STATE_ACTIVE);
6177         }
6178     }
6179 }
6180
6181 /* Private GtkNotebook Page Switch Methods:
6182  *
6183  * gtk_notebook_real_switch_page
6184  */
6185 static void
6186 gtk_notebook_real_switch_page (GtkNotebook     *notebook,
6187                                GtkWidget*       child,
6188                                guint            page_num)
6189 {
6190   GtkNotebookPrivate *priv = notebook->priv;
6191   GList *list = gtk_notebook_find_child (notebook, GTK_WIDGET (child), NULL);
6192   GtkNotebookPage *page = GTK_NOTEBOOK_PAGE (list);
6193   gboolean child_has_focus;
6194
6195   if (priv->cur_page == page || !gtk_widget_get_visible (GTK_WIDGET (child)))
6196     return;
6197
6198   /* save the value here, changing visibility changes focus */
6199   child_has_focus = priv->child_has_focus;
6200
6201   if (priv->cur_page)
6202     gtk_widget_set_child_visible (priv->cur_page->child, FALSE);
6203
6204   priv->cur_page = page;
6205
6206   if (!priv->focus_tab ||
6207       priv->focus_tab->data != (gpointer) priv->cur_page)
6208     priv->focus_tab =
6209       g_list_find (priv->children, priv->cur_page);
6210
6211   gtk_widget_set_child_visible (priv->cur_page->child, TRUE);
6212
6213   /* If the focus was on the previous page, move it to the first
6214    * element on the new page, if possible, or if not, to the
6215    * notebook itself.
6216    */
6217   if (child_has_focus)
6218     {
6219       if (priv->cur_page->last_focus_child &&
6220           gtk_widget_is_ancestor (priv->cur_page->last_focus_child, priv->cur_page->child))
6221         gtk_widget_grab_focus (priv->cur_page->last_focus_child);
6222       else
6223         if (!gtk_widget_child_focus (priv->cur_page->child, GTK_DIR_TAB_FORWARD))
6224           gtk_widget_grab_focus (GTK_WIDGET (notebook));
6225     }
6226   
6227   gtk_notebook_update_tab_states (notebook);
6228   gtk_widget_queue_resize (GTK_WIDGET (notebook));
6229   g_object_notify (G_OBJECT (notebook), "page");
6230 }
6231
6232 /* Private GtkNotebook Page Switch Functions:
6233  *
6234  * gtk_notebook_switch_page
6235  * gtk_notebook_page_select
6236  * gtk_notebook_switch_focus_tab
6237  * gtk_notebook_menu_switch_page
6238  */
6239 static void
6240 gtk_notebook_switch_page (GtkNotebook     *notebook,
6241                           GtkNotebookPage *page)
6242 {
6243   GtkNotebookPrivate *priv = notebook->priv;
6244   guint page_num;
6245
6246   if (priv->cur_page == page)
6247     return;
6248
6249   page_num = g_list_index (priv->children, page);
6250
6251   g_signal_emit (notebook,
6252                  notebook_signals[SWITCH_PAGE],
6253                  0,
6254                  page->child,
6255                  page_num);
6256 }
6257
6258 static gint
6259 gtk_notebook_page_select (GtkNotebook *notebook,
6260                           gboolean     move_focus)
6261 {
6262   GtkNotebookPrivate *priv = notebook->priv;
6263   GtkNotebookPage *page;
6264   GtkDirectionType dir = GTK_DIR_DOWN; /* Quiet GCC */
6265   gint tab_pos = get_effective_tab_pos (notebook);
6266
6267   if (!priv->focus_tab)
6268     return FALSE;
6269
6270   page = priv->focus_tab->data;
6271   gtk_notebook_switch_page (notebook, page);
6272
6273   if (move_focus)
6274     {
6275       switch (tab_pos)
6276         {
6277         case GTK_POS_TOP:
6278           dir = GTK_DIR_DOWN;
6279           break;
6280         case GTK_POS_BOTTOM:
6281           dir = GTK_DIR_UP;
6282           break;
6283         case GTK_POS_LEFT:
6284           dir = GTK_DIR_RIGHT;
6285           break;
6286         case GTK_POS_RIGHT:
6287           dir = GTK_DIR_LEFT;
6288           break;
6289         }
6290
6291       if (gtk_widget_child_focus (page->child, dir))
6292         return TRUE;
6293     }
6294   return FALSE;
6295 }
6296
6297 static void
6298 gtk_notebook_switch_focus_tab (GtkNotebook *notebook, 
6299                                GList       *new_child)
6300 {
6301   GtkNotebookPrivate *priv = notebook->priv;
6302   GList *old_child;
6303   GtkNotebookPage *page;
6304
6305   if (priv->focus_tab == new_child)
6306     return;
6307
6308   old_child = priv->focus_tab;
6309   priv->focus_tab = new_child;
6310
6311   if (priv->scrollable)
6312     gtk_notebook_redraw_arrows (notebook);
6313
6314   if (!priv->show_tabs || !priv->focus_tab)
6315     return;
6316
6317   page = priv->focus_tab->data;
6318   if (gtk_widget_get_mapped (page->tab_label))
6319     gtk_notebook_redraw_tabs (notebook);
6320   else
6321     gtk_notebook_pages_allocate (notebook);
6322
6323   gtk_notebook_switch_page (notebook, page);
6324 }
6325
6326 static void
6327 gtk_notebook_menu_switch_page (GtkWidget       *widget,
6328                                GtkNotebookPage *page)
6329 {
6330   GtkNotebookPrivate *priv;
6331   GtkNotebook *notebook;
6332   GtkWidget *parent;
6333   GList *children;
6334   guint page_num;
6335
6336   parent = gtk_widget_get_parent (widget);
6337   notebook = GTK_NOTEBOOK (gtk_menu_get_attach_widget (GTK_MENU (parent)));
6338   priv = notebook->priv;
6339
6340   if (priv->cur_page == page)
6341     return;
6342
6343   page_num = 0;
6344   children = priv->children;
6345   while (children && children->data != page)
6346     {
6347       children = children->next;
6348       page_num++;
6349     }
6350
6351   g_signal_emit (notebook,
6352                  notebook_signals[SWITCH_PAGE],
6353                  0,
6354                  page->child,
6355                  page_num);
6356 }
6357
6358 /* Private GtkNotebook Menu Functions:
6359  *
6360  * gtk_notebook_menu_item_create
6361  * gtk_notebook_menu_label_unparent
6362  * gtk_notebook_menu_detacher
6363  */
6364 static void
6365 gtk_notebook_menu_item_create (GtkNotebook *notebook, 
6366                                GList       *list)
6367 {
6368   GtkNotebookPrivate *priv = notebook->priv;
6369   GtkNotebookPage *page;
6370   GtkWidget *menu_item;
6371
6372   page = list->data;
6373   if (page->default_menu)
6374     {
6375       if (GTK_IS_LABEL (page->tab_label))
6376         page->menu_label = gtk_label_new (gtk_label_get_label (GTK_LABEL (page->tab_label)));
6377       else
6378         page->menu_label = gtk_label_new ("");
6379       gtk_misc_set_alignment (GTK_MISC (page->menu_label), 0.0, 0.5);
6380     }
6381
6382   gtk_widget_show (page->menu_label);
6383   menu_item = gtk_menu_item_new ();
6384   gtk_container_add (GTK_CONTAINER (menu_item), page->menu_label);
6385   gtk_menu_shell_insert (GTK_MENU_SHELL (priv->menu), menu_item,
6386                          gtk_notebook_real_page_position (notebook, list));
6387   g_signal_connect (menu_item, "activate",
6388                     G_CALLBACK (gtk_notebook_menu_switch_page), page);
6389   if (gtk_widget_get_visible (page->child))
6390     gtk_widget_show (menu_item);
6391 }
6392
6393 static void
6394 gtk_notebook_menu_label_unparent (GtkWidget *widget, 
6395                                   gpointer  data)
6396 {
6397   gtk_widget_unparent (gtk_bin_get_child (GTK_BIN (widget)));
6398   _gtk_bin_set_child (GTK_BIN (widget), NULL);
6399 }
6400
6401 static void
6402 gtk_notebook_menu_detacher (GtkWidget *widget,
6403                             GtkMenu   *menu)
6404 {
6405   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
6406   GtkNotebookPrivate *priv = notebook->priv;
6407
6408   g_return_if_fail (priv->menu == (GtkWidget*) menu);
6409
6410   priv->menu = NULL;
6411 }
6412
6413 /* Public GtkNotebook Page Insert/Remove Methods :
6414  *
6415  * gtk_notebook_append_page
6416  * gtk_notebook_append_page_menu
6417  * gtk_notebook_prepend_page
6418  * gtk_notebook_prepend_page_menu
6419  * gtk_notebook_insert_page
6420  * gtk_notebook_insert_page_menu
6421  * gtk_notebook_remove_page
6422  */
6423 /**
6424  * gtk_notebook_append_page:
6425  * @notebook: a #GtkNotebook
6426  * @child: the #GtkWidget to use as the contents of the page.
6427  * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6428  *             or %NULL to use the default label, 'page N'.
6429  *
6430  * Appends a page to @notebook.
6431  *
6432  * Return value: the index (starting from 0) of the appended
6433  * page in the notebook, or -1 if function fails
6434  **/
6435 gint
6436 gtk_notebook_append_page (GtkNotebook *notebook,
6437                           GtkWidget   *child,
6438                           GtkWidget   *tab_label)
6439 {
6440   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6441   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6442   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6443   
6444   return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, -1);
6445 }
6446
6447 /**
6448  * gtk_notebook_append_page_menu:
6449  * @notebook: a #GtkNotebook
6450  * @child: the #GtkWidget to use as the contents of the page.
6451  * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6452  *             or %NULL to use the default label, 'page N'.
6453  * @menu_label: (allow-none): the widget to use as a label for the page-switch
6454  *              menu, if that is enabled. If %NULL, and @tab_label
6455  *              is a #GtkLabel or %NULL, then the menu label will be
6456  *              a newly created label with the same text as @tab_label;
6457  *              If @tab_label is not a #GtkLabel, @menu_label must be
6458  *              specified if the page-switch menu is to be used.
6459  * 
6460  * Appends a page to @notebook, specifying the widget to use as the
6461  * label in the popup menu.
6462  *
6463  * Return value: the index (starting from 0) of the appended
6464  * page in the notebook, or -1 if function fails
6465  **/
6466 gint
6467 gtk_notebook_append_page_menu (GtkNotebook *notebook,
6468                                GtkWidget   *child,
6469                                GtkWidget   *tab_label,
6470                                GtkWidget   *menu_label)
6471 {
6472   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6473   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6474   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6475   g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6476   
6477   return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, -1);
6478 }
6479
6480 /**
6481  * gtk_notebook_prepend_page:
6482  * @notebook: a #GtkNotebook
6483  * @child: the #GtkWidget to use as the contents of the page.
6484  * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6485  *             or %NULL to use the default label, 'page N'.
6486  *
6487  * Prepends a page to @notebook.
6488  *
6489  * Return value: the index (starting from 0) of the prepended
6490  * page in the notebook, or -1 if function fails
6491  **/
6492 gint
6493 gtk_notebook_prepend_page (GtkNotebook *notebook,
6494                            GtkWidget   *child,
6495                            GtkWidget   *tab_label)
6496 {
6497   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6498   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6499   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6500   
6501   return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, 0);
6502 }
6503
6504 /**
6505  * gtk_notebook_prepend_page_menu:
6506  * @notebook: a #GtkNotebook
6507  * @child: the #GtkWidget to use as the contents of the page.
6508  * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6509  *             or %NULL to use the default label, 'page N'.
6510  * @menu_label: (allow-none): the widget to use as a label for the page-switch
6511  *              menu, if that is enabled. If %NULL, and @tab_label
6512  *              is a #GtkLabel or %NULL, then the menu label will be
6513  *              a newly created label with the same text as @tab_label;
6514  *              If @tab_label is not a #GtkLabel, @menu_label must be
6515  *              specified if the page-switch menu is to be used.
6516  * 
6517  * Prepends a page to @notebook, specifying the widget to use as the
6518  * label in the popup menu.
6519  *
6520  * Return value: the index (starting from 0) of the prepended
6521  * page in the notebook, or -1 if function fails
6522  **/
6523 gint
6524 gtk_notebook_prepend_page_menu (GtkNotebook *notebook,
6525                                 GtkWidget   *child,
6526                                 GtkWidget   *tab_label,
6527                                 GtkWidget   *menu_label)
6528 {
6529   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6530   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6531   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6532   g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6533   
6534   return gtk_notebook_insert_page_menu (notebook, child, tab_label, menu_label, 0);
6535 }
6536
6537 /**
6538  * gtk_notebook_insert_page:
6539  * @notebook: a #GtkNotebook
6540  * @child: the #GtkWidget to use as the contents of the page.
6541  * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6542  *             or %NULL to use the default label, 'page N'.
6543  * @position: the index (starting at 0) at which to insert the page,
6544  *            or -1 to append the page after all other pages.
6545  *
6546  * Insert a page into @notebook at the given position.
6547  *
6548  * Return value: the index (starting from 0) of the inserted
6549  * page in the notebook, or -1 if function fails
6550  **/
6551 gint
6552 gtk_notebook_insert_page (GtkNotebook *notebook,
6553                           GtkWidget   *child,
6554                           GtkWidget   *tab_label,
6555                           gint         position)
6556 {
6557   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6558   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6559   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6560   
6561   return gtk_notebook_insert_page_menu (notebook, child, tab_label, NULL, position);
6562 }
6563
6564
6565 static gint
6566 gtk_notebook_page_compare_tab (gconstpointer a,
6567                                gconstpointer b)
6568 {
6569   return (((GtkNotebookPage *) a)->tab_label != b);
6570 }
6571
6572 static gboolean
6573 gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
6574                                             gboolean overload,
6575                                             gpointer data)
6576 {
6577   GtkNotebook *notebook = GTK_NOTEBOOK (data);
6578   GtkNotebookPrivate *priv = notebook->priv;
6579   GList *list;
6580
6581   list = g_list_find_custom (priv->children, child,
6582                              gtk_notebook_page_compare_tab);
6583   if (list)
6584     {
6585       GtkNotebookPage *page = list->data;
6586
6587       gtk_widget_grab_focus (GTK_WIDGET (notebook));    /* Do this first to avoid focusing new page */
6588       gtk_notebook_switch_page (notebook, page);
6589       focus_tabs_in (notebook);
6590     }
6591
6592   return TRUE;
6593 }
6594
6595 /**
6596  * gtk_notebook_insert_page_menu:
6597  * @notebook: a #GtkNotebook
6598  * @child: the #GtkWidget to use as the contents of the page.
6599  * @tab_label: (allow-none): the #GtkWidget to be used as the label for the page,
6600  *             or %NULL to use the default label, 'page N'.
6601  * @menu_label: (allow-none): the widget to use as a label for the page-switch
6602  *              menu, if that is enabled. If %NULL, and @tab_label
6603  *              is a #GtkLabel or %NULL, then the menu label will be
6604  *              a newly created label with the same text as @tab_label;
6605  *              If @tab_label is not a #GtkLabel, @menu_label must be
6606  *              specified if the page-switch menu is to be used.
6607  * @position: the index (starting at 0) at which to insert the page,
6608  *            or -1 to append the page after all other pages.
6609  * 
6610  * Insert a page into @notebook at the given position, specifying
6611  * the widget to use as the label in the popup menu.
6612  *
6613  * Return value: the index (starting from 0) of the inserted
6614  * page in the notebook
6615  **/
6616 gint
6617 gtk_notebook_insert_page_menu (GtkNotebook *notebook,
6618                                GtkWidget   *child,
6619                                GtkWidget   *tab_label,
6620                                GtkWidget   *menu_label,
6621                                gint         position)
6622 {
6623   GtkNotebookClass *class;
6624
6625   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6626   g_return_val_if_fail (GTK_IS_WIDGET (child), -1);
6627   g_return_val_if_fail (tab_label == NULL || GTK_IS_WIDGET (tab_label), -1);
6628   g_return_val_if_fail (menu_label == NULL || GTK_IS_WIDGET (menu_label), -1);
6629
6630   class = GTK_NOTEBOOK_GET_CLASS (notebook);
6631
6632   return (class->insert_page) (notebook, child, tab_label, menu_label, position);
6633 }
6634
6635 /**
6636  * gtk_notebook_remove_page:
6637  * @notebook: a #GtkNotebook.
6638  * @page_num: the index of a notebook page, starting
6639  *            from 0. If -1, the last page will
6640  *            be removed.
6641  * 
6642  * Removes a page from the notebook given its index
6643  * in the notebook.
6644  **/
6645 void
6646 gtk_notebook_remove_page (GtkNotebook *notebook,
6647                           gint         page_num)
6648 {
6649   GtkNotebookPrivate *priv;
6650   GList *list = NULL;
6651
6652   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6653
6654   priv = notebook->priv;
6655
6656   if (page_num >= 0)
6657     list = g_list_nth (priv->children, page_num);
6658   else
6659     list = g_list_last (priv->children);
6660
6661   if (list)
6662     gtk_container_remove (GTK_CONTAINER (notebook),
6663                           ((GtkNotebookPage *) list->data)->child);
6664 }
6665
6666 /* Public GtkNotebook Page Switch Methods :
6667  * gtk_notebook_get_current_page
6668  * gtk_notebook_page_num
6669  * gtk_notebook_set_current_page
6670  * gtk_notebook_next_page
6671  * gtk_notebook_prev_page
6672  */
6673 /**
6674  * gtk_notebook_get_current_page:
6675  * @notebook: a #GtkNotebook
6676  * 
6677  * Returns the page number of the current page.
6678  * 
6679  * Return value: the index (starting from 0) of the current
6680  * page in the notebook. If the notebook has no pages, then
6681  * -1 will be returned.
6682  **/
6683 gint
6684 gtk_notebook_get_current_page (GtkNotebook *notebook)
6685 {
6686   GtkNotebookPrivate *priv;
6687
6688   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6689
6690   priv = notebook->priv;
6691
6692   if (!priv->cur_page)
6693     return -1;
6694
6695   return g_list_index (priv->children, priv->cur_page);
6696 }
6697
6698 /**
6699  * gtk_notebook_get_nth_page:
6700  * @notebook: a #GtkNotebook
6701  * @page_num: the index of a page in the notebook, or -1
6702  *            to get the last page.
6703  * 
6704  * Returns the child widget contained in page number @page_num.
6705  *
6706  * Return value: (transfer none): the child widget, or %NULL if @page_num is
6707  * out of bounds.
6708  **/
6709 GtkWidget*
6710 gtk_notebook_get_nth_page (GtkNotebook *notebook,
6711                            gint         page_num)
6712 {
6713   GtkNotebookPrivate *priv;
6714   GtkNotebookPage *page;
6715   GList *list;
6716
6717   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
6718
6719   priv = notebook->priv;
6720
6721   if (page_num >= 0)
6722     list = g_list_nth (priv->children, page_num);
6723   else
6724     list = g_list_last (priv->children);
6725
6726   if (list)
6727     {
6728       page = list->data;
6729       return page->child;
6730     }
6731
6732   return NULL;
6733 }
6734
6735 /**
6736  * gtk_notebook_get_n_pages:
6737  * @notebook: a #GtkNotebook
6738  * 
6739  * Gets the number of pages in a notebook.
6740  * 
6741  * Return value: the number of pages in the notebook.
6742  *
6743  * Since: 2.2
6744  **/
6745 gint
6746 gtk_notebook_get_n_pages (GtkNotebook *notebook)
6747 {
6748   GtkNotebookPrivate *priv;
6749
6750   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), 0);
6751
6752   priv = notebook->priv;
6753
6754   return g_list_length (priv->children);
6755 }
6756
6757 /**
6758  * gtk_notebook_page_num:
6759  * @notebook: a #GtkNotebook
6760  * @child: a #GtkWidget
6761  * 
6762  * Finds the index of the page which contains the given child
6763  * widget.
6764  * 
6765  * Return value: the index of the page containing @child, or
6766  *   -1 if @child is not in the notebook.
6767  **/
6768 gint
6769 gtk_notebook_page_num (GtkNotebook      *notebook,
6770                        GtkWidget        *child)
6771 {
6772   GtkNotebookPrivate *priv;
6773   GList *children;
6774   gint num;
6775
6776   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), -1);
6777
6778   priv = notebook->priv;
6779
6780   num = 0;
6781   children = priv->children;
6782   while (children)
6783     {
6784       GtkNotebookPage *page =  children->data;
6785       
6786       if (page->child == child)
6787         return num;
6788
6789       children = children->next;
6790       num++;
6791     }
6792
6793   return -1;
6794 }
6795
6796 /**
6797  * gtk_notebook_set_current_page:
6798  * @notebook: a #GtkNotebook
6799  * @page_num: index of the page to switch to, starting from 0.
6800  *            If negative, the last page will be used. If greater
6801  *            than the number of pages in the notebook, nothing
6802  *            will be done.
6803  *                
6804  * Switches to the page number @page_num. 
6805  *
6806  * Note that due to historical reasons, GtkNotebook refuses
6807  * to switch to a page unless the child widget is visible. 
6808  * Therefore, it is recommended to show child widgets before
6809  * adding them to a notebook. 
6810  */
6811 void
6812 gtk_notebook_set_current_page (GtkNotebook *notebook,
6813                                gint         page_num)
6814 {
6815   GtkNotebookPrivate *priv;
6816   GList *list;
6817
6818   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6819
6820   priv = notebook->priv;
6821
6822   if (page_num < 0)
6823     page_num = g_list_length (priv->children) - 1;
6824
6825   list = g_list_nth (priv->children, page_num);
6826   if (list)
6827     gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6828 }
6829
6830 /**
6831  * gtk_notebook_next_page:
6832  * @notebook: a #GtkNotebook
6833  * 
6834  * Switches to the next page. Nothing happens if the current page is
6835  * the last page.
6836  **/
6837 void
6838 gtk_notebook_next_page (GtkNotebook *notebook)
6839 {
6840   GtkNotebookPrivate *priv;
6841   GList *list;
6842
6843   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6844
6845   priv = notebook->priv;
6846
6847   list = g_list_find (priv->children, priv->cur_page);
6848   if (!list)
6849     return;
6850
6851   list = gtk_notebook_search_page (notebook, list, STEP_NEXT, TRUE);
6852   if (!list)
6853     return;
6854
6855   gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6856 }
6857
6858 /**
6859  * gtk_notebook_prev_page:
6860  * @notebook: a #GtkNotebook
6861  * 
6862  * Switches to the previous page. Nothing happens if the current page
6863  * is the first page.
6864  **/
6865 void
6866 gtk_notebook_prev_page (GtkNotebook *notebook)
6867 {
6868   GtkNotebookPrivate *priv;
6869   GList *list;
6870
6871   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6872
6873   priv = notebook->priv;
6874
6875   list = g_list_find (priv->children, priv->cur_page);
6876   if (!list)
6877     return;
6878
6879   list = gtk_notebook_search_page (notebook, list, STEP_PREV, TRUE);
6880   if (!list)
6881     return;
6882
6883   gtk_notebook_switch_page (notebook, GTK_NOTEBOOK_PAGE (list));
6884 }
6885
6886 /* Public GtkNotebook/Tab Style Functions
6887  *
6888  * gtk_notebook_set_show_border
6889  * gtk_notebook_get_show_border
6890  * gtk_notebook_set_show_tabs
6891  * gtk_notebook_get_show_tabs
6892  * gtk_notebook_set_tab_pos
6893  * gtk_notebook_get_tab_pos
6894  * gtk_notebook_set_scrollable
6895  * gtk_notebook_get_scrollable
6896  * gtk_notebook_get_tab_hborder
6897  * gtk_notebook_get_tab_vborder
6898  */
6899 /**
6900  * gtk_notebook_set_show_border:
6901  * @notebook: a #GtkNotebook
6902  * @show_border: %TRUE if a bevel should be drawn around the notebook.
6903  * 
6904  * Sets whether a bevel will be drawn around the notebook pages.
6905  * This only has a visual effect when the tabs are not shown.
6906  * See gtk_notebook_set_show_tabs().
6907  **/
6908 void
6909 gtk_notebook_set_show_border (GtkNotebook *notebook,
6910                               gboolean     show_border)
6911 {
6912   GtkNotebookPrivate *priv;
6913
6914   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6915
6916   priv = notebook->priv;
6917
6918   if (priv->show_border != show_border)
6919     {
6920       priv->show_border = show_border;
6921
6922       if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
6923         gtk_widget_queue_resize (GTK_WIDGET (notebook));
6924       
6925       g_object_notify (G_OBJECT (notebook), "show-border");
6926     }
6927 }
6928
6929 /**
6930  * gtk_notebook_get_show_border:
6931  * @notebook: a #GtkNotebook
6932  *
6933  * Returns whether a bevel will be drawn around the notebook pages. See
6934  * gtk_notebook_set_show_border().
6935  *
6936  * Return value: %TRUE if the bevel is drawn
6937  **/
6938 gboolean
6939 gtk_notebook_get_show_border (GtkNotebook *notebook)
6940 {
6941   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
6942
6943   return notebook->priv->show_border;
6944 }
6945
6946 /**
6947  * gtk_notebook_set_show_tabs:
6948  * @notebook: a #GtkNotebook
6949  * @show_tabs: %TRUE if the tabs should be shown.
6950  * 
6951  * Sets whether to show the tabs for the notebook or not.
6952  **/
6953 void
6954 gtk_notebook_set_show_tabs (GtkNotebook *notebook,
6955                             gboolean     show_tabs)
6956 {
6957   GtkNotebookPrivate *priv;
6958   GtkNotebookPage *page;
6959   GList *children;
6960   gint i;
6961
6962   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
6963
6964   priv = notebook->priv;
6965
6966   show_tabs = show_tabs != FALSE;
6967
6968   if (priv->show_tabs == show_tabs)
6969     return;
6970
6971   priv->show_tabs = show_tabs;
6972   children = priv->children;
6973
6974   if (!show_tabs)
6975     {
6976       gtk_widget_set_can_focus (GTK_WIDGET (notebook), FALSE);
6977
6978       while (children)
6979         {
6980           page = children->data;
6981           children = children->next;
6982           if (page->default_tab)
6983             {
6984               gtk_widget_destroy (page->tab_label);
6985               page->tab_label = NULL;
6986             }
6987           else
6988             gtk_widget_hide (page->tab_label);
6989         }
6990     }
6991   else
6992     {
6993       gtk_widget_set_can_focus (GTK_WIDGET (notebook), TRUE);
6994       gtk_notebook_update_labels (notebook);
6995     }
6996
6997   for (i = 0; i < N_ACTION_WIDGETS; i++)
6998     {
6999       if (priv->action_widget[i])
7000         gtk_widget_set_child_visible (priv->action_widget[i], show_tabs);
7001     }
7002
7003   gtk_widget_queue_resize (GTK_WIDGET (notebook));
7004
7005   g_object_notify (G_OBJECT (notebook), "show-tabs");
7006 }
7007
7008 /**
7009  * gtk_notebook_get_show_tabs:
7010  * @notebook: a #GtkNotebook
7011  *
7012  * Returns whether the tabs of the notebook are shown. See
7013  * gtk_notebook_set_show_tabs().
7014  *
7015  * Return value: %TRUE if the tabs are shown
7016  **/
7017 gboolean
7018 gtk_notebook_get_show_tabs (GtkNotebook *notebook)
7019 {
7020   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7021
7022   return notebook->priv->show_tabs;
7023 }
7024
7025 /**
7026  * gtk_notebook_set_tab_pos:
7027  * @notebook: a #GtkNotebook.
7028  * @pos: the edge to draw the tabs at.
7029  * 
7030  * Sets the edge at which the tabs for switching pages in the
7031  * notebook are drawn.
7032  **/
7033 void
7034 gtk_notebook_set_tab_pos (GtkNotebook     *notebook,
7035                           GtkPositionType  pos)
7036 {
7037   GtkNotebookPrivate *priv;
7038
7039   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7040
7041   priv = notebook->priv;
7042
7043   if (priv->tab_pos != pos)
7044     {
7045       priv->tab_pos = pos;
7046       if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7047         gtk_widget_queue_resize (GTK_WIDGET (notebook));
7048     }
7049
7050   g_object_notify (G_OBJECT (notebook), "tab-pos");
7051 }
7052
7053 /**
7054  * gtk_notebook_get_tab_pos:
7055  * @notebook: a #GtkNotebook
7056  *
7057  * Gets the edge at which the tabs for switching pages in the
7058  * notebook are drawn.
7059  *
7060  * Return value: the edge at which the tabs are drawn
7061  **/
7062 GtkPositionType
7063 gtk_notebook_get_tab_pos (GtkNotebook *notebook)
7064 {
7065   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), GTK_POS_TOP);
7066
7067   return notebook->priv->tab_pos;
7068 }
7069
7070 /**
7071  * gtk_notebook_set_scrollable:
7072  * @notebook: a #GtkNotebook
7073  * @scrollable: %TRUE if scroll arrows should be added
7074  * 
7075  * Sets whether the tab label area will have arrows for scrolling if
7076  * there are too many tabs to fit in the area.
7077  **/
7078 void
7079 gtk_notebook_set_scrollable (GtkNotebook *notebook,
7080                              gboolean     scrollable)
7081 {
7082   GtkNotebookPrivate *priv;
7083
7084   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7085
7086   priv = notebook->priv;
7087
7088   scrollable = (scrollable != FALSE);
7089
7090   if (scrollable != priv->scrollable)
7091     {
7092       priv->scrollable = scrollable;
7093
7094       if (gtk_widget_get_visible (GTK_WIDGET (notebook)))
7095         gtk_widget_queue_resize (GTK_WIDGET (notebook));
7096
7097       g_object_notify (G_OBJECT (notebook), "scrollable");
7098     }
7099 }
7100
7101 /**
7102  * gtk_notebook_get_scrollable:
7103  * @notebook: a #GtkNotebook
7104  *
7105  * Returns whether the tab label area has arrows for scrolling. See
7106  * gtk_notebook_set_scrollable().
7107  *
7108  * Return value: %TRUE if arrows for scrolling are present
7109  **/
7110 gboolean
7111 gtk_notebook_get_scrollable (GtkNotebook *notebook)
7112 {
7113   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7114
7115   return notebook->priv->scrollable;
7116 }
7117
7118 /**
7119  * gtk_notebook_get_tab_hborder:
7120  * @notebook: a #GtkNotebook
7121  *
7122  * Returns the horizontal width of a tab border.
7123  *
7124  * Return value: horizontal width of a tab border
7125  *
7126  * Since: 2.22
7127  */
7128 guint16
7129 gtk_notebook_get_tab_hborder (GtkNotebook *notebook)
7130 {
7131   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7132
7133   return notebook->priv->tab_hborder;
7134 }
7135
7136 /**
7137  * gtk_notebook_get_tab_vborder:
7138  * @notebook: a #GtkNotebook
7139  *
7140  * Returns the vertical width of a tab border.
7141  *
7142  * Return value: vertical width of a tab border
7143  *
7144  * Since: 2.22
7145  */
7146 guint16
7147 gtk_notebook_get_tab_vborder (GtkNotebook *notebook)
7148 {
7149   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7150
7151   return notebook->priv->tab_vborder;
7152 }
7153
7154
7155 /* Public GtkNotebook Popup Menu Methods:
7156  *
7157  * gtk_notebook_popup_enable
7158  * gtk_notebook_popup_disable
7159  */
7160
7161
7162 /**
7163  * gtk_notebook_popup_enable:
7164  * @notebook: a #GtkNotebook
7165  * 
7166  * Enables the popup menu: if the user clicks with the right mouse button on
7167  * the tab labels, a menu with all the pages will be popped up.
7168  **/
7169 void
7170 gtk_notebook_popup_enable (GtkNotebook *notebook)
7171 {
7172   GtkNotebookPrivate *priv;
7173   GList *list;
7174
7175   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7176
7177   priv = notebook->priv;
7178
7179   if (priv->menu)
7180     return;
7181
7182   priv->menu = gtk_menu_new ();
7183   for (list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, FALSE);
7184        list;
7185        list = gtk_notebook_search_page (notebook, list, STEP_NEXT, FALSE))
7186     gtk_notebook_menu_item_create (notebook, list);
7187
7188   gtk_notebook_update_labels (notebook);
7189   gtk_menu_attach_to_widget (GTK_MENU (priv->menu),
7190                              GTK_WIDGET (notebook),
7191                              gtk_notebook_menu_detacher);
7192
7193   g_object_notify (G_OBJECT (notebook), "enable-popup");
7194 }
7195
7196 /**
7197  * gtk_notebook_popup_disable:
7198  * @notebook: a #GtkNotebook
7199  * 
7200  * Disables the popup menu.
7201  **/
7202 void       
7203 gtk_notebook_popup_disable  (GtkNotebook *notebook)
7204 {
7205   GtkNotebookPrivate *priv;
7206
7207   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7208
7209   priv = notebook->priv;
7210
7211   if (!priv->menu)
7212     return;
7213
7214   gtk_container_foreach (GTK_CONTAINER (priv->menu),
7215                          (GtkCallback) gtk_notebook_menu_label_unparent, NULL);
7216   gtk_widget_destroy (priv->menu);
7217
7218   g_object_notify (G_OBJECT (notebook), "enable-popup");
7219 }
7220
7221 /* Public GtkNotebook Page Properties Functions:
7222  *
7223  * gtk_notebook_get_tab_label
7224  * gtk_notebook_set_tab_label
7225  * gtk_notebook_set_tab_label_text
7226  * gtk_notebook_get_menu_label
7227  * gtk_notebook_set_menu_label
7228  * gtk_notebook_set_menu_label_text
7229  * gtk_notebook_get_tab_reorderable
7230  * gtk_notebook_set_tab_reorderable
7231  * gtk_notebook_get_tab_detachable
7232  * gtk_notebook_set_tab_detachable
7233  */
7234
7235 /**
7236  * gtk_notebook_get_tab_label:
7237  * @notebook: a #GtkNotebook
7238  * @child: the page
7239  * 
7240  * Returns the tab label widget for the page @child. %NULL is returned
7241  * if @child is not in @notebook or if no tab label has specifically
7242  * been set for @child.
7243  *
7244  * Return value: (transfer none): the tab label
7245  **/
7246 GtkWidget *
7247 gtk_notebook_get_tab_label (GtkNotebook *notebook,
7248                             GtkWidget   *child)
7249 {
7250   GList *list;
7251
7252   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7253   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7254
7255   list = CHECK_FIND_CHILD (notebook, child);
7256   if (!list)  
7257     return NULL;
7258
7259   if (GTK_NOTEBOOK_PAGE (list)->default_tab)
7260     return NULL;
7261
7262   return GTK_NOTEBOOK_PAGE (list)->tab_label;
7263 }  
7264
7265 /**
7266  * gtk_notebook_set_tab_label:
7267  * @notebook: a #GtkNotebook
7268  * @child: the page
7269  * @tab_label: (allow-none): the tab label widget to use, or %NULL for default tab
7270  *             label.
7271  *
7272  * Changes the tab label for @child. If %NULL is specified
7273  * for @tab_label, then the page will have the label 'page N'.
7274  **/
7275 void
7276 gtk_notebook_set_tab_label (GtkNotebook *notebook,
7277                             GtkWidget   *child,
7278                             GtkWidget   *tab_label)
7279 {
7280   GtkNotebookPrivate *priv;
7281   GtkNotebookPage *page;
7282   GList *list;
7283
7284   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7285   g_return_if_fail (GTK_IS_WIDGET (child));
7286
7287   priv = notebook->priv;
7288
7289   list = CHECK_FIND_CHILD (notebook, child);
7290   if (!list)  
7291     return;
7292
7293   /* a NULL pointer indicates a default_tab setting, otherwise
7294    * we need to set the associated label
7295    */
7296   page = list->data;
7297   
7298   if (page->tab_label == tab_label)
7299     return;
7300   
7301
7302   gtk_notebook_remove_tab_label (notebook, page);
7303   
7304   if (tab_label)
7305     {
7306       page->default_tab = FALSE;
7307       page->tab_label = tab_label;
7308       gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7309     }
7310   else
7311     {
7312       page->default_tab = TRUE;
7313       page->tab_label = NULL;
7314
7315       if (priv->show_tabs)
7316         {
7317           gchar string[32];
7318
7319           g_snprintf (string, sizeof(string), _("Page %u"), 
7320                       gtk_notebook_real_page_position (notebook, list));
7321           page->tab_label = gtk_label_new (string);
7322           gtk_widget_set_parent (page->tab_label, GTK_WIDGET (notebook));
7323         }
7324     }
7325
7326   if (page->tab_label)
7327     page->mnemonic_activate_signal =
7328       g_signal_connect (page->tab_label,
7329                         "mnemonic-activate",
7330                         G_CALLBACK (gtk_notebook_mnemonic_activate_switch_page),
7331                         notebook);
7332
7333   if (priv->show_tabs && gtk_widget_get_visible (child))
7334     {
7335       gtk_widget_show (page->tab_label);
7336       gtk_widget_queue_resize (GTK_WIDGET (notebook));
7337     }
7338
7339   gtk_notebook_update_tab_states (notebook);
7340   gtk_widget_child_notify (child, "tab-label");
7341 }
7342
7343 /**
7344  * gtk_notebook_set_tab_label_text:
7345  * @notebook: a #GtkNotebook
7346  * @child: the page
7347  * @tab_text: the label text
7348  * 
7349  * Creates a new label and sets it as the tab label for the page
7350  * containing @child.
7351  **/
7352 void
7353 gtk_notebook_set_tab_label_text (GtkNotebook *notebook,
7354                                  GtkWidget   *child,
7355                                  const gchar *tab_text)
7356 {
7357   GtkWidget *tab_label = NULL;
7358
7359   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7360
7361   if (tab_text)
7362     tab_label = gtk_label_new (tab_text);
7363   gtk_notebook_set_tab_label (notebook, child, tab_label);
7364   gtk_widget_child_notify (child, "tab-label");
7365 }
7366
7367 /**
7368  * gtk_notebook_get_tab_label_text:
7369  * @notebook: a #GtkNotebook
7370  * @child: a widget contained in a page of @notebook
7371  *
7372  * Retrieves the text of the tab label for the page containing
7373  *    @child.
7374  *
7375  * Return value: the text of the tab label, or %NULL if the
7376  *               tab label widget is not a #GtkLabel. The
7377  *               string is owned by the widget and must not
7378  *               be freed.
7379  **/
7380 G_CONST_RETURN gchar *
7381 gtk_notebook_get_tab_label_text (GtkNotebook *notebook,
7382                                  GtkWidget   *child)
7383 {
7384   GtkWidget *tab_label;
7385
7386   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7387   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7388
7389   tab_label = gtk_notebook_get_tab_label (notebook, child);
7390
7391   if (GTK_IS_LABEL (tab_label))
7392     return gtk_label_get_text (GTK_LABEL (tab_label));
7393   else
7394     return NULL;
7395 }
7396
7397 /**
7398  * gtk_notebook_get_menu_label:
7399  * @notebook: a #GtkNotebook
7400  * @child: a widget contained in a page of @notebook
7401  *
7402  * Retrieves the menu label widget of the page containing @child.
7403  *
7404  * Return value: (transfer none): the menu label, or %NULL if the
7405  *     notebook page does not have a menu label other than the
7406  *     default (the tab label).
7407  **/
7408 GtkWidget*
7409 gtk_notebook_get_menu_label (GtkNotebook *notebook,
7410                              GtkWidget   *child)
7411 {
7412   GList *list;
7413
7414   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7415   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7416
7417   list = CHECK_FIND_CHILD (notebook, child);
7418   if (!list)
7419     return NULL;
7420
7421   if (GTK_NOTEBOOK_PAGE (list)->default_menu)
7422     return NULL;
7423
7424   return GTK_NOTEBOOK_PAGE (list)->menu_label;
7425 }
7426
7427 /**
7428  * gtk_notebook_set_menu_label:
7429  * @notebook: a #GtkNotebook
7430  * @child: the child widget
7431  * @menu_label: (allow-none): the menu label, or NULL for default
7432  *
7433  * Changes the menu label for the page containing @child.
7434  **/
7435 void
7436 gtk_notebook_set_menu_label (GtkNotebook *notebook,
7437                              GtkWidget   *child,
7438                              GtkWidget   *menu_label)
7439 {
7440   GtkNotebookPrivate *priv;
7441   GtkNotebookPage *page;
7442   GList *list;
7443
7444   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7445   g_return_if_fail (GTK_IS_WIDGET (child));
7446
7447   priv = notebook->priv;
7448
7449   list = CHECK_FIND_CHILD (notebook, child);
7450   if (!list)  
7451     return;
7452
7453   page = list->data;
7454   if (page->menu_label)
7455     {
7456       if (priv->menu)
7457         gtk_container_remove (GTK_CONTAINER (priv->menu),
7458                               gtk_widget_get_parent (page->menu_label));
7459
7460       if (!page->default_menu)
7461         g_object_unref (page->menu_label);
7462     }
7463
7464   if (menu_label)
7465     {
7466       page->menu_label = menu_label;
7467       g_object_ref_sink (page->menu_label);
7468       page->default_menu = FALSE;
7469     }
7470   else
7471     page->default_menu = TRUE;
7472
7473   if (priv->menu)
7474     gtk_notebook_menu_item_create (notebook, list);
7475   gtk_widget_child_notify (child, "menu-label");
7476 }
7477
7478 /**
7479  * gtk_notebook_set_menu_label_text:
7480  * @notebook: a #GtkNotebook
7481  * @child: the child widget
7482  * @menu_text: the label text
7483  * 
7484  * Creates a new label and sets it as the menu label of @child.
7485  **/
7486 void
7487 gtk_notebook_set_menu_label_text (GtkNotebook *notebook,
7488                                   GtkWidget   *child,
7489                                   const gchar *menu_text)
7490 {
7491   GtkWidget *menu_label = NULL;
7492
7493   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7494
7495   if (menu_text)
7496     {
7497       menu_label = gtk_label_new (menu_text);
7498       gtk_misc_set_alignment (GTK_MISC (menu_label), 0.0, 0.5);
7499     }
7500   gtk_notebook_set_menu_label (notebook, child, menu_label);
7501   gtk_widget_child_notify (child, "menu-label");
7502 }
7503
7504 /**
7505  * gtk_notebook_get_menu_label_text:
7506  * @notebook: a #GtkNotebook
7507  * @child: the child widget of a page of the notebook.
7508  *
7509  * Retrieves the text of the menu label for the page containing
7510  *    @child.
7511  *
7512  * Return value: the text of the tab label, or %NULL if the
7513  *               widget does not have a menu label other than
7514  *               the default menu label, or the menu label widget
7515  *               is not a #GtkLabel. The string is owned by
7516  *               the widget and must not be freed.
7517  **/
7518 G_CONST_RETURN gchar *
7519 gtk_notebook_get_menu_label_text (GtkNotebook *notebook,
7520                                   GtkWidget *child)
7521 {
7522   GtkWidget *menu_label;
7523
7524   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7525   g_return_val_if_fail (GTK_IS_WIDGET (child), NULL);
7526  
7527   menu_label = gtk_notebook_get_menu_label (notebook, child);
7528
7529   if (GTK_IS_LABEL (menu_label))
7530     return gtk_label_get_text (GTK_LABEL (menu_label));
7531   else
7532     return NULL;
7533 }
7534   
7535 /* Helper function called when pages are reordered
7536  */
7537 static void
7538 gtk_notebook_child_reordered (GtkNotebook     *notebook,
7539                               GtkNotebookPage *page)
7540 {
7541   GtkNotebookPrivate *priv = notebook->priv;
7542
7543   if (priv->menu)
7544     {
7545       GtkWidget *menu_item;
7546
7547       menu_item = gtk_widget_get_parent (page->menu_label);
7548       gtk_container_remove (GTK_CONTAINER (menu_item), page->menu_label);
7549       gtk_container_remove (GTK_CONTAINER (priv->menu), menu_item);
7550       gtk_notebook_menu_item_create (notebook, g_list_find (priv->children, page));
7551     }
7552
7553   gtk_notebook_update_tab_states (notebook);
7554   gtk_notebook_update_labels (notebook);
7555 }
7556
7557 static void
7558 gtk_notebook_set_tab_label_packing (GtkNotebook *notebook,
7559                                     GtkWidget   *child,
7560                                     gboolean     expand,
7561                                     gboolean     fill,
7562                                     GtkPackType  pack_type)
7563 {
7564   GtkNotebookPrivate *priv;
7565   GtkNotebookPage *page;
7566   GList *list;
7567
7568   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7569   g_return_if_fail (GTK_IS_WIDGET (child));
7570
7571   priv = notebook->priv;
7572
7573   list = CHECK_FIND_CHILD (notebook, child);
7574   if (!list)  
7575     return;
7576
7577   page = list->data;
7578   expand = expand != FALSE;
7579   fill = fill != FALSE;
7580   if (page->pack == pack_type && page->expand == expand && page->fill == fill)
7581     return;
7582
7583   gtk_widget_freeze_child_notify (child);
7584   page->expand = expand;
7585   gtk_widget_child_notify (child, "tab-expand");
7586   page->fill = fill;
7587   gtk_widget_child_notify (child, "tab-fill");
7588   if (page->pack != pack_type)
7589     {
7590       page->pack = pack_type;
7591       gtk_notebook_child_reordered (notebook, page);
7592     }
7593   gtk_widget_child_notify (child, "tab-pack");
7594   gtk_widget_child_notify (child, "position");
7595   if (priv->show_tabs)
7596     gtk_notebook_pages_allocate (notebook);
7597   gtk_widget_thaw_child_notify (child);
7598 }  
7599
7600 static void
7601 gtk_notebook_query_tab_label_packing (GtkNotebook *notebook,
7602                                       GtkWidget   *child,
7603                                       gboolean    *expand,
7604                                       gboolean    *fill,
7605                                       GtkPackType *pack_type)
7606 {
7607   GList *list;
7608
7609   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7610   g_return_if_fail (GTK_IS_WIDGET (child));
7611
7612   list = CHECK_FIND_CHILD (notebook, child);
7613   if (!list)
7614     return;
7615
7616   if (expand)
7617     *expand = GTK_NOTEBOOK_PAGE (list)->expand;
7618   if (fill)
7619     *fill = GTK_NOTEBOOK_PAGE (list)->fill;
7620   if (pack_type)
7621     *pack_type = GTK_NOTEBOOK_PAGE (list)->pack;
7622 }
7623
7624 /**
7625  * gtk_notebook_reorder_child:
7626  * @notebook: a #GtkNotebook
7627  * @child: the child to move
7628  * @position: the new position, or -1 to move to the end
7629  * 
7630  * Reorders the page containing @child, so that it appears in position
7631  * @position. If @position is greater than or equal to the number of
7632  * children in the list or negative, @child will be moved to the end
7633  * of the list.
7634  **/
7635 void
7636 gtk_notebook_reorder_child (GtkNotebook *notebook,
7637                             GtkWidget   *child,
7638                             gint         position)
7639 {
7640   GtkNotebookPrivate *priv;
7641   GList *list, *new_list;
7642   GtkNotebookPage *page;
7643   gint old_pos;
7644   gint max_pos;
7645
7646   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7647   g_return_if_fail (GTK_IS_WIDGET (child));
7648
7649   priv = notebook->priv;
7650
7651   list = CHECK_FIND_CHILD (notebook, child);
7652   if (!list)
7653     return;
7654
7655   max_pos = g_list_length (priv->children) - 1;
7656   if (position < 0 || position > max_pos)
7657     position = max_pos;
7658
7659   old_pos = g_list_position (priv->children, list);
7660
7661   if (old_pos == position)
7662     return;
7663
7664   page = list->data;
7665   priv->children = g_list_delete_link (priv->children, list);
7666
7667   priv->children = g_list_insert (priv->children, page, position);
7668   new_list = g_list_nth (priv->children, position);
7669
7670   /* Fix up GList references in GtkNotebook structure */
7671   if (priv->first_tab == list)
7672     priv->first_tab = new_list;
7673   if (priv->focus_tab == list)
7674     priv->focus_tab = new_list;
7675
7676   gtk_widget_freeze_child_notify (child);
7677
7678   /* Move around the menu items if necessary */
7679   gtk_notebook_child_reordered (notebook, page);
7680   gtk_widget_child_notify (child, "tab-pack");
7681   gtk_widget_child_notify (child, "position");
7682
7683   if (priv->show_tabs)
7684     gtk_notebook_pages_allocate (notebook);
7685
7686   gtk_widget_thaw_child_notify (child);
7687
7688   g_signal_emit (notebook,
7689                  notebook_signals[PAGE_REORDERED],
7690                  0,
7691                  child,
7692                  position);
7693 }
7694
7695 /**
7696  * gtk_notebook_set_group_name:
7697  * @notebook: a #GtkNotebook
7698  * @group_name: (allow-none): the name of the notebook group,
7699  *     or %NULL to unset it
7700  *
7701  * Sets a group name for @notebook.
7702  *
7703  * Notebooks with the same name will be able to exchange tabs
7704  * via drag and drop. A notebook with a %NULL group name will
7705  * not be able to exchange tabs with any other notebook.
7706  *
7707  * Since: 2.24
7708  */
7709 void
7710 gtk_notebook_set_group_name (GtkNotebook *notebook,
7711                              const gchar *group_name)
7712 {
7713   GtkNotebookPrivate *priv;
7714   GQuark group;
7715
7716   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7717
7718   priv = notebook->priv;
7719
7720   group = g_quark_from_string (group_name);
7721
7722   if (priv->group != group)
7723     {
7724       priv->group = group;
7725       g_object_notify (G_OBJECT (notebook), "group-name");
7726     }
7727 }
7728
7729 /**
7730  * gtk_notebook_get_group_name:
7731  * @notebook: a #GtkNotebook
7732  *
7733  * Gets the current group name for @notebook.
7734  *
7735  * Return Value: (transfer none): the group name,
7736  *     or %NULL if none is set.
7737  *
7738  * Since: 2.24
7739  **/
7740 const gchar *
7741 gtk_notebook_get_group_name (GtkNotebook *notebook)
7742 {
7743   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7744
7745   return g_quark_to_string (notebook->priv->group);
7746 }
7747
7748 /**
7749  * gtk_notebook_get_tab_reorderable:
7750  * @notebook: a #GtkNotebook
7751  * @child: a child #GtkWidget
7752  * 
7753  * Gets whether the tab can be reordered via drag and drop or not.
7754  * 
7755  * Return Value: %TRUE if the tab is reorderable.
7756  * 
7757  * Since: 2.10
7758  **/
7759 gboolean
7760 gtk_notebook_get_tab_reorderable (GtkNotebook *notebook,
7761                                   GtkWidget   *child)
7762 {
7763   GList *list;
7764
7765   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7766   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7767
7768   list = CHECK_FIND_CHILD (notebook, child);
7769   if (!list)  
7770     return FALSE;
7771
7772   return GTK_NOTEBOOK_PAGE (list)->reorderable;
7773 }
7774
7775 /**
7776  * gtk_notebook_set_tab_reorderable:
7777  * @notebook: a #GtkNotebook
7778  * @child: a child #GtkWidget
7779  * @reorderable: whether the tab is reorderable or not.
7780  *
7781  * Sets whether the notebook tab can be reordered
7782  * via drag and drop or not.
7783  * 
7784  * Since: 2.10
7785  **/
7786 void
7787 gtk_notebook_set_tab_reorderable (GtkNotebook *notebook,
7788                                   GtkWidget   *child,
7789                                   gboolean     reorderable)
7790 {
7791   GList *list;
7792
7793   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7794   g_return_if_fail (GTK_IS_WIDGET (child));
7795
7796   list = CHECK_FIND_CHILD (notebook, child);
7797   if (!list)  
7798     return;
7799
7800   if (GTK_NOTEBOOK_PAGE (list)->reorderable != reorderable)
7801     {
7802       GTK_NOTEBOOK_PAGE (list)->reorderable = (reorderable == TRUE);
7803       gtk_widget_child_notify (child, "reorderable");
7804     }
7805 }
7806
7807 /**
7808  * gtk_notebook_get_tab_detachable:
7809  * @notebook: a #GtkNotebook
7810  * @child: a child #GtkWidget
7811  * 
7812  * Returns whether the tab contents can be detached from @notebook.
7813  * 
7814  * Return Value: TRUE if the tab is detachable.
7815  *
7816  * Since: 2.10
7817  **/
7818 gboolean
7819 gtk_notebook_get_tab_detachable (GtkNotebook *notebook,
7820                                  GtkWidget   *child)
7821 {
7822   GList *list;
7823
7824   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), FALSE);
7825   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
7826
7827   list = CHECK_FIND_CHILD (notebook, child);
7828   if (!list)  
7829     return FALSE;
7830
7831   return GTK_NOTEBOOK_PAGE (list)->detachable;
7832 }
7833
7834 /**
7835  * gtk_notebook_set_tab_detachable:
7836  * @notebook: a #GtkNotebook
7837  * @child: a child #GtkWidget
7838  * @detachable: whether the tab is detachable or not
7839  *
7840  * Sets whether the tab can be detached from @notebook to another
7841  * notebook or widget.
7842  *
7843  * Note that 2 notebooks must share a common group identificator
7844  * (see gtk_notebook_set_group()) to allow automatic tabs
7845  * interchange between them.
7846  *
7847  * If you want a widget to interact with a notebook through DnD
7848  * (i.e.: accept dragged tabs from it) it must be set as a drop
7849  * destination and accept the target "GTK_NOTEBOOK_TAB". The notebook
7850  * will fill the selection with a GtkWidget** pointing to the child
7851  * widget that corresponds to the dropped tab.
7852  * |[
7853  *  static void
7854  *  on_drop_zone_drag_data_received (GtkWidget        *widget,
7855  *                                   GdkDragContext   *context,
7856  *                                   gint              x,
7857  *                                   gint              y,
7858  *                                   GtkSelectionData *selection_data,
7859  *                                   guint             info,
7860  *                                   guint             time,
7861  *                                   gpointer          user_data)
7862  *  {
7863  *    GtkWidget *notebook;
7864  *    GtkWidget **child;
7865  *    
7866  *    notebook = gtk_drag_get_source_widget (context);
7867  *    child = (void*) selection_data->data;
7868  *    
7869  *    process_widget (*child);
7870  *    gtk_container_remove (GTK_CONTAINER (notebook), *child);
7871  *  }
7872  * ]|
7873  *
7874  * If you want a notebook to accept drags from other widgets,
7875  * you will have to set your own DnD code to do it.
7876  *
7877  * Since: 2.10
7878  **/
7879 void
7880 gtk_notebook_set_tab_detachable (GtkNotebook *notebook,
7881                                  GtkWidget  *child,
7882                                  gboolean    detachable)
7883 {
7884   GList *list;
7885
7886   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7887   g_return_if_fail (GTK_IS_WIDGET (child));
7888
7889   list = CHECK_FIND_CHILD (notebook, child);
7890   if (!list)  
7891     return;
7892
7893   if (GTK_NOTEBOOK_PAGE (list)->detachable != detachable)
7894     {
7895       GTK_NOTEBOOK_PAGE (list)->detachable = (detachable == TRUE);
7896       gtk_widget_child_notify (child, "detachable");
7897     }
7898 }
7899
7900 /**
7901  * gtk_notebook_get_action_widget:
7902  * @notebook: a #GtkNotebook
7903  * @pack_type: pack type of the action widget to receive
7904  *
7905  * Gets one of the action widgets. See gtk_notebook_set_action_widget().
7906  *
7907  * Returns: (transfer none): The action widget with the given @pack_type
7908  *     or %NULL when this action widget has not been set
7909  *
7910  * Since: 2.20
7911  */
7912 GtkWidget*
7913 gtk_notebook_get_action_widget (GtkNotebook *notebook,
7914                                 GtkPackType  pack_type)
7915 {
7916   g_return_val_if_fail (GTK_IS_NOTEBOOK (notebook), NULL);
7917
7918   return notebook->priv->action_widget[pack_type];
7919 }
7920
7921 /**
7922  * gtk_notebook_set_action_widget:
7923  * @notebook: a #GtkNotebook
7924  * @widget: a #GtkWidget
7925  * @pack_type: pack type of the action widget
7926  *
7927  * Sets @widget as one of the action widgets. Depending on the pack type
7928  * the widget will be placed before or after the tabs. You can use
7929  * a #GtkBox if you need to pack more than one widget on the same side.
7930  *
7931  * Note that action widgets are "internal" children of the notebook and thus
7932  * not included in the list returned from gtk_container_foreach().
7933  *
7934  * Since: 2.20
7935  */
7936 void
7937 gtk_notebook_set_action_widget (GtkNotebook *notebook,
7938                                 GtkWidget   *widget,
7939                                 GtkPackType  pack_type)
7940 {
7941   GtkNotebookPrivate *priv;
7942
7943   g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
7944   g_return_if_fail (!widget || GTK_IS_WIDGET (widget));
7945   g_return_if_fail (!widget || gtk_widget_get_parent (widget) == NULL);
7946
7947   priv = notebook->priv;
7948
7949   if (priv->action_widget[pack_type])
7950     gtk_widget_unparent (priv->action_widget[pack_type]);
7951
7952   priv->action_widget[pack_type] = widget;
7953
7954   if (widget)
7955     {
7956       gtk_widget_set_child_visible (widget, priv->show_tabs);
7957       gtk_widget_set_parent (widget, GTK_WIDGET (notebook));
7958     }
7959
7960   gtk_widget_queue_resize (GTK_WIDGET (notebook));
7961 }