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