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