gtknotebook.c 131 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1 2 3 4
/* GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
15 16 17
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
18
 */
19 20

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

Elliot Lee's avatar
Elliot Lee committed
27
#include "gtknotebook.h"
28
#include "gtksignal.h"
29 30 31 32 33 34
#include "gtkmain.h"
#include "gtkmenu.h"
#include "gtkmenuitem.h"
#include "gtklabel.h"
#include <gdk/gdkkeysyms.h>
#include <stdio.h>
35
#include "gtkintl.h"
36
#include "gtkbindings.h"
Elliot Lee's avatar
Elliot Lee committed
37 38 39 40


#define TAB_OVERLAP    2
#define TAB_CURVATURE  1
41 42
#define ARROW_SIZE     12
#define ARROW_SPACING  0
43
#define FOCUS_WIDTH    1
44 45
#define NOTEBOOK_INIT_SCROLL_DELAY (200)
#define NOTEBOOK_SCROLL_DELAY      (100)
Elliot Lee's avatar
Elliot Lee committed
46 47


48 49
enum {
  SWITCH_PAGE,
50 51
  FOCUS_TAB,
  SELECT_PAGE,
52 53 54
  LAST_SIGNAL
};

55 56 57 58 59
enum {
  STEP_PREV,
  STEP_NEXT
};

60
enum {
61 62 63 64 65 66 67 68 69 70 71
  PROP_0,
  PROP_TAB_POS,
  PROP_SHOW_TABS,
  PROP_SHOW_BORDER,
  PROP_SCROLLABLE,
  PROP_TAB_BORDER,
  PROP_TAB_HBORDER,
  PROP_TAB_VBORDER,
  PROP_PAGE,
  PROP_ENABLE_POPUP,
  PROP_HOMOGENEOUS
72 73 74
};

enum {
Tim Janik's avatar
Tim Janik committed
75 76 77 78 79 80 81
  CHILD_PROP_0,
  CHILD_PROP_TAB_LABEL,
  CHILD_PROP_MENU_LABEL,
  CHILD_PROP_POSITION,
  CHILD_PROP_TAB_EXPAND,
  CHILD_PROP_TAB_FILL,
  CHILD_PROP_TAB_PACK
82 83
};

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
#define GTK_NOTEBOOK_PAGE(_glist_)         ((GtkNotebookPage *)((GList *)(_glist_))->data)

struct _GtkNotebookPage
{
  GtkWidget *child;
  GtkWidget *tab_label;
  GtkWidget *menu_label;

  guint default_menu : 1;	/* If true, we create the menu label ourself */
  guint default_tab  : 1;	/* If true, we create the tab label ourself */
  guint expand       : 1;
  guint fill         : 1;
  guint pack         : 1;

  GtkRequisition requisition;
  GtkAllocation allocation;
100

101
  guint mnemonic_activate_signal;
102 103 104 105 106 107 108 109 110 111 112
};

#ifdef G_DISABLE_CHECKS
#define CHECK_FIND_CHILD(notebook, child)                           \
 gtk_notebook_find_child(notebook, child,                           \
			 G_GNUC_PRETTY_FUNCTION)
#else
#define CHECK_FIND_CHILD(notebook, child)                           \
 gtk_notebook_find_child(notebook, child, NULL)
#endif
 
113
/*** GtkNotebook Methods ***/
114 115
static void gtk_notebook_class_init          (GtkNotebookClass *klass);
static void gtk_notebook_init                (GtkNotebook      *notebook);
116

117 118 119 120 121
static void gtk_notebook_select_page         (GtkNotebook       *notebook,
                                              gboolean           move_focus);
static void gtk_notebook_focus_tab           (GtkNotebook       *notebook,
                                              GtkNotebookTab     type);

122
/*** GtkObject Methods ***/
123
static void gtk_notebook_destroy             (GtkObject        *object);
124 125 126 127 128 129 130 131
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);
132

133
/*** GtkWidget Methods ***/
134 135 136
static void gtk_notebook_map                 (GtkWidget        *widget);
static void gtk_notebook_unmap               (GtkWidget        *widget);
static void gtk_notebook_realize             (GtkWidget        *widget);
137
static void gtk_notebook_unrealize           (GtkWidget        *widget);
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
static void gtk_notebook_size_request        (GtkWidget        *widget,
					      GtkRequisition   *requisition);
static void gtk_notebook_size_allocate       (GtkWidget        *widget,
					      GtkAllocation    *allocation);
static gint gtk_notebook_expose              (GtkWidget        *widget,
					      GdkEventExpose   *event);
static gint gtk_notebook_button_press        (GtkWidget        *widget,
					      GdkEventButton   *event);
static gint gtk_notebook_button_release      (GtkWidget        *widget,
					      GdkEventButton   *event);
static gint gtk_notebook_enter_notify        (GtkWidget        *widget,
					      GdkEventCrossing *event);
static gint gtk_notebook_leave_notify        (GtkWidget        *widget,
					      GdkEventCrossing *event);
static gint gtk_notebook_motion_notify       (GtkWidget        *widget,
					      GdkEventMotion   *event);
154 155 156
static gint gtk_notebook_focus_in            (GtkWidget        *widget,
					      GdkEventFocus    *event);
static void gtk_notebook_draw_focus          (GtkWidget        *widget);
157 158
static gint gtk_notebook_focus               (GtkWidget        *widget,
					      GtkDirectionType  direction);
159

160
/*** GtkContainer Methods ***/
Tim Janik's avatar
Tim Janik committed
161
static void gtk_notebook_set_child_property  (GtkContainer     *container,
162
					      GtkWidget        *child,
Tim Janik's avatar
Tim Janik committed
163 164 165 166
					      guint             property_id,
					      const GValue     *value,
					      GParamSpec       *pspec);
static void gtk_notebook_get_child_property  (GtkContainer     *container,
167
					      GtkWidget        *child,
Tim Janik's avatar
Tim Janik committed
168 169 170
					      guint             property_id,
					      GValue           *value,
					      GParamSpec       *pspec);
171 172 173 174
static void gtk_notebook_add                 (GtkContainer     *container,
					      GtkWidget        *widget);
static void gtk_notebook_remove              (GtkContainer     *container,
					      GtkWidget        *widget);
175 176 177
static void gtk_notebook_set_focus_child     (GtkContainer     *container,
					      GtkWidget        *child);
static GtkType gtk_notebook_child_type       (GtkContainer     *container);
178 179
static void gtk_notebook_forall              (GtkContainer     *container,
					      gboolean		include_internals,
180 181
					      GtkCallback       callback,
					      gpointer          callback_data);
182

183
/*** GtkNotebook Private Functions ***/
184
static void gtk_notebook_panel_realize       (GtkNotebook      *notebook);
185 186
static void gtk_notebook_redraw_tabs         (GtkNotebook      *notebook);
static void gtk_notebook_redraw_arrows       (GtkNotebook      *notebook);
187 188 189 190 191 192 193 194
static void gtk_notebook_focus_changed       (GtkNotebook      *notebook,
					      GtkNotebookPage  *old_page);
static void gtk_notebook_real_remove         (GtkNotebook      *notebook,
					      GList            *list);
static void gtk_notebook_update_labels       (GtkNotebook      *notebook);
static gint gtk_notebook_timer               (GtkNotebook      *notebook);
static gint gtk_notebook_page_compare        (gconstpointer     a,
					      gconstpointer     b);
195 196 197
static GList *gtk_notebook_find_child        (GtkNotebook      *notebook,
					      GtkWidget        *child,
					      const gchar      *function);
198 199 200 201 202 203 204
static gint  gtk_notebook_real_page_position (GtkNotebook      *notebook,
					      GList            *list);
static GList * gtk_notebook_search_page      (GtkNotebook      *notebook,
					      GList            *list,
					      gint              direction,
					      gboolean          find_visible);

205
/*** GtkNotebook Drawing Functions ***/
206 207
static void gtk_notebook_paint               (GtkWidget        *widget,
					      GdkRectangle     *area);
208 209 210
static void gtk_notebook_draw_tab            (GtkNotebook      *notebook,
					      GtkNotebookPage  *page,
					      GdkRectangle     *area);
211 212 213 214
static void gtk_notebook_draw_arrow          (GtkNotebook      *notebook,
					      guint             arrow);
static void gtk_notebook_set_shape           (GtkNotebook      *notebook);

215
/*** GtkNotebook Size Allocate Functions ***/
216
static void gtk_notebook_pages_allocate      (GtkNotebook      *notebook);
217 218 219
static void gtk_notebook_page_allocate       (GtkNotebook      *notebook,
					      GtkNotebookPage  *page,
					      GtkAllocation    *allocation);
220 221
static void gtk_notebook_calc_tabs           (GtkNotebook      *notebook,
			                      GList            *start,
222 223 224
					      GList           **end,
					      gint             *tab_space,
					      guint             direction);
225

226
/*** GtkNotebook Page Switch Methods ***/
227
static void gtk_notebook_real_switch_page    (GtkNotebook      *notebook,
Lars Hamann's avatar
Lars Hamann committed
228
					      GtkNotebookPage  *page,
229
					      guint             page_num);
230

231
/*** GtkNotebook Page Switch Functions ***/
232 233
static void gtk_notebook_switch_page         (GtkNotebook      *notebook,
					      GtkNotebookPage  *page,
234
					      gint              page_num);
235 236
static gint gtk_notebook_page_select         (GtkNotebook      *notebook,
					      gboolean          move_focus);
237 238
static void gtk_notebook_switch_focus_tab    (GtkNotebook      *notebook,
                                              GList            *new_child);
239 240
static void gtk_notebook_menu_switch_page    (GtkWidget        *widget,
					      GtkNotebookPage  *page);
241

242
/*** GtkNotebook Menu Functions ***/
243 244 245 246
static void gtk_notebook_menu_item_create    (GtkNotebook      *notebook,
					      GList            *list);
static void gtk_notebook_menu_label_unparent (GtkWidget        *widget,
					      gpointer          data);
247 248
static void gtk_notebook_menu_detacher       (GtkWidget        *widget,
					      GtkMenu          *menu);
249

Elliot Lee's avatar
Elliot Lee committed
250

251
static GtkContainerClass *parent_class = NULL;
252
static guint notebook_signals[LAST_SIGNAL] = { 0 };
Elliot Lee's avatar
Elliot Lee committed
253

254
GtkType
255
gtk_notebook_get_type (void)
Elliot Lee's avatar
Elliot Lee committed
256
{
257
  static GtkType notebook_type = 0;
Elliot Lee's avatar
Elliot Lee committed
258 259 260

  if (!notebook_type)
    {
261
      static const GtkTypeInfo notebook_info =
Elliot Lee's avatar
Elliot Lee committed
262 263 264 265 266 267
      {
	"GtkNotebook",
	sizeof (GtkNotebook),
	sizeof (GtkNotebookClass),
	(GtkClassInitFunc) gtk_notebook_class_init,
	(GtkObjectInitFunc) gtk_notebook_init,
268 269
	/* reserved_1 */ NULL,
        /* reserved_2 */ NULL,
270
        (GtkClassInitFunc) NULL,
Elliot Lee's avatar
Elliot Lee committed
271 272 273 274 275 276 277 278 279 280 281
      };

      notebook_type = gtk_type_unique (gtk_container_get_type (), &notebook_info);
    }

  return notebook_type;
}

static void
gtk_notebook_class_init (GtkNotebookClass *class)
{
Tim Janik's avatar
Tim Janik committed
282 283 284 285
  GObjectClass   *gobject_class = G_OBJECT_CLASS (class);
  GtkObjectClass *object_class = GTK_OBJECT_CLASS (class);
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
286 287
  GtkBindingSet *binding_set;
  
Tim Janik's avatar
Tim Janik committed
288
  parent_class = g_type_class_peek_parent (class);
289

290 291
  gobject_class->set_property = gtk_notebook_set_property;
  gobject_class->get_property = gtk_notebook_get_property;
292 293
  object_class->destroy = gtk_notebook_destroy;

Elliot Lee's avatar
Elliot Lee committed
294 295 296
  widget_class->map = gtk_notebook_map;
  widget_class->unmap = gtk_notebook_unmap;
  widget_class->realize = gtk_notebook_realize;
297
  widget_class->unrealize = gtk_notebook_unrealize;
Elliot Lee's avatar
Elliot Lee committed
298 299 300 301
  widget_class->size_request = gtk_notebook_size_request;
  widget_class->size_allocate = gtk_notebook_size_allocate;
  widget_class->expose_event = gtk_notebook_expose;
  widget_class->button_press_event = gtk_notebook_button_press;
302 303 304 305 306
  widget_class->button_release_event = gtk_notebook_button_release;
  widget_class->enter_notify_event = gtk_notebook_enter_notify;
  widget_class->leave_notify_event = gtk_notebook_leave_notify;
  widget_class->motion_notify_event = gtk_notebook_motion_notify;
  widget_class->focus_in_event = gtk_notebook_focus_in;
307 308
  widget_class->focus = gtk_notebook_focus;
  
Elliot Lee's avatar
Elliot Lee committed
309 310
  container_class->add = gtk_notebook_add;
  container_class->remove = gtk_notebook_remove;
311
  container_class->forall = gtk_notebook_forall;
Lars Hamann's avatar
Lars Hamann committed
312
  container_class->set_focus_child = gtk_notebook_set_focus_child;
Tim Janik's avatar
Tim Janik committed
313 314
  container_class->get_child_property = gtk_notebook_get_child_property;
  container_class->set_child_property = gtk_notebook_set_child_property;
315
  container_class->child_type = gtk_notebook_child_type;
316

317
  class->switch_page = gtk_notebook_real_switch_page;
318

319 320 321
  class->focus_tab = gtk_notebook_focus_tab;
  class->select_page = gtk_notebook_select_page;
  
322 323 324 325 326 327 328 329 330
  g_object_class_install_property (gobject_class,
				   PROP_PAGE,
				   g_param_spec_int ("page",
 						     _("Page"),
 						     _("The index of the current page"),
 						     0,
 						     G_MAXINT,
 						     0,
 						     G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
331 332 333
  g_object_class_install_property (gobject_class,
				   PROP_TAB_POS,
				   g_param_spec_enum ("tab_pos",
334 335 336 337 338
 						      _("Tab Position"),
 						      _("Which side of the notebook holds the tabs"),
 						      GTK_TYPE_POSITION_TYPE,
 						      GTK_POS_TOP,
 						      G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
339 340 341
  g_object_class_install_property (gobject_class,
				   PROP_TAB_BORDER,
				   g_param_spec_uint ("tab_border",
342 343 344 345 346 347
 						      _("Tab Border"),
 						      _("Width of the border around the tab labels"),
 						      0,
 						      G_MAXUINT,
 						      2,
 						      G_PARAM_WRITABLE));
Tim Janik's avatar
Tim Janik committed
348 349 350
  g_object_class_install_property (gobject_class,
				   PROP_TAB_HBORDER,
				   g_param_spec_uint ("tab_hborder",
351 352 353 354 355 356
 						      _("Horizontal Tab Border"),
 						      _("Width of the horizontal border of tab labels"),
 						      0,
 						      G_MAXUINT,
 						      2,
 						      G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
357 358 359
  g_object_class_install_property (gobject_class,
				   PROP_TAB_VBORDER,
				   g_param_spec_uint ("tab_vborder",
360 361 362 363 364 365
 						      _("Vertical Tab Border"),
 						      _("Width of the vertical border of tab labels"),
 						      0,
 						      G_MAXUINT,
 						      2,
 						      G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
366 367 368
  g_object_class_install_property (gobject_class,
				   PROP_SHOW_TABS,
				   g_param_spec_boolean ("show_tabs",
369 370 371 372
 							 _("Show Tabs"),
 							 _("Whether tabs should be shown or not"),
 							 TRUE,
 							 G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
373 374 375
  g_object_class_install_property (gobject_class,
				   PROP_SHOW_BORDER,
				   g_param_spec_boolean ("show_border",
376 377 378 379
 							 _("Show Border"),
 							 _("Whether the border should be shown or not"),
 							 TRUE,
 							 G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
380 381 382
  g_object_class_install_property (gobject_class,
				   PROP_SCROLLABLE,
				   g_param_spec_boolean ("scrollable",
383 384 385 386
 							 _("Scrollable"),
 							 _("If TRUE, scroll arrows are added if there are to many tabs to fit"),
 							 FALSE,
 							 G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
387 388 389
  g_object_class_install_property (gobject_class,
				   PROP_ENABLE_POPUP,
				   g_param_spec_boolean ("enable_popup",
390 391 392 393
 							 _("Enable Popup"),
 							 _("If TRUE, pressing the right mouse button on the notebook pops up a menu that you can use to go to a page"),
 							 FALSE,
 							 G_PARAM_READWRITE));
Tim Janik's avatar
Tim Janik committed
394 395 396
  g_object_class_install_property (gobject_class,
				   PROP_HOMOGENEOUS,
				   g_param_spec_boolean ("homogeneous",
397 398 399
 							 _("Homogeneous"),
 							 _("Whether tabs should have homogeneous sizes"),
 							 FALSE,
Tim Janik's avatar
Tim Janik committed
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
							 G_PARAM_READWRITE));

  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_TAB_LABEL,
					      g_param_spec_string ("tab_label", NULL, NULL,
								   NULL,
								   G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_MENU_LABEL,
					      g_param_spec_string ("menu_label", NULL, NULL,
								   NULL,
								   G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_POSITION,
					      g_param_spec_int ("position", NULL, NULL,
								-1, G_MAXINT, 0,
								G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_TAB_EXPAND,
					      g_param_spec_boolean ("tab_expand", NULL, NULL,
								    TRUE,
								    G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_TAB_FILL,
					      g_param_spec_boolean ("tab_fill", NULL, NULL,
								    TRUE,
								    G_PARAM_READWRITE));
  gtk_container_class_install_child_property (container_class,
					      CHILD_PROP_TAB_PACK,
					      g_param_spec_boolean ("tab_pack", NULL, NULL,
								    TRUE,
								    G_PARAM_READWRITE));
  
433 434 435 436 437 438 439 440 441
  notebook_signals[SWITCH_PAGE] =
    gtk_signal_new ("switch_page",
		    GTK_RUN_LAST,
		    GTK_CLASS_TYPE (object_class),
		    GTK_SIGNAL_OFFSET (GtkNotebookClass, switch_page),
		    gtk_marshal_VOID__POINTER_UINT,
		    GTK_TYPE_NONE, 2,
		    GTK_TYPE_POINTER,
		    GTK_TYPE_UINT);
442
  notebook_signals[FOCUS_TAB] = 
443 444 445 446 447 448 449 450
    g_signal_new ("focus_tab",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkNotebookClass, focus_tab),
                  NULL, NULL,
                  gtk_marshal_VOID__ENUM,
                  G_TYPE_NONE, 1,
                  GTK_TYPE_NOTEBOOK_TAB);
451
  notebook_signals[SELECT_PAGE] = 
452 453 454 455 456 457 458 459 460
    g_signal_new ("select_page",
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                  G_STRUCT_OFFSET (GtkNotebookClass, select_page),
                  NULL, NULL,
                  gtk_marshal_VOID__BOOLEAN,
                  G_TYPE_NONE, 1,
                  G_TYPE_BOOLEAN);
  
461 462 463 464 465 466 467 468 469 470 471 472 473
  binding_set = gtk_binding_set_by_class (object_class);
  gtk_binding_entry_add_signal (binding_set,
                                GDK_Return, 0,
                                "select_page", 1, 
                                G_TYPE_BOOLEAN, TRUE);
  gtk_binding_entry_add_signal (binding_set,
                                GDK_KP_Enter, 0,
                                "select_page", 1, 
                                G_TYPE_BOOLEAN, TRUE);
  gtk_binding_entry_add_signal (binding_set,
                                GDK_space, 0,
                                "select_page", 1, 
                                G_TYPE_BOOLEAN, FALSE);
474 475 476 477 478
  gtk_binding_entry_add_signal (binding_set,
                                GDK_KP_Space, 0,
                                "select_page", 1, 
                                G_TYPE_BOOLEAN, FALSE);
  
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
  gtk_binding_entry_add_signal (binding_set,
                                GDK_Home, 0,
                                "focus_tab", 1, 
                                GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
  gtk_binding_entry_add_signal (binding_set,
                                GDK_KP_Home, 0,
                                "focus_tab", 1, 
                                GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_FIRST);
  gtk_binding_entry_add_signal (binding_set,
                                GDK_End, 0,
                                "focus_tab", 1, 
                                GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
  gtk_binding_entry_add_signal (binding_set,
                                GDK_KP_End, 0,
                                "focus_tab", 1, 
                                GTK_TYPE_NOTEBOOK_TAB, GTK_NOTEBOOK_TAB_LAST);
Elliot Lee's avatar
Elliot Lee committed
495 496
}

497
static void
498 499
gtk_notebook_init (GtkNotebook *notebook)
{
500
  GTK_WIDGET_SET_FLAGS (notebook, GTK_CAN_FOCUS | GTK_RECEIVES_DEFAULT);
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
  GTK_WIDGET_UNSET_FLAGS (notebook, GTK_NO_WINDOW);

  notebook->cur_page = NULL;
  notebook->children = NULL;
  notebook->first_tab = NULL;
  notebook->focus_tab = NULL;
  notebook->panel = NULL;
  notebook->menu = NULL;

  notebook->tab_hborder = 2;
  notebook->tab_vborder = 2;

  notebook->show_tabs = TRUE;
  notebook->show_border = TRUE;
  notebook->tab_pos = GTK_POS_TOP;
  notebook->scrollable = FALSE;
  notebook->in_child = 0;
  notebook->click_child = 0;
  notebook->button = 0;
  notebook->need_timer = 0;
  notebook->child_has_focus = FALSE;
522
  notebook->have_visible_child = FALSE;
523 524
}

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
static void
gtk_notebook_select_page (GtkNotebook *notebook,
                          gboolean     move_focus)
{
  gtk_notebook_page_select (notebook, move_focus);
}

static void
gtk_notebook_focus_tab (GtkNotebook       *notebook,
                        GtkNotebookTab     type)
{
  GList *list;
  
  switch (type)
    {
    case GTK_NOTEBOOK_TAB_FIRST:
      list = gtk_notebook_search_page (notebook, NULL, STEP_NEXT, TRUE);
      if (list)
	gtk_notebook_switch_focus_tab (notebook, list);
      break;
    case GTK_NOTEBOOK_TAB_LAST:
      list = gtk_notebook_search_page (notebook, NULL, STEP_PREV, TRUE);
      if (list)
	gtk_notebook_switch_focus_tab (notebook, list);
      break;
    }
}

553 554 555 556 557 558 559
/**
 * gtk_notebook_new:
 * 
 * Creates a new #GtkNotebook widget with no pages.

 * Return value: the newly created #GtkNotebook
 **/
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
GtkWidget*
gtk_notebook_new (void)
{
  return GTK_WIDGET (gtk_type_new (gtk_notebook_get_type ()));
}

/* Private GtkObject Methods :
 * 
 * gtk_notebook_destroy
 * gtk_notebook_set_arg
 * gtk_notebook_get_arg
 */
static void
gtk_notebook_destroy (GtkObject *object)
{
  GtkNotebook *notebook;
  
  g_return_if_fail (GTK_IS_NOTEBOOK (object));

  notebook = GTK_NOTEBOOK (object);

  if (notebook->menu)
    gtk_notebook_popup_disable (notebook);

  GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
588 589 590 591
gtk_notebook_set_property (GObject         *object,
			   guint            prop_id,
			   const GValue    *value,
			   GParamSpec      *pspec)
592 593 594 595 596
{
  GtkNotebook *notebook;

  notebook = GTK_NOTEBOOK (object);

597
  switch (prop_id)
598
    {
599 600
    case PROP_SHOW_TABS:
      gtk_notebook_set_show_tabs (notebook, g_value_get_boolean (value));
601
      break;
602 603
    case PROP_SHOW_BORDER:
      gtk_notebook_set_show_border (notebook, g_value_get_boolean (value));
604
      break;
605 606
    case PROP_SCROLLABLE:
      gtk_notebook_set_scrollable (notebook, g_value_get_boolean (value));
607
      break;
608 609
    case PROP_ENABLE_POPUP:
      if (g_value_get_boolean (value))
610 611 612 613
	gtk_notebook_popup_enable (notebook);
      else
	gtk_notebook_popup_disable (notebook);
      break;
614 615
    case PROP_HOMOGENEOUS:
      gtk_notebook_set_homogeneous_tabs (notebook, g_value_get_boolean (value));
616
      break;  
617
    case PROP_PAGE:
618
      gtk_notebook_set_current_page (notebook, g_value_get_int (value));
619
      break;
620 621
    case PROP_TAB_POS:
      gtk_notebook_set_tab_pos (notebook, g_value_get_enum (value));
622
      break;
623 624
    case PROP_TAB_BORDER:
      gtk_notebook_set_tab_border (notebook, g_value_get_uint (value));
625
      break;
626 627
    case PROP_TAB_HBORDER:
      gtk_notebook_set_tab_hborder (notebook, g_value_get_uint (value));
628
      break;
629 630
    case PROP_TAB_VBORDER:
      gtk_notebook_set_tab_vborder (notebook, g_value_get_uint (value));
631
      break;
632 633 634 635 636 637
    default:
      break;
    }
}

static void
638 639 640 641
gtk_notebook_get_property (GObject         *object,
			   guint            prop_id,
			   GValue          *value,
			   GParamSpec      *pspec)
642 643 644 645 646
{
  GtkNotebook *notebook;

  notebook = GTK_NOTEBOOK (object);

647
  switch (prop_id)
648
    {
649 650
    case PROP_SHOW_TABS:
      g_value_set_boolean (value, notebook->show_tabs);
651
      break;
652 653
    case PROP_SHOW_BORDER:
      g_value_set_boolean (value, notebook->show_border);
654
      break;
655 656
    case PROP_SCROLLABLE:
      g_value_set_boolean (value, notebook->scrollable);
657
      break;
658 659
    case PROP_ENABLE_POPUP:
      g_value_set_boolean (value, notebook->menu != NULL);
660
      break;
661 662
    case PROP_HOMOGENEOUS:
      g_value_set_boolean (value, notebook->homogeneous);
663
      break;
664 665
    case PROP_PAGE:
      g_value_set_int (value, gtk_notebook_get_current_page (notebook));
666
      break;
667 668
    case PROP_TAB_POS:
      g_value_set_enum (value, notebook->tab_pos);
669
      break;
670 671
    case PROP_TAB_HBORDER:
      g_value_set_uint (value, notebook->tab_hborder);
672
      break;
673 674
    case PROP_TAB_VBORDER:
      g_value_set_uint (value, notebook->tab_vborder);
675
      break;
676
    default:
677
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
678 679 680 681
      break;
    }
}

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
/* Private GtkWidget Methods :
 * 
 * gtk_notebook_map
 * gtk_notebook_unmap
 * gtk_notebook_realize
 * gtk_notebook_size_request
 * gtk_notebook_size_allocate
 * gtk_notebook_expose
 * gtk_notebook_button_press
 * gtk_notebook_button_release
 * gtk_notebook_enter_notify
 * gtk_notebook_leave_notify
 * gtk_notebook_motion_notify
 * gtk_notebook_focus_in
 * gtk_notebook_focus_out
 * gtk_notebook_draw_focus
 * gtk_notebook_style_set
 */
700
static void
701
gtk_notebook_map (GtkWidget *widget)
702 703
{
  GtkNotebook *notebook;
704 705
  GtkNotebookPage *page;
  GList *children;
706

707
  g_return_if_fail (GTK_IS_NOTEBOOK (widget));
708

709
  GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
710

711
  notebook = GTK_NOTEBOOK (widget);
712

713 714 715 716
  if (notebook->cur_page && 
      GTK_WIDGET_VISIBLE (notebook->cur_page->child) &&
      !GTK_WIDGET_MAPPED (notebook->cur_page->child))
    gtk_widget_map (notebook->cur_page->child);
717

718
  if (notebook->scrollable)
719
    gtk_notebook_pages_allocate (notebook);
720 721 722
  else
    {
      children = notebook->children;
723

724
      while (children)
725
	{
726 727
	  page = children->data;
	  children = children->next;
728

Owen Taylor's avatar
Owen Taylor committed
729 730
	  if (page->tab_label &&
	      GTK_WIDGET_VISIBLE (page->tab_label) &&
731 732
	      !GTK_WIDGET_MAPPED (page->tab_label))
	    gtk_widget_map (page->tab_label);
733
	}
734
    }
735 736

  gdk_window_show (widget->window);
737
}
738

739 740 741 742
static void
gtk_notebook_unmap (GtkWidget *widget)
{
  g_return_if_fail (GTK_IS_NOTEBOOK (widget));
743

744 745 746 747
  GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
  gdk_window_hide (widget->window);
  if (GTK_NOTEBOOK (widget)->panel)
    gdk_window_hide (GTK_NOTEBOOK (widget)->panel);
748 749 750
}

static void
751
gtk_notebook_realize (GtkWidget *widget)
752 753
{
  GtkNotebook *notebook;
754 755
  GdkWindowAttr attributes;
  gint attributes_mask;
756

757
  g_return_if_fail (GTK_IS_NOTEBOOK (widget));
758

759 760
  notebook = GTK_NOTEBOOK (widget);
  GTK_WIDGET_SET_FLAGS (notebook, GTK_REALIZED);
761

762 763 764 765 766 767 768 769 770 771 772
  attributes.window_type = GDK_WINDOW_CHILD;
  attributes.x = widget->allocation.x;
  attributes.y = widget->allocation.y;
  attributes.width = widget->allocation.width;
  attributes.height = widget->allocation.height;
  attributes.wclass = GDK_INPUT_OUTPUT;
  attributes.visual = gtk_widget_get_visual (widget);
  attributes.colormap = gtk_widget_get_colormap (widget);
  attributes.event_mask = gtk_widget_get_events (widget);
  attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
			    GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK);
773

774
  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
775

776 777
  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
  gdk_window_set_user_data (widget->window, notebook);
778

779 780
  widget->style = gtk_style_attach (widget->style, widget->window);
  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
Elliot Lee's avatar
Elliot Lee committed
781

782 783 784
  gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
  if (notebook->scrollable)
    gtk_notebook_panel_realize (notebook);
Elliot Lee's avatar
Elliot Lee committed
785 786
}

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
static void
gtk_notebook_unrealize (GtkWidget *widget)
{
  GtkNotebook *notebook;

  g_return_if_fail (GTK_IS_NOTEBOOK (widget));

  notebook = GTK_NOTEBOOK (widget);

  if (notebook->panel)
    {
      gdk_window_set_user_data (notebook->panel, NULL);
      gdk_window_destroy (notebook->panel);
      notebook->panel = NULL;
    }

  if (GTK_WIDGET_CLASS (parent_class)->unrealize)
    (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}

807
static void
808 809
gtk_notebook_size_request (GtkWidget      *widget,
			   GtkRequisition *requisition)
810 811
{
  GtkNotebook *notebook;
812 813
  GtkNotebookPage *page;
  GList *children;
814
  GtkRequisition child_requisition;
815 816
  gboolean switch_page = FALSE;
  gint vis_pages;
817

818 819
  g_return_if_fail (GTK_IS_NOTEBOOK (widget));
  g_return_if_fail (requisition != NULL);
820

821 822 823
  notebook = GTK_NOTEBOOK (widget);
  widget->requisition.width = 0;
  widget->requisition.height = 0;
824

825 826 827 828
  for (children = notebook->children, vis_pages = 0; children;
       children = children->next)
    {
      page = children->data;
829

830 831 832
      if (GTK_WIDGET_VISIBLE (page->child))
	{
	  vis_pages++;
833
	  gtk_widget_size_request (page->child, &child_requisition);
834 835
	  
	  widget->requisition.width = MAX (widget->requisition.width,
836
					   child_requisition.width);
837
	  widget->requisition.height = MAX (widget->requisition.height,
838
					    child_requisition.height);
Elliot Lee's avatar
Elliot Lee committed
839

840 841 842 843 844 845 846 847 848 849 850 851 852
	  if (notebook->menu && page->menu_label->parent &&
	      !GTK_WIDGET_VISIBLE (page->menu_label->parent))
	    gtk_widget_show (page->menu_label->parent);
	}
      else
	{
	  if (page == notebook->cur_page)
	    switch_page = TRUE;
	  if (notebook->menu && page->menu_label->parent &&
	      GTK_WIDGET_VISIBLE (page->menu_label->parent))
	    gtk_widget_hide (page->menu_label->parent);
	}
    }
853

854 855
  if (notebook->show_border || notebook->show_tabs)
    {
856 857
      widget->requisition.width += widget->style->xthickness * 2;
      widget->requisition.height += widget->style->ythickness * 2;
858

859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
      if (notebook->show_tabs)
	{
	  gint tab_width = 0;
	  gint tab_height = 0;
	  gint tab_max = 0;
	  gint padding;
	  
	  for (children = notebook->children; children;
	       children = children->next)
	    {
	      page = children->data;
	      
	      if (GTK_WIDGET_VISIBLE (page->child))
		{
		  if (!GTK_WIDGET_VISIBLE (page->tab_label))
		    gtk_widget_show (page->tab_label);
Elliot Lee's avatar
Elliot Lee committed
875

876 877
		  gtk_widget_size_request (page->tab_label,
					   &child_requisition);
Elliot Lee's avatar
Elliot Lee committed
878

879
		  page->requisition.width = 
880
		    child_requisition.width +
881
		    2 * widget->style->xthickness;
882
		  page->requisition.height = 
883
		    child_requisition.height +
884
		    2 * widget->style->ythickness;
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
		  
		  switch (notebook->tab_pos)
		    {
		    case GTK_POS_TOP:
		    case GTK_POS_BOTTOM:
		      page->requisition.height += 2 * (notebook->tab_vborder +
						       FOCUS_WIDTH);
		      tab_height = MAX (tab_height, page->requisition.height);
		      tab_max = MAX (tab_max, page->requisition.width);
		      break;
		    case GTK_POS_LEFT:
		    case GTK_POS_RIGHT:
		      page->requisition.width += 2 * (notebook->tab_hborder +
						      FOCUS_WIDTH);
		      tab_width = MAX (tab_width, page->requisition.width);
		      tab_max = MAX (tab_max, page->requisition.height);
		      break;
		    }
		}
	      else if (GTK_WIDGET_VISIBLE (page->tab_label))
		gtk_widget_hide (page->tab_label);
	    }
907

908
	  children = notebook->children;
909

910 911 912 913 914 915 916 917
	  if (vis_pages)
	    {
	      switch (notebook->tab_pos)
		{
		case GTK_POS_TOP:
		case GTK_POS_BOTTOM:
		  if (tab_height == 0)
		    break;
Elliot Lee's avatar
Elliot Lee committed
918

919 920 921
		  if (notebook->scrollable && vis_pages > 1 && 
		      widget->requisition.width < tab_width)
		    tab_height = MAX (tab_height, ARROW_SIZE);
922

923 924 925 926 927 928 929 930 931 932
		  padding = 2 * (TAB_CURVATURE + FOCUS_WIDTH +
				 notebook->tab_hborder) - TAB_OVERLAP;
		  tab_max += padding;
		  while (children)
		    {
		      page = children->data;
		      children = children->next;
		  
		      if (!GTK_WIDGET_VISIBLE (page->child))
			continue;
933

934 935 936 937
		      if (notebook->homogeneous)
			page->requisition.width = tab_max;
		      else
			page->requisition.width += padding;
Elliot Lee's avatar
Elliot Lee committed
938

939 940 941
		      tab_width += page->requisition.width;
		      page->requisition.height = tab_height;
		    }
Elliot Lee's avatar
Elliot Lee committed
942

943 944 945
		  if (notebook->scrollable && vis_pages > 1 &&
		      widget->requisition.width < tab_width)
		    tab_width = tab_max + 2 * (ARROW_SIZE + ARROW_SPACING);
Elliot Lee's avatar
Elliot Lee committed
946

947 948 949 950 951 952 953
                  if (notebook->homogeneous && !notebook->scrollable)
                    widget->requisition.width = MAX (widget->requisition.width,
                                                     vis_pages * tab_max +
                                                     TAB_OVERLAP);
                  else
                    widget->requisition.width = MAX (widget->requisition.width,
                                                     tab_width + TAB_OVERLAP);
Elliot Lee's avatar
Elliot Lee committed
954

955 956 957 958 959 960
		  widget->requisition.height += tab_height;
		  break;
		case GTK_POS_LEFT:
		case GTK_POS_RIGHT:
		  if (tab_width == 0)
		    break;
961

962 963 964
		  if (notebook->scrollable && vis_pages > 1 && 
		      widget->requisition.height < tab_height)
		    tab_width = MAX (tab_width, ARROW_SPACING +2 * ARROW_SIZE);
965

966 967 968
		  padding = 2 * (TAB_CURVATURE + FOCUS_WIDTH +
				 notebook->tab_vborder) - TAB_OVERLAP;
		  tab_max += padding;
969

970 971 972 973
		  while (children)
		    {
		      page = children->data;
		      children = children->next;
974

975 976
		      if (!GTK_WIDGET_VISIBLE (page->child))
			continue;
977

978
		      page->requisition.width   = tab_width;
979

980 981 982 983
		      if (notebook->homogeneous)
			page->requisition.height = tab_max;
		      else
			page->requisition.height += padding;
Elliot Lee's avatar
Elliot Lee committed
984

985 986
		      tab_height += page->requisition.height;
		    }
Elliot Lee's avatar
Elliot Lee committed
987

988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
		  if (notebook->scrollable && vis_pages > 1 && 
		      widget->requisition.height < tab_height)
		    tab_height = tab_max + ARROW_SIZE + ARROW_SPACING;

		  widget->requisition.width += tab_width;

                  if (notebook->homogeneous && !notebook->scrollable)
                    widget->requisition.height =
		      MAX (widget->requisition.height,
			   vis_pages * tab_max + TAB_OVERLAP);
                  else
                    widget->requisition.height =
		      MAX (widget->requisition.height,
			   tab_height + TAB_OVERLAP);

		  if (!notebook->homogeneous || notebook->scrollable)
		    vis_pages = 1;
		  widget->requisition.height = MAX (widget->requisition.height,
						    vis_pages * tab_max +
						    TAB_OVERLAP);
		  break;
		}
	    }
1011
	}
Owen Taylor's avatar
Owen Taylor committed
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
      else
	{
	  for (children = notebook->children; children;
	       children = children->next)
	    {
	      page = children->data;
	      
	      if (page->tab_label && GTK_WIDGET_VISIBLE (page->tab_label))
		gtk_widget_hide (page->tab_label);
	    }
	}
Elliot Lee's avatar
Elliot Lee committed
1023 1024
    }

1025 1026
  widget->requisition.width += GTK_CONTAINER (widget)->border_width * 2;
  widget->requisition.height += GTK_CONTAINER (widget)->border_width * 2;
Elliot Lee's avatar
Elliot Lee committed
1027

1028
  if (switch_page)
1029
    {
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047
      if (vis_pages)
	{
	  for (children = notebook->children; children;
	       children = children->next)
	    {
	      page = children->data;
	      if (GTK_WIDGET_VISIBLE (page->child))
		{
		  gtk_notebook_switch_page (notebook, page, -1);
		  break;
		}
	    }
	}
      else if (GTK_WIDGET_VISIBLE (widget))
	{
	  widget->requisition.width = GTK_CONTAINER (widget)->border_width * 2;
	  widget->requisition.height= GTK_CONTAINER (widget)->border_width * 2;
	}
1048
    }
1049
  if (vis_pages && !notebook->cur_page)
1050
    {