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