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