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