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