gtknotebook.c 261 KB
Newer Older
1
/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
Cody Russell's avatar
Cody Russell committed
2
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
3 4 5
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18 19

/*
20
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21 22
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
23
 * GTK+ at ftp:ftp.gtk.org/pub/gtk/.
24 25
 */

26
#include "config.h"
27 28

#include <stdio.h>
Johan Dahlin's avatar
Johan Dahlin committed
29
#include <string.h>
30
#include <math.h>
31

32
#include "gtknotebook.h"
33

34 35 36 37
#include "gtkmain.h"
#include "gtkmenu.h"
#include "gtkmenuitem.h"
#include "gtklabel.h"
38
#include "gtkintl.h"
39
#include "gtkmarshalers.h"
40
#include "gtkbindings.h"
41
#include "gtkprivate.h"
42
#include "gtkdnd.h"
Johan Dahlin's avatar
Johan Dahlin committed
43
#include "gtkbuildable.h"
44
#include "gtktypebuiltins.h"
45
#include "gtkwidgetpath.h"
Benjamin Otte's avatar
Benjamin Otte committed
46
#include "gtkboxgadgetprivate.h"
47
#include "gtkbuiltiniconprivate.h"
48
#include "gtkcsscustomgadgetprivate.h"
49
#include "gtkcssstylepropertyprivate.h"
50
#include "gtksizerequest.h"
51
#include "gtkstylecontextprivate.h"
52
#include "gtkwidgetprivate.h"
53
#include "a11y/gtknotebookaccessible.h"
54

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

/**
 * SECTION:gtknotebook
 * @Short_description: A tabbed notebook container
 * @Title: GtkNotebook
 * @See_also: #GtkContainer
 *
 * The #GtkNotebook widget is a #GtkContainer whose children are pages that
 * can be switched between using tab labels along one edge.
 *
 * There are many configuration options for GtkNotebook. Among other
 * things, you can choose on which edge the tabs appear
 * (see gtk_notebook_set_tab_pos()), whether, if there are too many
 * tabs to fit the notebook should be made bigger or scrolling
 * arrows added (see gtk_notebook_set_scrollable()), and whether there
 * will be a popup menu allowing the users to switch pages.
 * (see gtk_notebook_popup_enable(), gtk_notebook_popup_disable())
 *
73
 * # GtkNotebook as GtkBuildable
Matthias Clasen's avatar
Matthias Clasen committed
74
 * 
75
 * The GtkNotebook implementation of the #GtkBuildable interface
William Jon McCann's avatar
William Jon McCann committed
76
 * supports placing children into tabs by specifying “tab” as the
77
 * “type” attribute of a <child> element. Note that the content
78
 * of the tab must be created before the tab can be filled.
79
 * A tab child can be specified without specifying a <child>
80
 * type attribute.
81
 *
82
 * To add a child widget in the notebooks action area, specify
83 84
 * "action-start" or “action-end” as the “type” attribute of the
 * <child> element.
Matthias Clasen's avatar
Matthias Clasen committed
85 86
 *
 * An example of a UI definition fragment with GtkNotebook:
87
 * |[
88 89 90 91 92 93 94 95 96 97 98 99
 * <object class="GtkNotebook">
 *   <child>
 *     <object class="GtkLabel" id="notebook-content">
 *       <property name="label">Content</property>
 *     </object>
 *   </child>
 *   <child type="tab">
 *     <object class="GtkLabel" id="notebook-tab">
 *       <property name="label">Tab</property>
 *     </object>
 *   </child>
 * </object>
100
 * ]|
101 102 103
 *
 * # CSS nodes
 *
104 105 106
 * |[<!-- language="plain" -->
 * notebook
 * ├── header.top
107
 * │   ├── [<action widget>]
108
 * │   ├── tabs
109
 * │   │   ├── [arrow]
110
 * │   │   ├── tab
111 112
 * │   │   │   ╰── <tab label>
 * ┊   ┊   ┊
113
 * │   │   ├── tab[.reorderable-page]
114
 * │   │   │   ╰── <tab label>
115
 * │   │   ╰── [arrow]
116
 * │   ╰── [<action widget>]
117
 * │
118 119 120 121
 * ╰── stack
 *     ├── <child>
 *     ┊
 *     ╰── <child>
122 123
 * ]|
 *
124
 * GtkNotebook has a main CSS node with name notebook, a subnode
125 126 127 128
 * with name header and below that a subnode with name tabs which
 * contains one subnode per tab with name tab.
 *
 * If action widgets are present, their CSS nodes are placed next
129 130
 * to the tabs node. If the notebook is scrollable, CSS nodes with
 * name arrow are placed as first and last child of the tabs node.
131
 *
Matthias Clasen's avatar
Matthias Clasen committed
132
 * The main node gets the .frame style class when the notebook
Timm Bäder's avatar
Timm Bäder committed
133
 * has a border (see gtk_notebook_set_show_border()).
134
 *
135
 * The header node gets one of the style class .top, .bottom,
136
 * .left or .right, depending on where the tabs are placed. For
137
 * reorderable pages, the tab node gets the .reorderable-page class.
138 139
 *
 * A tab node gets the .dnd style class while it is moved with drag-and-drop.
140 141
 *
 * The nodes are always arranged from left-to-right, regarldess of text direction.
142 143 144
 */


145
#define SCROLL_DELAY_FACTOR   5
146 147
#define SCROLL_THRESHOLD      12
#define DND_THRESHOLD_MULTIPLIER 4
Elliot Lee's avatar
Elliot Lee committed
148

149 150 151 152
#define TIMEOUT_INITIAL  500
#define TIMEOUT_REPEAT    50
#define TIMEOUT_EXPAND   500

153
typedef struct _GtkNotebookPage GtkNotebookPage;
154 155 156 157 158 159 160 161 162 163 164 165 166 167

typedef enum
{
  DRAG_OPERATION_NONE,
  DRAG_OPERATION_REORDER,
  DRAG_OPERATION_DETACH
} GtkNotebookDragOperation;

enum {
  ACTION_WIDGET_START,
  ACTION_WIDGET_END,
  N_ACTION_WIDGETS
};

168
struct _GtkNotebookPrivate
169 170 171 172
{
  GtkNotebookDragOperation   operation;
  GtkNotebookPage           *cur_page;
  GtkNotebookPage           *detached_tab;
Paolo Borelli's avatar
Paolo Borelli committed
173
  GtkNotebookPage           *prelight_tab;
174 175 176 177 178 179 180 181
  GtkTargetList             *source_targets;
  GtkWidget                 *action_widget[N_ACTION_WIDGETS];
  GtkWidget                 *dnd_window;
  GtkWidget                 *menu;

  GdkWindow               *drag_window;
  GdkWindow               *event_window;

182
  GtkCssGadget              *gadget;
Benjamin Otte's avatar
Benjamin Otte committed
183
  GtkCssGadget              *stack_gadget;
184
  GtkCssGadget              *header_gadget;
185
  GtkCssGadget              *tabs_gadget;
186
  GtkCssGadget              *arrow_gadget[4];
187

188 189 190 191 192 193 194 195 196 197 198 199 200 201
  GList         *children;
  GList         *first_tab;             /* The first tab visible (for scrolling notebooks) */
  GList         *focus_tab;

  gint           drag_begin_x;
  gint           drag_begin_y;
  gint           drag_offset_x;
  gint           drag_offset_y;
  gint           drag_window_x;
  gint           drag_window_y;
  gint           mouse_x;
  gint           mouse_y;
  gint           pressed_button;

202
  GQuark         group;
203 204 205

  guint          dnd_timer;
  guint          switch_tab_timer;
206
  GList         *switch_tab;
207 208 209 210 211

  guint32        timer;

  guint          child_has_focus    : 1;
  guint          click_child        : 3;
212
  guint          remove_in_detach   : 1;
213 214 215 216 217 218 219 220
  guint          focus_out          : 1; /* Flag used by ::move-focus-out implementation */
  guint          has_scrolled       : 1;
  guint          in_child           : 3;
  guint          need_timer         : 1;
  guint          show_border        : 1;
  guint          show_tabs          : 1;
  guint          scrollable         : 1;
  guint          tab_pos            : 2;
221
  guint          tabs_reversed      : 1;
222
  guint          rootwindow_drop    : 1;
223 224
};

225 226
enum {
  SWITCH_PAGE,
227 228
  FOCUS_TAB,
  SELECT_PAGE,
229
  CHANGE_CURRENT_PAGE,
230
  MOVE_FOCUS_OUT,
231
  REORDER_TAB,
232 233 234
  PAGE_REORDERED,
  PAGE_REMOVED,
  PAGE_ADDED,
235
  CREATE_WINDOW,
236 237 238
  LAST_SIGNAL
};

239 240 241 242 243
enum {
  STEP_PREV,
  STEP_NEXT
};

244 245 246 247 248
typedef enum
{
  ARROW_LEFT_BEFORE,
  ARROW_RIGHT_BEFORE,
  ARROW_LEFT_AFTER,
249 250
  ARROW_RIGHT_AFTER,
  ARROW_NONE
251 252
} GtkNotebookArrow;

253 254 255 256 257 258 259
typedef enum
{
  POINTER_BEFORE,
  POINTER_AFTER,
  POINTER_BETWEEN
} GtkNotebookPointerPosition;

260 261 262
#define ARROW_IS_LEFT(arrow)  ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_LEFT_AFTER)
#define ARROW_IS_BEFORE(arrow) ((arrow) == ARROW_LEFT_BEFORE || (arrow) == ARROW_RIGHT_BEFORE)

263
enum {
264 265 266 267 268 269 270
  PROP_0,
  PROP_TAB_POS,
  PROP_SHOW_TABS,
  PROP_SHOW_BORDER,
  PROP_SCROLLABLE,
  PROP_PAGE,
  PROP_ENABLE_POPUP,
271 272
  PROP_GROUP_NAME,
  LAST_PROP
273 274
};

275 276
static GParamSpec *properties[LAST_PROP];

277
enum {
Tim Janik's avatar
Tim Janik committed
278 279 280 281 282 283
  CHILD_PROP_0,
  CHILD_PROP_TAB_LABEL,
  CHILD_PROP_MENU_LABEL,
  CHILD_PROP_POSITION,
  CHILD_PROP_TAB_EXPAND,
  CHILD_PROP_TAB_FILL,
284 285
  CHILD_PROP_REORDERABLE,
  CHILD_PROP_DETACHABLE
286 287
};

288
#define GTK_NOTEBOOK_PAGE(_glist_)         ((GtkNotebookPage *)(_glist_)->data)
289

290
/* some useful defines for calculating coords */
291
#define NOTEBOOK_IS_TAB_LABEL_PARENT(_notebook_,_page_) (gtk_widget_get_parent ((_page_)->tab_label) == (GTK_WIDGET (_notebook_)))
292

293 294 295 296 297
struct _GtkNotebookPage
{
  GtkWidget *child;
  GtkWidget *tab_label;
  GtkWidget *menu_label;
298
  GtkWidget *last_focus_child;  /* Last descendant of the page that had focus */
299

300
  GtkCssGadget *gadget;         /* gadget used for the tab itself */
301

302 303
  guint default_menu : 1;       /* If true, we create the menu label ourself */
  guint default_tab  : 1;       /* If true, we create the tab label ourself */
304 305
  guint expand       : 1;
  guint fill         : 1;
306 307
  guint reorderable  : 1;
  guint detachable   : 1;
308 309

  GtkRequisition requisition;
310

311 312
  gulong mnemonic_activate_signal;
  gulong notify_visible_handler;
313 314
};

315
static const GtkTargetEntry src_notebook_targets [] = {
316
  { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
317
  { "application/x-rootwindow-drop", 0, 0 },
318 319
};

320 321 322
static const GtkTargetEntry dst_notebook_targets [] = {
  { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
};
323

324
/*** GtkNotebook Methods ***/
325
static gboolean gtk_notebook_select_page         (GtkNotebook      *notebook,
326
                                                  gboolean          move_focus);
327
static gboolean gtk_notebook_focus_tab           (GtkNotebook      *notebook,
328
                                                  GtkNotebookTab    type);
329
static gboolean gtk_notebook_change_current_page (GtkNotebook      *notebook,
330
                                                  gint              offset);
331
static void     gtk_notebook_move_focus_out      (GtkNotebook      *notebook,
332
                                                  GtkDirectionType  direction_type);
333
static gboolean gtk_notebook_reorder_tab         (GtkNotebook      *notebook,
334 335
                                                  GtkDirectionType  direction_type,
                                                  gboolean          move_to_last);
336
static void     gtk_notebook_remove_tab_label    (GtkNotebook      *notebook,
337
                                                  GtkNotebookPage  *page);
338 339 340
static void     gtk_notebook_set_tab_label_packing   (GtkNotebook  *notebook,
                                                      GtkWidget    *child,
                                                      gboolean      expand,
341
                                                      gboolean      fill);
342 343 344
static void     gtk_notebook_query_tab_label_packing (GtkNotebook  *notebook,
                                                      GtkWidget    *child,
                                                      gboolean     *expand,
345
                                                      gboolean     *fill);
346

347
/*** GObject Methods ***/
348 349 350 351 352 353 354 355
static void gtk_notebook_set_property        (GObject         *object,
                                              guint            prop_id,
                                              const GValue    *value,
                                              GParamSpec      *pspec);
static void gtk_notebook_get_property        (GObject         *object,
                                              guint            prop_id,
                                              GValue          *value,
                                              GParamSpec      *pspec);
356
static void gtk_notebook_finalize            (GObject         *object);
357

358
/*** GtkWidget Methods ***/
359
static void gtk_notebook_destroy             (GtkWidget        *widget);
360 361 362
static void gtk_notebook_map                 (GtkWidget        *widget);
static void gtk_notebook_unmap               (GtkWidget        *widget);
static void gtk_notebook_realize             (GtkWidget        *widget);
363
static void gtk_notebook_unrealize           (GtkWidget        *widget);
364
static void gtk_notebook_get_preferred_width (GtkWidget        *widget,
365 366
                                              gint             *minimum,
                                              gint             *natural);
367
static void gtk_notebook_get_preferred_height(GtkWidget        *widget,
368 369
                                              gint             *minimum,
                                              gint             *natural);
370 371 372 373 374 375 376 377 378 379
static void gtk_notebook_get_preferred_width_for_height
                                             (GtkWidget        *widget,
                                              gint              height,
                                              gint             *minimum,
                                              gint             *natural);
static void gtk_notebook_get_preferred_height_for_width
                                             (GtkWidget        *widget,
                                              gint              width,
                                              gint             *minimum,
                                              gint             *natural);
380
static void gtk_notebook_size_allocate       (GtkWidget        *widget,
381
                                              GtkAllocation    *allocation);
382
static gboolean gtk_notebook_draw            (GtkWidget        *widget,
Benjamin Otte's avatar
Benjamin Otte committed
383
                                              cairo_t          *cr);
384
static gboolean gtk_notebook_button_press    (GtkWidget        *widget,
385
                                              GdkEventButton   *event);
386
static gboolean gtk_notebook_button_release  (GtkWidget        *widget,
387
                                              GdkEventButton   *event);
388
static gboolean gtk_notebook_popup_menu      (GtkWidget        *widget);
Paolo Borelli's avatar
Paolo Borelli committed
389 390
static gboolean gtk_notebook_enter_notify    (GtkWidget        *widget,
                                              GdkEventCrossing *event);
391
static gboolean gtk_notebook_leave_notify    (GtkWidget        *widget,
392
                                              GdkEventCrossing *event);
393
static gboolean gtk_notebook_motion_notify   (GtkWidget        *widget,
394
                                              GdkEventMotion   *event);
395
static gboolean gtk_notebook_focus_in        (GtkWidget        *widget,
396
                                              GdkEventFocus    *event);
397
static gboolean gtk_notebook_focus_out       (GtkWidget        *widget,
398
                                              GdkEventFocus    *event);
Matthias Clasen's avatar
Matthias Clasen committed
399
static void gtk_notebook_grab_notify         (GtkWidget          *widget,
400
                                              gboolean            was_grabbed);
401
static void gtk_notebook_state_flags_changed (GtkWidget          *widget,
402
                                              GtkStateFlags       previous_state);
403
static gboolean gtk_notebook_focus           (GtkWidget        *widget,
404
                                              GtkDirectionType  direction);
405
static void gtk_notebook_style_updated       (GtkWidget        *widget);
406

407 408
/*** Drag and drop Methods ***/
static void gtk_notebook_drag_begin          (GtkWidget        *widget,
409
                                              GdkDragContext   *context);
410
static void gtk_notebook_drag_end            (GtkWidget        *widget,
411
                                              GdkDragContext   *context);
412
static gboolean gtk_notebook_drag_failed     (GtkWidget        *widget,
413 414
                                              GdkDragContext   *context,
                                              GtkDragResult     result);
415
static gboolean gtk_notebook_drag_motion     (GtkWidget        *widget,
416 417 418 419
                                              GdkDragContext   *context,
                                              gint              x,
                                              gint              y,
                                              guint             time);
420
static void gtk_notebook_drag_leave          (GtkWidget        *widget,
421 422
                                              GdkDragContext   *context,
                                              guint             time);
423
static gboolean gtk_notebook_drag_drop       (GtkWidget        *widget,
424 425 426 427
                                              GdkDragContext   *context,
                                              gint              x,
                                              gint              y,
                                              guint             time);
428
static void gtk_notebook_drag_data_get       (GtkWidget        *widget,
429 430 431 432
                                              GdkDragContext   *context,
                                              GtkSelectionData *data,
                                              guint             info,
                                              guint             time);
433
static void gtk_notebook_drag_data_received  (GtkWidget        *widget,
434 435 436 437 438 439
                                              GdkDragContext   *context,
                                              gint              x,
                                              gint              y,
                                              GtkSelectionData *data,
                                              guint             info,
                                              guint             time);
440 441
static void gtk_notebook_direction_changed   (GtkWidget        *widget,
                                              GtkTextDirection  previous_direction);
442

443
/*** GtkContainer Methods ***/
Tim Janik's avatar
Tim Janik committed
444
static void gtk_notebook_set_child_property  (GtkContainer     *container,
445 446 447 448
                                              GtkWidget        *child,
                                              guint             property_id,
                                              const GValue     *value,
                                              GParamSpec       *pspec);
Tim Janik's avatar
Tim Janik committed
449
static void gtk_notebook_get_child_property  (GtkContainer     *container,
450 451 452 453
                                              GtkWidget        *child,
                                              guint             property_id,
                                              GValue           *value,
                                              GParamSpec       *pspec);
454
static void gtk_notebook_add                 (GtkContainer     *container,
455
                                              GtkWidget        *widget);
456
static void gtk_notebook_remove              (GtkContainer     *container,
457
                                              GtkWidget        *widget);
458
static void gtk_notebook_set_focus_child     (GtkContainer     *container,
459
                                              GtkWidget        *child);
Manish Singh's avatar
Manish Singh committed
460
static GType gtk_notebook_child_type       (GtkContainer     *container);
461
static void gtk_notebook_forall              (GtkContainer     *container,
462 463 464
                                              gboolean          include_internals,
                                              GtkCallback       callback,
                                              gpointer          callback_data);
465

466 467
/*** GtkNotebook Methods ***/
static gint gtk_notebook_real_insert_page    (GtkNotebook      *notebook,
468 469 470 471
                                              GtkWidget        *child,
                                              GtkWidget        *tab_label,
                                              GtkWidget        *menu_label,
                                              gint              position);
472

473 474 475 476 477
static GtkNotebook *gtk_notebook_create_window (GtkNotebook    *notebook,
                                                GtkWidget      *page,
                                                gint            x,
                                                gint            y);

478
/*** Gadget Functions ***/
479
static void gtk_notebook_measure_tabs        (GtkCssGadget     *gadget,
480 481 482 483 484 485 486
                                              GtkOrientation    orientation,
                                              gint              for_size,
                                              gint             *minimum,
                                              gint             *natural,
                                              gint             *minimum_baseline,
                                              gint             *natural_baseline,
                                              gpointer          data);
487
static void gtk_notebook_allocate_tabs       (GtkCssGadget     *gadget,
488 489 490 491
                                              const GtkAllocation *allocation,
                                              int               baseline,
                                              GtkAllocation    *out_clip,
                                              gpointer          data);
492
static gboolean gtk_notebook_draw_tabs       (GtkCssGadget     *gadget,
493 494 495 496 497 498
                                              cairo_t          *cr,
                                              int               x,
                                              int               y,
                                              int               width,
                                              int               height,
                                              gpointer          data);
Benjamin Otte's avatar
Benjamin Otte committed
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
static void gtk_notebook_measure_stack       (GtkCssGadget     *gadget,
                                              GtkOrientation    orientation,
                                              gint              for_size,
                                              gint             *minimum,
                                              gint             *natural,
                                              gint             *minimum_baseline,
                                              gint             *natural_baseline,
                                              gpointer          data);
static void gtk_notebook_allocate_stack      (GtkCssGadget     *gadget,
                                              const GtkAllocation *allocation,
                                              int               baseline,
                                              GtkAllocation    *out_clip,
                                              gpointer          data);
static gboolean gtk_notebook_draw_stack      (GtkCssGadget     *gadget,
                                              cairo_t          *cr,
                                              int               x,
                                              int               y,
                                              int               width,
                                              int               height,
                                              gpointer          data);
519

520
/*** GtkNotebook Private Functions ***/
521
static void gtk_notebook_redraw_arrows       (GtkNotebook      *notebook);
522
static void gtk_notebook_real_remove         (GtkNotebook      *notebook,
523
                                              GList            *list);
524 525
static void gtk_notebook_update_labels       (GtkNotebook      *notebook);
static gint gtk_notebook_timer               (GtkNotebook      *notebook);
526
static void gtk_notebook_set_scroll_timer    (GtkNotebook *notebook);
527
static gint gtk_notebook_page_compare        (gconstpointer     a,
528
                                              gconstpointer     b);
529
static GList* gtk_notebook_find_child        (GtkNotebook      *notebook,
530
                                              GtkWidget        *child);
531
static GList * gtk_notebook_search_page      (GtkNotebook      *notebook,
532 533 534
                                              GList            *list,
                                              gint              direction,
                                              gboolean          find_visible);
535
static void  gtk_notebook_child_reordered    (GtkNotebook      *notebook,
536
                                              GtkNotebookPage  *page);
537

538
/*** GtkNotebook Size Allocate Functions ***/
539 540
static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook,
                                              const GtkAllocation *allocation);
541
static void gtk_notebook_calc_tabs           (GtkNotebook      *notebook,
542 543 544 545
                                              GList            *start,
                                              GList           **end,
                                              gint             *tab_space,
                                              guint             direction);
546

547
/*** GtkNotebook Page Switch Methods ***/
548
static void gtk_notebook_real_switch_page    (GtkNotebook      *notebook,
549 550
                                              GtkWidget        *child,
                                              guint             page_num);
551

552
/*** GtkNotebook Page Switch Functions ***/
553
static void gtk_notebook_switch_page         (GtkNotebook      *notebook,
554
                                              GtkNotebookPage  *page);
555
static gint gtk_notebook_page_select         (GtkNotebook      *notebook,
556
                                              gboolean          move_focus);
557 558
static void gtk_notebook_switch_focus_tab    (GtkNotebook      *notebook,
                                              GList            *new_child);
559
static void gtk_notebook_menu_switch_page    (GtkWidget        *widget,
560
                                              GtkNotebookPage  *page);
561

562
/*** GtkNotebook Menu Functions ***/
563
static void gtk_notebook_menu_item_create    (GtkNotebook      *notebook,
564
                                              GList            *list);
565 566
static void gtk_notebook_menu_item_recreate  (GtkNotebook      *notebook,
                                              GList            *list);
567
static void gtk_notebook_menu_label_unparent (GtkWidget        *widget,
568
                                              gpointer          data);
569
static void gtk_notebook_menu_detacher       (GtkWidget        *widget,
570
                                              GtkMenu          *menu);
571

Benjamin Otte's avatar
Benjamin Otte committed
572 573
static void gtk_notebook_update_tab_pos      (GtkNotebook      *notebook);

574
/*** GtkNotebook Private Setters ***/
575
static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
576 577
                                                            gboolean overload,
                                                            gpointer data);
578

579 580
static gboolean focus_tabs_in  (GtkNotebook      *notebook);
static gboolean focus_child_in (GtkNotebook      *notebook,
581
                                GtkDirectionType  direction);
Elliot Lee's avatar
Elliot Lee committed
582

583
static void stop_scrolling (GtkNotebook *notebook);
584
static void do_detach_tab  (GtkNotebook *from,
585 586 587 588
                            GtkNotebook *to,
                            GtkWidget   *child,
                            gint         x,
                            gint         y);
589

Johan Dahlin's avatar
Johan Dahlin committed
590 591
/* GtkBuildable */
static void gtk_notebook_buildable_init           (GtkBuildableIface *iface);
592
static void gtk_notebook_buildable_add_child      (GtkBuildable *buildable,
593 594 595
                                                   GtkBuilder   *builder,
                                                   GObject      *child,
                                                   const gchar  *type);
596

597
static guint notebook_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
598

Johan Dahlin's avatar
Johan Dahlin committed
599
G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
600
                         G_ADD_PRIVATE (GtkNotebook)
601 602
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_notebook_buildable_init))
Elliot Lee's avatar
Elliot Lee committed
603

604 605
static void
add_tab_bindings (GtkBindingSet    *binding_set,
606 607
                  GdkModifierType   modifiers,
                  GtkDirectionType  direction)
608
{
609
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
610 611
                                "move_focus_out", 1,
                                GTK_TYPE_DIRECTION_TYPE, direction);
612
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
613 614 615 616 617 618
                                "move_focus_out", 1,
                                GTK_TYPE_DIRECTION_TYPE, direction);
}

static void
add_arrow_bindings (GtkBindingSet    *binding_set,
619 620
                    guint             keysym,
                    GtkDirectionType  direction)
621
{
622
  guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
623

624 625 626 627 628 629 630 631
  gtk_binding_entry_add_signal (binding_set, keysym, GDK_CONTROL_MASK,
                                "move_focus_out", 1,
                                GTK_TYPE_DIRECTION_TYPE, direction);
  gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_CONTROL_MASK,
                                "move_focus_out", 1,
                                GTK_TYPE_DIRECTION_TYPE, direction);
}

632 633
static void
add_reorder_bindings (GtkBindingSet    *binding_set,
634 635 636
                      guint             keysym,
                      GtkDirectionType  direction,
                      gboolean          move_to_last)
637
{
638
  guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
639 640

  gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
641 642 643
                                "reorder_tab", 2,
                                GTK_TYPE_DIRECTION_TYPE, direction,
                                G_TYPE_BOOLEAN, move_to_last);
644
  gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
645 646 647
                                "reorder_tab", 2,
                                GTK_TYPE_DIRECTION_TYPE, direction,
                                G_TYPE_BOOLEAN, move_to_last);
648 649
}

650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
static gboolean
gtk_object_handled_accumulator (GSignalInvocationHint *ihint,
                                GValue                *return_accu,
                                const GValue          *handler_return,
                                gpointer               dummy)
{
  gboolean continue_emission;
  GObject *object;

  object = g_value_get_object (handler_return);
  g_value_set_object (return_accu, object);
  continue_emission = !object;

  return continue_emission;
}

666
static void
667 668 669
gtk_notebook_compute_expand (GtkWidget *widget,
                             gboolean  *hexpand_p,
                             gboolean  *vexpand_p)
670
{
671
  GtkNotebook *notebook = GTK_NOTEBOOK (widget);
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
  GtkNotebookPrivate *priv = notebook->priv;
  gboolean hexpand;
  gboolean vexpand;
  GList *list;
  GtkNotebookPage *page;

  hexpand = FALSE;
  vexpand = FALSE;

  for (list = priv->children; list; list = list->next)
    {
      page = list->data;

      hexpand = hexpand ||
        gtk_widget_compute_expand (page->child, GTK_ORIENTATION_HORIZONTAL);

      vexpand = vexpand ||
        gtk_widget_compute_expand (page->child, GTK_ORIENTATION_VERTICAL);

      if (hexpand & vexpand)
        break;
    }

  *hexpand_p = hexpand;
  *vexpand_p = vexpand;
}

Elliot Lee's avatar
Elliot Lee committed
699 700 701
static void
gtk_notebook_class_init (GtkNotebookClass *class)
{
Tim Janik's avatar
Tim Janik committed
702 703 704
  GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
705
  GtkBindingSet *binding_set;
706

707 708
  gobject_class->set_property = gtk_notebook_set_property;
  gobject_class->get_property = gtk_notebook_get_property;
709
  gobject_class->finalize = gtk_notebook_finalize;
710

711
  widget_class->destroy = gtk_notebook_destroy;
Elliot Lee's avatar
Elliot Lee committed
712 713 714
  widget_class->map = gtk_notebook_map;
  widget_class->unmap = gtk_notebook_unmap;
  widget_class->realize = gtk_notebook_realize;
715
  widget_class->unrealize = gtk_notebook_unrealize;
716 717
  widget_class->get_preferred_width = gtk_notebook_get_preferred_width;
  widget_class->get_preferred_height = gtk_notebook_get_preferred_height;
718 719
  widget_class->get_preferred_width_for_height = gtk_notebook_get_preferred_width_for_height;
  widget_class->get_preferred_height_for_width = gtk_notebook_get_preferred_height_for_width;
Elliot Lee's avatar
Elliot Lee committed
720
  widget_class->size_allocate = gtk_notebook_size_allocate;
Benjamin Otte's avatar
Benjamin Otte committed
721
  widget_class->draw = gtk_notebook_draw;
Elliot Lee's avatar
Elliot Lee committed
722
  widget_class->button_press_event = gtk_notebook_button_press;
723
  widget_class->button_release_event = gtk_notebook_button_release;
724
  widget_class->popup_menu = gtk_notebook_popup_menu;
Paolo Borelli's avatar
Paolo Borelli committed
725
  widget_class->enter_notify_event = gtk_notebook_enter_notify;
726 727
  widget_class->leave_notify_event = gtk_notebook_leave_notify;
  widget_class->motion_notify_event = gtk_notebook_motion_notify;
Matthias Clasen's avatar
Matthias Clasen committed
728
  widget_class->grab_notify = gtk_notebook_grab_notify;
729
  widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
730
  widget_class->focus_in_event = gtk_notebook_focus_in;
731
  widget_class->focus_out_event = gtk_notebook_focus_out;
732
  widget_class->focus = gtk_notebook_focus;
733
  widget_class->style_updated = gtk_notebook_style_updated;
734
  widget_class->drag_begin = gtk_notebook_drag_begin;
735
  widget_class->drag_end = gtk_notebook_drag_end;
736
  widget_class->drag_motion = gtk_notebook_drag_motion;
737
  widget_class->drag_leave = gtk_notebook_drag_leave;
738 739 740
  widget_class->drag_drop = gtk_notebook_drag_drop;
  widget_class->drag_data_get = gtk_notebook_drag_data_get;
  widget_class->drag_data_received = gtk_notebook_drag_data_received;
741
  widget_class->drag_failed = gtk_notebook_drag_failed;
742
  widget_class->compute_expand = gtk_notebook_compute_expand;
743
  widget_class->direction_changed = gtk_notebook_direction_changed;
744

Elliot Lee's avatar
Elliot Lee committed
745 746
  container_class->add = gtk_notebook_add;
  container_class->remove = gtk_notebook_remove;
747
  container_class->forall = gtk_notebook_forall;
Lars Hamann's avatar
Lars Hamann committed
748
  container_class->set_focus_child = gtk_notebook_set_focus_child;
Tim Janik's avatar
Tim Janik committed
749 750
  container_class->get_child_property = gtk_notebook_get_child_property;
  container_class->set_child_property = gtk_notebook_set_child_property;
751
  container_class->child_type = gtk_notebook_child_type;
752

753
  class->switch_page = gtk_notebook_real_switch_page;
754
  class->insert_page = gtk_notebook_real_insert_page;
755

756 757
  class->focus_tab = gtk_notebook_focus_tab;
  class->select_page = gtk_notebook_select_page;
758
  class->change_current_page = gtk_notebook_change_current_page;
759
  class->move_focus_out = gtk_notebook_move_focus_out;
760
  class->reorder_tab = gtk_notebook_reorder_tab;
761
  class->create_window = gtk_notebook_create_window;
762

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
  properties[PROP_PAGE] =
      g_param_spec_int ("page",
                        P_("Page"),
                        P_("The index of the current page"),
                        -1, G_MAXINT,
                        -1,
                        GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_TAB_POS] =
      g_param_spec_enum ("tab-pos",
                         P_("Tab Position"),
                         P_("Which side of the notebook holds the tabs"),
                         GTK_TYPE_POSITION_TYPE,
                         GTK_POS_TOP,
                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_SHOW_TABS] =
      g_param_spec_boolean ("show-tabs",
                            P_("Show Tabs"),
                            P_("Whether tabs should be shown"),
                            TRUE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_SHOW_BORDER] =
      g_param_spec_boolean ("show-border",
                            P_("Show Border"),
                            P_("Whether the border should be shown"),
                            TRUE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_SCROLLABLE] =
      g_param_spec_boolean ("scrollable",
                            P_("Scrollable"),
                            P_("If TRUE, scroll arrows are added if there are too many tabs to fit"),
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  properties[PROP_ENABLE_POPUP] =
      g_param_spec_boolean ("enable-popup",
                            P_("Enable Popup"),
                            P_("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
                            FALSE,
                            GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);
Matthias Clasen's avatar
Matthias Clasen committed
806 807

  /**
808
   * GtkNotebook:group-name:
Matthias Clasen's avatar
Matthias Clasen committed
809
   *
810
   * Group name for tab drag and drop.
Matthias Clasen's avatar
Matthias Clasen committed
811 812
   *
   * Since: 2.24
813
   */
814 815 816 817 818 819 820 821
  properties[PROP_GROUP_NAME] =
      g_param_spec_string ("group-name",
                           P_("Group Name"),
                           P_("Group name for tab drag and drop"),
                           NULL,
                           GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY);

  g_object_class_install_properties (gobject_class, LAST_PROP, properties);
Tim Janik's avatar
Tim Janik committed
822 823

  gtk_container_class_install_child_property (container_class,
824 825 826 827 828 829
                                              CHILD_PROP_TAB_LABEL,
                                              g_param_spec_string ("tab-label",
                                                                   P_("Tab label"),
                                                                   P_("The string displayed on the child's tab label"),
                                                                   NULL,
                                                                   GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
830
  gtk_container_class_install_child_property (container_class,
831 832 833 834 835 836
                                              CHILD_PROP_MENU_LABEL,
                                              g_param_spec_string ("menu-label",
                                                                   P_("Menu label"),
                                                                   P_("The string displayed in the child's menu entry"),
                                                                   NULL,
                                                                   GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
837
  gtk_container_class_install_child_property (container_class,
838 839 840 841 842 843
                                              CHILD_PROP_POSITION,
                                              g_param_spec_int ("position",
                                                                P_("Position"),
                                                                P_("The index of the child in the parent"),
                                                                -1, G_MAXINT, 0,
                                                                GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
844
  gtk_container_class_install_child_property (container_class,
845 846 847 848 849 850
                                              CHILD_PROP_TAB_EXPAND,
                                              g_param_spec_boolean ("tab-expand",
                                                                    P_("Tab expand"),
                                                                    P_("Whether to expand the child's tab"),
                                                                    FALSE,
                                                                    GTK_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
851
  gtk_container_class_install_child_property (container_class,
852 853 854 855 856 857
                                              CHILD_PROP_TAB_FILL,
                                              g_param_spec_boolean ("tab-fill",
                                                                    P_("Tab fill"),
                                                                    P_("Whether the child's tab should fill the allocated area"),
                                                                    TRUE,
                                                                    GTK_PARAM_READWRITE));
858

859
  gtk_container_class_install_child_property (container_class,
860 861 862 863 864 865
                                              CHILD_PROP_REORDERABLE,
                                              g_param_spec_boolean ("reorderable",
                                                                    P_("Tab reorderable"),
                                                                    P_("Whether the tab is reorderable by user action"),
                                                                    FALSE,
                                                                    GTK_PARAM_READWRITE));
866
  gtk_container_class_install_child_property (container_class,
867 868 869 870 871 872
                                              CHILD_PROP_DETACHABLE,
                                              g_param_spec_boolean ("detachable",
                                                                    P_("Tab detachable"),
                                                                    P_("Whether the tab is detachable"),
                                                                    FALSE,
                                                                    GTK_PARAM_READWRITE));
873 874 875 876

/**
 * GtkNotebook:has-secondary-backward-stepper:
 *
William Jon McCann's avatar
William Jon McCann committed
877
 * The “has-secondary-backward-stepper” property determines whether
878
 * a second backward arrow button is displayed on the opposite end
879 880 881
 * of the tab area.
 *
 * Since: 2.4
882
 */
883
  gtk_widget_class_install_style_property (widget_class,
884 885 886 887 888
                                           g_param_spec_boolean ("has-secondary-backward-stepper",
                                                                 P_("Secondary backward stepper"),
                                                                 P_("Display a second backward arrow button on the opposite end of the tab area"),
                                                                 FALSE,
                                                                 GTK_PARAM_READABLE));
889

890 891 892
/**
 * GtkNotebook:has-secondary-forward-stepper:
 *
William Jon McCann's avatar
William Jon McCann committed
893
 * The “has-secondary-forward-stepper” property determines whether
894
 * a second forward arrow button is displayed on the opposite end
895 896 897
 * of the tab area.
 *
 * Since: 2.4
898
 */
899
  gtk_widget_class_install_style_property (widget_class,
900 901 902 903 904
                                           g_param_spec_boolean ("has-secondary-forward-stepper",
                                                                 P_("Secondary forward stepper"),
                                                                 P_("Display a second forward arrow button on the opposite end of the tab area"),
                                                                 FALSE,
                                                                 GTK_PARAM_READABLE));
905

906 907 908
/**
 * GtkNotebook:has-backward-stepper:
 *
William Jon McCann's avatar
William Jon McCann committed
909
 * The “has-backward-stepper” property determines whether
910 911 912
 * the standard backward arrow button is displayed.
 *
 * Since: 2.4
913
 */
914
  gtk_widget_class_install_style_property (widget_class,
915 916 917 918 919
                                           g_param_spec_boolean ("has-backward-stepper",
                                                                 P_("Backward stepper"),
                                                                 P_("Display the standard backward arrow button"),
                                                                 TRUE,
                                                                 GTK_PARAM_READABLE));
920

921 922 923
/**
 * GtkNotebook:has-forward-stepper:
 *
William Jon McCann's avatar
William Jon McCann committed
924
 * The “has-forward-stepper” property determines whether
925 926 927
 * the standard forward arrow button is displayed.
 *
 * Since: 2.4
928
 */
929
  gtk_widget_class_install_style_property (widget_class,
930 931 932 933 934 935
                                           g_param_spec_boolean ("has-forward-stepper",
                                                                 P_("Forward stepper"),
                                                                 P_("Display the standard forward arrow button"),
                                                                 TRUE,
                                                                 GTK_PARAM_READABLE));

936 937 938
/**
 * GtkNotebook:tab-overlap:
 *
William Jon McCann's avatar
William Jon McCann committed
939
 * The “tab-overlap” property defines size of tab overlap
940 941 942
 * area.
 *
 * Since: 2.10
943 944 945
 *
 * Deprecated: 3.20: This property is ignored. Use margins on tab nodes
 *     to achieve the same effect.
946
 */
947
  gtk_widget_class_install_style_property (widget_class,
948 949 950 951 952 953
                                           g_param_spec_int ("tab-overlap",
                                                             P_("Tab overlap"),
                                                             P_("Size of tab overlap area"),
                                                             G_MININT,
                                                             G_MAXINT,
                                                             2,
954
                                                             GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
955 956 957 958

/**
 * GtkNotebook:tab-curvature:
 *
William Jon McCann's avatar
William Jon McCann committed
959
 * The “tab-curvature” property defines size of tab curvature.
960 961
 *
 * Since: 2.10
962 963 964
 *
 * Deprecated: 3.20: This property is ignored. Use margins on tab nodes
 *     to achieve the same effect.
965
 */
966
  gtk_widget_class_install_style_property (widget_class,
967 968 969 970 971 972
                                           g_param_spec_int ("tab-curvature",
                                                             P_("Tab curvature"),
                                                             P_("Size of tab curvature"),
                                                             0,
                                                             G_MAXINT,
                                                             1,
973
                                                             GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
974

975 976 977
  /**
   * GtkNotebook:arrow-spacing:
   *
978
   * The "arrow-spacing" property defines the spacing between the scroll
979 980 981
   * arrows and the tabs.
   *
   * Since: 2.10
982 983 984
   *
   * Deprecated: 3.20: This property is ignored. Use margins on arrows or
   *     the "tabs" node to achieve the same effect.
985 986 987
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("arrow-spacing",
988 989
                                                             P_("Arrow spacing"),
                                                             P_("Scroll arrow spacing"),
990 991 992
                                                             0,
                                                             G_MAXINT,
                                                             0,
993
                                                             GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
994

995 996 997 998 999 1000 1001
  /**
   * GtkNotebook:initial-gap:
   *
   * The "initial-gap" property defines the minimum size for the initial
   * gap between the first tab.
   *
   * Since: 3.2
1002 1003 1004
   *
   * Deprecated: 3.20: The intial gap is ignored. Use margins on the header node
   *     to achieve the same effect.
1005 1006 1007 1008 1009 1010 1011 1012
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("initial-gap",
                                                             P_("Initial gap"),
                                                             P_("Initial gap before the first tab"),
                                                             0,
                                                             G_MAXINT,
                                                             0,
1013
                                                             GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
1014

1015 1016 1017 1018 1019 1020 1021 1022 1023
  /**
   * GtkNotebook:has-tab-gap:
   *
   * The "has-tab-gap" property defines whether the active tab is draw
   * with a gap at the bottom. When %TRUE the theme engine uses
   * gtk_render_extension to draw the active tab. When %FALSE
   * gtk_render_background and gtk_render_frame are used.
   *
   * Since: 3.12
1024 1025
   *
   * Deprecated: 3.20: This function always behaves as if it was set to %FALSE.
1026 1027 1028 1029 1030 1031
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_boolean ("has-tab-gap",
                                                                 P_("Tab gap"),
                                                                 P_("Active tab is drawn with a gap at the bottom"),
                                                                 TRUE,
1032
                                                                 GTK_PARAM_READABLE | G_PARAM_DEPRECATED));
1033

1034 1035 1036 1037 1038 1039 1040 1041
  /**
   * GtkNotebook::switch-page:
   * @notebook: the object which received the signal.
   * @page: the new current page
   * @page_num: the index of the page
   *
   * Emitted when the user or a function changes the current page.
   */
1042