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