gtknotebook.c 257 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 133
 * The main node gets the .frame style class when the notebook
 * has a border (see gtk_notebook_set_has_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 142
 */


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

147 148 149 150
#define TIMEOUT_INITIAL  500
#define TIMEOUT_REPEAT    50
#define TIMEOUT_EXPAND   500

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

typedef enum
{
  DRAG_OPERATION_NONE,
  DRAG_OPERATION_REORDER,
  DRAG_OPERATION_DETACH
} GtkNotebookDragOperation;

enum {
  ACTION_WIDGET_START,
  ACTION_WIDGET_END,
  N_ACTION_WIDGETS
};

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

  GdkWindow               *drag_window;
  GdkWindow               *event_window;

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

186 187 188 189 190 191 192 193 194 195 196 197 198 199
  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;

200
  GQuark         group;
201 202 203

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

  guint32        timer;

  guint          button             : 2;
  guint          child_has_focus    : 1;
  guint          click_child        : 3;
  guint          during_detach      : 1;
  guint          during_reorder     : 1;
213
  guint          remove_in_detach   : 1;
214 215 216 217 218 219 220 221 222 223
  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;
};

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

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

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

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

259 260 261
#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)

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

274 275
static GParamSpec *properties[LAST_PROP];

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

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

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

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

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

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

  GtkRequisition requisition;
309

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

314
static const GtkTargetEntry notebook_targets [] = {
315
  { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
316 317
};

318

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

342
/*** GObject Methods ***/
343 344 345 346 347 348 349 350
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);
351

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

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

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

460 461
/*** GtkNotebook Methods ***/
static gint gtk_notebook_real_insert_page    (GtkNotebook      *notebook,
462 463 464 465
                                              GtkWidget        *child,
                                              GtkWidget        *tab_label,
                                              GtkWidget        *menu_label,
                                              gint              position);
466

467 468 469 470 471
static GtkNotebook *gtk_notebook_create_window (GtkNotebook    *notebook,
                                                GtkWidget      *page,
                                                gint            x,
                                                gint            y);

472
/*** Gadget Functions ***/
473
static void gtk_notebook_measure_tabs        (GtkCssGadget     *gadget,
474 475 476 477 478 479 480
                                              GtkOrientation    orientation,
                                              gint              for_size,
                                              gint             *minimum,
                                              gint             *natural,
                                              gint             *minimum_baseline,
                                              gint             *natural_baseline,
                                              gpointer          data);
481
static void gtk_notebook_allocate_tabs       (GtkCssGadget     *gadget,
482 483 484 485
                                              const GtkAllocation *allocation,
                                              int               baseline,
                                              GtkAllocation    *out_clip,
                                              gpointer          data);
486
static gboolean gtk_notebook_draw_tabs       (GtkCssGadget     *gadget,
487 488 489 490 491 492
                                              cairo_t          *cr,
                                              int               x,
                                              int               y,
                                              int               width,
                                              int               height,
                                              gpointer          data);
Benjamin Otte's avatar
Benjamin Otte committed
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
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);
513

514
/*** GtkNotebook Private Functions ***/
515
static void gtk_notebook_redraw_arrows       (GtkNotebook      *notebook);
516
static void gtk_notebook_real_remove         (GtkNotebook      *notebook,
517
                                              GList            *list);
518 519
static void gtk_notebook_update_labels       (GtkNotebook      *notebook);
static gint gtk_notebook_timer               (GtkNotebook      *notebook);
520
static void gtk_notebook_set_scroll_timer    (GtkNotebook *notebook);
521
static gint gtk_notebook_page_compare        (gconstpointer     a,
522
                                              gconstpointer     b);
523
static GList* gtk_notebook_find_child        (GtkNotebook      *notebook,
524
                                              GtkWidget        *child);
525
static GList * gtk_notebook_search_page      (GtkNotebook      *notebook,
526 527 528
                                              GList            *list,
                                              gint              direction,
                                              gboolean          find_visible);
529
static void  gtk_notebook_child_reordered    (GtkNotebook      *notebook,
530
                                              GtkNotebookPage  *page);
531

532
/*** GtkNotebook Size Allocate Functions ***/
533 534
static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook,
                                              const GtkAllocation *allocation);
535
static void gtk_notebook_calc_tabs           (GtkNotebook      *notebook,
536 537 538 539
                                              GList            *start,
                                              GList           **end,
                                              gint             *tab_space,
                                              guint             direction);
540

541
/*** GtkNotebook Page Switch Methods ***/
542
static void gtk_notebook_real_switch_page    (GtkNotebook      *notebook,
543 544
                                              GtkWidget        *child,
                                              guint             page_num);
545

546
/*** GtkNotebook Page Switch Functions ***/
547
static void gtk_notebook_switch_page         (GtkNotebook      *notebook,
548
                                              GtkNotebookPage  *page);
549
static gint gtk_notebook_page_select         (GtkNotebook      *notebook,
550
                                              gboolean          move_focus);
551 552
static void gtk_notebook_switch_focus_tab    (GtkNotebook      *notebook,
                                              GList            *new_child);
553
static void gtk_notebook_menu_switch_page    (GtkWidget        *widget,
554
                                              GtkNotebookPage  *page);
555

556
/*** GtkNotebook Menu Functions ***/
557
static void gtk_notebook_menu_item_create    (GtkNotebook      *notebook,
558
                                              GList            *list);
559
static void gtk_notebook_menu_label_unparent (GtkWidget        *widget,
560
                                              gpointer          data);
561
static void gtk_notebook_menu_detacher       (GtkWidget        *widget,
562
                                              GtkMenu          *menu);
563

Benjamin Otte's avatar
Benjamin Otte committed
564 565
static void gtk_notebook_update_tab_pos      (GtkNotebook      *notebook);

566
/*** GtkNotebook Private Setters ***/
567
static gboolean gtk_notebook_mnemonic_activate_switch_page (GtkWidget *child,
568 569
                                                            gboolean overload,
                                                            gpointer data);
570

571 572
static gboolean focus_tabs_in  (GtkNotebook      *notebook);
static gboolean focus_child_in (GtkNotebook      *notebook,
573
                                GtkDirectionType  direction);
Elliot Lee's avatar
Elliot Lee committed
574

575
static void stop_scrolling (GtkNotebook *notebook);
576
static void do_detach_tab  (GtkNotebook *from,
577 578 579 580
                            GtkNotebook *to,
                            GtkWidget   *child,
                            gint         x,
                            gint         y);
581

Johan Dahlin's avatar
Johan Dahlin committed
582 583
/* GtkBuildable */
static void gtk_notebook_buildable_init           (GtkBuildableIface *iface);
584
static void gtk_notebook_buildable_add_child      (GtkBuildable *buildable,
585 586 587
                                                   GtkBuilder   *builder,
                                                   GObject      *child,
                                                   const gchar  *type);
588

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

Johan Dahlin's avatar
Johan Dahlin committed
591
G_DEFINE_TYPE_WITH_CODE (GtkNotebook, gtk_notebook, GTK_TYPE_CONTAINER,
592
                         G_ADD_PRIVATE (GtkNotebook)
593 594
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_notebook_buildable_init))
Elliot Lee's avatar
Elliot Lee committed
595

596 597
static void
add_tab_bindings (GtkBindingSet    *binding_set,
598 599
                  GdkModifierType   modifiers,
                  GtkDirectionType  direction)
600
{
601
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Tab, modifiers,
602 603
                                "move_focus_out", 1,
                                GTK_TYPE_DIRECTION_TYPE, direction);
604
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Tab, modifiers,
605 606 607 608 609 610
                                "move_focus_out", 1,
                                GTK_TYPE_DIRECTION_TYPE, direction);
}

static void
add_arrow_bindings (GtkBindingSet    *binding_set,
611 612
                    guint             keysym,
                    GtkDirectionType  direction)
613
{
614
  guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
615

616 617 618 619 620 621 622 623
  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);
}

624 625
static void
add_reorder_bindings (GtkBindingSet    *binding_set,
626 627 628
                      guint             keysym,
                      GtkDirectionType  direction,
                      gboolean          move_to_last)
629
{
630
  guint keypad_keysym = keysym - GDK_KEY_Left + GDK_KEY_KP_Left;
631 632

  gtk_binding_entry_add_signal (binding_set, keysym, GDK_MOD1_MASK,
633 634 635
                                "reorder_tab", 2,
                                GTK_TYPE_DIRECTION_TYPE, direction,
                                G_TYPE_BOOLEAN, move_to_last);
636
  gtk_binding_entry_add_signal (binding_set, keypad_keysym, GDK_MOD1_MASK,
637 638 639
                                "reorder_tab", 2,
                                GTK_TYPE_DIRECTION_TYPE, direction,
                                G_TYPE_BOOLEAN, move_to_last);
640 641
}

642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
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;
}

658
static void
659 660 661
gtk_notebook_compute_expand (GtkWidget *widget,
                             gboolean  *hexpand_p,
                             gboolean  *vexpand_p)
662
{
663
  GtkNotebook *notebook = GTK_NOTEBOOK (widget);
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690
  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
691 692 693
static void
gtk_notebook_class_init (GtkNotebookClass *class)
{
Tim Janik's avatar
Tim Janik committed
694 695 696
  GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
697
  GtkBindingSet *binding_set;
698

699 700
  gobject_class->set_property = gtk_notebook_set_property;
  gobject_class->get_property = gtk_notebook_get_property;
701

702
  widget_class->destroy = gtk_notebook_destroy;
Elliot Lee's avatar
Elliot Lee committed
703 704 705
  widget_class->map = gtk_notebook_map;
  widget_class->unmap = gtk_notebook_unmap;
  widget_class->realize = gtk_notebook_realize;
706
  widget_class->unrealize = gtk_notebook_unrealize;
707 708
  widget_class->get_preferred_width = gtk_notebook_get_preferred_width;
  widget_class->get_preferred_height = gtk_notebook_get_preferred_height;
709 710
  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
711
  widget_class->size_allocate = gtk_notebook_size_allocate;
Benjamin Otte's avatar
Benjamin Otte committed
712
  widget_class->draw = gtk_notebook_draw;
Elliot Lee's avatar
Elliot Lee committed
713
  widget_class->button_press_event = gtk_notebook_button_press;
714
  widget_class->button_release_event = gtk_notebook_button_release;
715
  widget_class->popup_menu = gtk_notebook_popup_menu;
Paolo Borelli's avatar
Paolo Borelli committed
716
  widget_class->enter_notify_event = gtk_notebook_enter_notify;
717 718
  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
719
  widget_class->grab_notify = gtk_notebook_grab_notify;
720
  widget_class->state_flags_changed = gtk_notebook_state_flags_changed;
721
  widget_class->focus_in_event = gtk_notebook_focus_in;
722
  widget_class->focus_out_event = gtk_notebook_focus_out;
723
  widget_class->focus = gtk_notebook_focus;
724
  widget_class->style_updated = gtk_notebook_style_updated;
725
  widget_class->drag_begin = gtk_notebook_drag_begin;
726
  widget_class->drag_end = gtk_notebook_drag_end;
727
  widget_class->drag_motion = gtk_notebook_drag_motion;
728
  widget_class->drag_leave = gtk_notebook_drag_leave;
729 730 731
  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;
732
  widget_class->drag_failed = gtk_notebook_drag_failed;
733
  widget_class->compute_expand = gtk_notebook_compute_expand;
734
  widget_class->direction_changed = gtk_notebook_direction_changed;
735

Elliot Lee's avatar
Elliot Lee committed
736 737
  container_class->add = gtk_notebook_add;
  container_class->remove = gtk_notebook_remove;
738
  container_class->forall = gtk_notebook_forall;
Lars Hamann's avatar
Lars Hamann committed
739
  container_class->set_focus_child = gtk_notebook_set_focus_child;
Tim Janik's avatar
Tim Janik committed
740 741
  container_class->get_child_property = gtk_notebook_get_child_property;
  container_class->set_child_property = gtk_notebook_set_child_property;
742
  container_class->child_type = gtk_notebook_child_type;
743

744
  class->switch_page = gtk_notebook_real_switch_page;
745
  class->insert_page = gtk_notebook_real_insert_page;
746

747 748
  class->focus_tab = gtk_notebook_focus_tab;
  class->select_page = gtk_notebook_select_page;
749
  class->change_current_page = gtk_notebook_change_current_page;
750
  class->move_focus_out = gtk_notebook_move_focus_out;
751
  class->reorder_tab = gtk_notebook_reorder_tab;
752
  class->create_window = gtk_notebook_create_window;
753

754 755 756 757 758 759 760 761 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
  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
797 798

  /**
799
   * GtkNotebook:group-name:
Matthias Clasen's avatar
Matthias Clasen committed
800
   *
801
   * Group name for tab drag and drop.
Matthias Clasen's avatar
Matthias Clasen committed
802 803
   *
   * Since: 2.24
804
   */
805 806 807 808 809 810 811 812
  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
813 814

  gtk_container_class_install_child_property (container_class,
815 816 817 818 819 820
                                              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
821
  gtk_container_class_install_child_property (container_class,
822 823 824 825 826 827
                                              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
828
  gtk_container_class_install_child_property (container_class,
829 830 831 832 833 834
                                              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