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