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