1/*
2 * GTK - The GIMP Toolkit
3 * Copyright (C) 1999 Red Hat, Inc.
4 * Copyright (C) 2002 Anders Carlsson <andersca@gnu.org>
5 * Copyright (C) 2003 Matthias Clasen <mclasen@redhat.com>
6 * Copyright (C) 2005 Carlos Garnacho Parro <carlosg@gnome.org>
7 *
8 * All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/**
25 * GtkAssistant:
26 *
27 * `GtkAssistant` is used to represent a complex as a series of steps.
28 *
29 * ![An example GtkAssistant](assistant.png)
30 *
31 * Each step consists of one or more pages. `GtkAssistant` guides the user
32 * through the pages, and controls the page flow to collect the data needed
33 * for the operation.
34 *
35 * `GtkAssistant` handles which buttons to show and to make sensitive based
36 * on page sequence knowledge and the [enum@Gtk.AssistantPageType] of each
37 * page in addition to state information like the *completed* and *committed*
38 * page statuses.
39 *
40 * If you have a case that doesn’t quite fit in `GtkAssistant`s way of
41 * handling buttons, you can use the %GTK_ASSISTANT_PAGE_CUSTOM page
42 * type and handle buttons yourself.
43 *
44 * `GtkAssistant` maintains a `GtkAssistantPage` object for each added
45 * child, which holds additional per-child properties. You
46 * obtain the `GtkAssistantPage` for a child with [method@Gtk.Assistant.get_page].
47 *
48 * # GtkAssistant as GtkBuildable
49 *
50 * The `GtkAssistant` implementation of the `GtkBuildable` interface
51 * exposes the @action_area as internal children with the name
52 * “action_area”.
53 *
54 * To add pages to an assistant in `GtkBuilder`, simply add it as a
55 * child to the `GtkAssistant` object. If you need to set per-object
56 * properties, create a `GtkAssistantPage` object explicitly, and
57 * set the child widget as a property on it.
58 *
59 * # CSS nodes
60 *
61 * `GtkAssistant` has a single CSS node with the name window and style
62 * class .assistant.
63 */
64
65/**
66 * GtkAssistantPage:
67 *
68 * `GtkAssistantPage` is an auxiliary object used by `GtkAssistant.
69 */
70
71#include "config.h"
72
73#include "gtkassistant.h"
74
75#include "gtkbox.h"
76#include "gtkbuildable.h"
77#include "gtkbutton.h"
78#include "gtkframe.h"
79#include "gtkheaderbar.h"
80#include "gtkintl.h"
81#include "gtkimage.h"
82#include "gtklabel.h"
83#include "gtklistlistmodelprivate.h"
84#include "gtkmaplistmodel.h"
85#include "gtkprivate.h"
86#include "gtksettings.h"
87#include "gtksizegroup.h"
88#include "gtksizerequest.h"
89#include "gtkstack.h"
90#include "gtktypebuiltins.h"
91
92typedef struct _GtkAssistantPageClass GtkAssistantPageClass;
93
94struct _GtkAssistantPage
95{
96 GObject instance;
97 GtkAssistantPageType type;
98 guint complete : 1;
99 guint complete_set : 1;
100
101 char *title;
102
103 GtkWidget *page;
104 GtkWidget *regular_title;
105 GtkWidget *current_title;
106};
107
108struct _GtkAssistantPageClass
109{
110 GObjectClass parent_class;
111};
112
113typedef struct _GtkAssistantClass GtkAssistantClass;
114
115struct _GtkAssistant
116{
117 GtkWindow parent;
118
119 GtkWidget *cancel;
120 GtkWidget *forward;
121 GtkWidget *back;
122 GtkWidget *apply;
123 GtkWidget *close;
124 GtkWidget *last;
125
126 GtkWidget *sidebar;
127 GtkWidget *content;
128 GtkWidget *action_area;
129 GtkWidget *headerbar;
130 int use_header_bar;
131 gboolean constructed;
132
133 GList *pages;
134 GSList *visited_pages;
135 GtkAssistantPage *current_page;
136
137 GtkSizeGroup *button_size_group;
138 GtkSizeGroup *title_size_group;
139
140 GtkAssistantPageFunc forward_function;
141 gpointer forward_function_data;
142 GDestroyNotify forward_data_destroy;
143
144 GListModel *model;
145
146 int extra_buttons;
147
148 guint committed : 1;
149};
150
151struct _GtkAssistantClass
152{
153 GtkWindowClass parent_class;
154
155 void (* prepare) (GtkAssistant *assistant, GtkWidget *page);
156 void (* apply) (GtkAssistant *assistant);
157 void (* close) (GtkAssistant *assistant);
158 void (* cancel) (GtkAssistant *assistant);
159};
160
161static void gtk_assistant_dispose (GObject *object);
162static void gtk_assistant_map (GtkWidget *widget);
163static void gtk_assistant_unmap (GtkWidget *widget);
164static gboolean gtk_assistant_close_request (GtkWindow *window);
165
166static void gtk_assistant_page_set_property (GObject *object,
167 guint property_id,
168 const GValue *value,
169 GParamSpec *pspec);
170static void gtk_assistant_page_get_property (GObject *object,
171 guint property_id,
172 GValue *value,
173 GParamSpec *pspec);
174
175static void gtk_assistant_buildable_interface_init (GtkBuildableIface *iface);
176static void gtk_assistant_buildable_add_child (GtkBuildable *buildable,
177 GtkBuilder *builder,
178 GObject *child,
179 const char *type);
180static gboolean gtk_assistant_buildable_custom_tag_start (GtkBuildable *buildable,
181 GtkBuilder *builder,
182 GObject *child,
183 const char *tagname,
184 GtkBuildableParser *parser,
185 gpointer *data);
186static void gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
187 GtkBuilder *builder,
188 GObject *child,
189 const char *tagname,
190 gpointer user_data);
191
192static GList* find_page (GtkAssistant *assistant,
193 GtkWidget *page);
194static void on_assistant_close (GtkWidget *widget,
195 GtkAssistant *assistant);
196static void on_assistant_apply (GtkWidget *widget,
197 GtkAssistant *assistant);
198static void on_assistant_forward (GtkWidget *widget,
199 GtkAssistant *assistant);
200static void on_assistant_back (GtkWidget *widget,
201 GtkAssistant *assistant);
202static void on_assistant_cancel (GtkWidget *widget,
203 GtkAssistant *assistant);
204static void on_assistant_last (GtkWidget *widget,
205 GtkAssistant *assistant);
206
207static int gtk_assistant_add_page (GtkAssistant *assistant,
208 GtkAssistantPage *page_info,
209 int position);
210
211enum
212{
213 CHILD_PROP_0,
214 CHILD_PROP_CHILD,
215 CHILD_PROP_PAGE_TYPE,
216 CHILD_PROP_PAGE_TITLE,
217 CHILD_PROP_PAGE_COMPLETE,
218 CHILD_PROP_HAS_PADDING
219};
220
221G_DEFINE_TYPE (GtkAssistantPage, gtk_assistant_page, G_TYPE_OBJECT)
222
223static void
224gtk_assistant_page_init (GtkAssistantPage *page)
225{
226 page->type = GTK_ASSISTANT_PAGE_CONTENT;
227}
228
229static void
230gtk_assistant_page_finalize (GObject *object)
231{
232 GtkAssistantPage *page = GTK_ASSISTANT_PAGE (object);
233
234 g_clear_object (&page->page);
235 g_free (mem: page->title);
236
237 G_OBJECT_CLASS (gtk_assistant_page_parent_class)->finalize (object);
238}
239
240static void
241gtk_assistant_page_class_init (GtkAssistantPageClass *class)
242{
243 GObjectClass *object_class = G_OBJECT_CLASS (class);
244
245 object_class->finalize = gtk_assistant_page_finalize;
246 object_class->get_property = gtk_assistant_page_get_property;
247 object_class->set_property = gtk_assistant_page_set_property;
248
249 /**
250 * GtkAssistantPage:page-type:
251 *
252 * The type of the assistant page.
253 */
254 g_object_class_install_property (oclass: object_class,
255 property_id: CHILD_PROP_PAGE_TYPE,
256 pspec: g_param_spec_enum (name: "page-type",
257 P_("Page type"),
258 P_("The type of the assistant page"),
259 enum_type: GTK_TYPE_ASSISTANT_PAGE_TYPE,
260 default_value: GTK_ASSISTANT_PAGE_CONTENT,
261 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
262
263 /**
264 * GtkAssistantPage:title:
265 *
266 * The title of the page.
267 */
268 g_object_class_install_property (oclass: object_class,
269 property_id: CHILD_PROP_PAGE_TITLE,
270 pspec: g_param_spec_string (name: "title",
271 P_("Page title"),
272 P_("The title of the assistant page"),
273 NULL,
274 GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
275
276 /**
277 * GtkAssistantPage:complete:
278 *
279 * Whether all required fields are filled in.
280 *
281 * GTK uses this information to control the sensitivity
282 * of the navigation buttons.
283 */
284 g_object_class_install_property (oclass: object_class,
285 property_id: CHILD_PROP_PAGE_COMPLETE,
286 pspec: g_param_spec_boolean (name: "complete",
287 P_("Page complete"),
288 P_("Whether all required fields on the page have been filled out"),
289 FALSE,
290 flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY));
291
292 /**
293 * GtkAssistantPage:child:
294 *
295 * The child widget.
296 */
297 g_object_class_install_property (oclass: object_class,
298 property_id: CHILD_PROP_CHILD,
299 pspec: g_param_spec_object (name: "child",
300 P_("Child widget"),
301 P_("The content the assistant page"),
302 GTK_TYPE_WIDGET,
303 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
304}
305
306enum
307{
308 CANCEL,
309 PREPARE,
310 APPLY,
311 CLOSE,
312 ESCAPE,
313 LAST_SIGNAL
314};
315
316enum {
317 PROP_0,
318 PROP_USE_HEADER_BAR,
319 PROP_PAGES
320};
321
322static guint signals [LAST_SIGNAL] = { 0 };
323
324G_DEFINE_TYPE_WITH_CODE (GtkAssistant, gtk_assistant, GTK_TYPE_WINDOW,
325 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
326 gtk_assistant_buildable_interface_init))
327
328static void
329set_use_header_bar (GtkAssistant *assistant,
330 int use_header_bar)
331{
332 if (use_header_bar == -1)
333 return;
334
335 assistant->use_header_bar = use_header_bar;
336}
337
338static void
339gtk_assistant_set_property (GObject *object,
340 guint prop_id,
341 const GValue *value,
342 GParamSpec *pspec)
343{
344 GtkAssistant *assistant = GTK_ASSISTANT (object);
345
346 switch (prop_id)
347 {
348 case PROP_USE_HEADER_BAR:
349 set_use_header_bar (assistant, use_header_bar: g_value_get_int (value));
350 break;
351
352 default:
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
354 break;
355 }
356}
357
358static void
359gtk_assistant_get_property (GObject *object,
360 guint prop_id,
361 GValue *value,
362 GParamSpec *pspec)
363{
364 GtkAssistant *assistant = GTK_ASSISTANT (object);
365
366 switch (prop_id)
367 {
368 case PROP_USE_HEADER_BAR:
369 g_value_set_int (value, v_int: assistant->use_header_bar);
370 break;
371
372 case PROP_PAGES:
373 g_value_set_object (value, v_object: gtk_assistant_get_pages (assistant));
374 break;
375
376 default:
377 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
378 break;
379 }
380}
381
382static void
383apply_use_header_bar (GtkAssistant *assistant)
384{
385 gtk_widget_set_visible (widget: assistant->action_area, visible: !assistant->use_header_bar);
386 gtk_widget_set_visible (widget: assistant->headerbar, visible: assistant->use_header_bar);
387 if (!assistant->use_header_bar)
388 gtk_window_set_titlebar (GTK_WINDOW (assistant), NULL);
389}
390
391static void
392add_to_header_bar (GtkAssistant *assistant,
393 GtkWidget *child)
394{
395 gtk_widget_set_valign (widget: child, align: GTK_ALIGN_CENTER);
396
397 if (child == assistant->back || child == assistant->cancel)
398 gtk_header_bar_pack_start (GTK_HEADER_BAR (assistant->headerbar), child);
399 else
400 gtk_header_bar_pack_end (GTK_HEADER_BAR (assistant->headerbar), child);
401}
402
403static void
404add_action_widgets (GtkAssistant *assistant)
405{
406 GList *children, *l;
407 GtkWidget *child;
408
409 if (assistant->use_header_bar)
410 {
411 children = NULL;
412 for (child = gtk_widget_get_last_child (widget: assistant->action_area);
413 child != NULL;
414 child = gtk_widget_get_prev_sibling (widget: child))
415 children = g_list_prepend (list: children, data: child);
416 for (l = children; l != NULL; l = l->next)
417 {
418 gboolean has_default;
419
420 child = l->data;
421 has_default = gtk_widget_has_default (widget: child);
422
423 g_object_ref (child);
424 gtk_box_remove (GTK_BOX (assistant->action_area), child);
425 add_to_header_bar (assistant, child);
426 g_object_unref (object: child);
427
428 if (has_default)
429 {
430 gtk_window_set_default_widget (GTK_WINDOW (assistant), default_widget: child);
431 gtk_widget_add_css_class (widget: child, css_class: "suggested-action");
432 }
433 }
434 g_list_free (list: children);
435 }
436}
437
438static void
439gtk_assistant_constructed (GObject *object)
440{
441 GtkAssistant *assistant = GTK_ASSISTANT (object);
442
443 G_OBJECT_CLASS (gtk_assistant_parent_class)->constructed (object);
444
445 assistant->constructed = TRUE;
446 if (assistant->use_header_bar == -1)
447 assistant->use_header_bar = FALSE;
448
449 add_action_widgets (assistant);
450 apply_use_header_bar (assistant);
451}
452
453static void
454escape_cb (GtkAssistant *assistant)
455{
456 /* Do not allow cancelling in the middle of a progress page */
457 if (assistant->current_page &&
458 (assistant->current_page->type != GTK_ASSISTANT_PAGE_PROGRESS ||
459 assistant->current_page->complete))
460 g_signal_emit (instance: assistant, signal_id: signals [CANCEL], detail: 0, NULL);
461
462 /* don't run any user handlers - this is not a public signal */
463 g_signal_stop_emission (instance: assistant, signal_id: signals[ESCAPE], detail: 0);
464}
465
466static void
467gtk_assistant_finalize (GObject *object)
468{
469 GtkAssistant *assistant = GTK_ASSISTANT (object);
470
471 if (assistant->model)
472 g_object_remove_weak_pointer (G_OBJECT (assistant->model), weak_pointer_location: (gpointer *)&assistant->model);
473
474 G_OBJECT_CLASS (gtk_assistant_parent_class)->finalize (object);
475}
476
477static void
478gtk_assistant_class_init (GtkAssistantClass *class)
479{
480 GObjectClass *gobject_class;
481 GtkWidgetClass *widget_class;
482 GtkWindowClass *window_class;
483
484 gobject_class = (GObjectClass *) class;
485 widget_class = (GtkWidgetClass *) class;
486 window_class = (GtkWindowClass *) class;
487
488 gobject_class->dispose = gtk_assistant_dispose;
489 gobject_class->finalize = gtk_assistant_finalize;
490 gobject_class->constructed = gtk_assistant_constructed;
491 gobject_class->set_property = gtk_assistant_set_property;
492 gobject_class->get_property = gtk_assistant_get_property;
493
494 widget_class->map = gtk_assistant_map;
495 widget_class->unmap = gtk_assistant_unmap;
496
497 window_class->close_request = gtk_assistant_close_request;
498
499 /**
500 * GtkAssistant::cancel:
501 * @assistant: the `GtkAssistant`
502 *
503 * Emitted when then the cancel button is clicked.
504 */
505 signals[CANCEL] =
506 g_signal_new (I_("cancel"),
507 G_TYPE_FROM_CLASS (gobject_class),
508 signal_flags: G_SIGNAL_RUN_LAST,
509 G_STRUCT_OFFSET (GtkAssistantClass, cancel),
510 NULL, NULL,
511 NULL,
512 G_TYPE_NONE, n_params: 0);
513
514 /**
515 * GtkAssistant::prepare:
516 * @assistant: the `GtkAssistant`
517 * @page: the current page
518 *
519 * Emitted when a new page is set as the assistant's current page,
520 * before making the new page visible.
521 *
522 * A handler for this signal can do any preparations which are
523 * necessary before showing @page.
524 */
525 signals[PREPARE] =
526 g_signal_new (I_("prepare"),
527 G_TYPE_FROM_CLASS (gobject_class),
528 signal_flags: G_SIGNAL_RUN_LAST,
529 G_STRUCT_OFFSET (GtkAssistantClass, prepare),
530 NULL, NULL,
531 NULL,
532 G_TYPE_NONE, n_params: 1, GTK_TYPE_WIDGET);
533
534 /**
535 * GtkAssistant::apply:
536 * @assistant: the `GtkAssistant`
537 *
538 * Emitted when the apply button is clicked.
539 *
540 * The default behavior of the `GtkAssistant` is to switch to the page
541 * after the current page, unless the current page is the last one.
542 *
543 * A handler for the ::apply signal should carry out the actions for
544 * which the wizard has collected data. If the action takes a long time
545 * to complete, you might consider putting a page of type
546 * %GTK_ASSISTANT_PAGE_PROGRESS after the confirmation page and handle
547 * this operation within the [signal@Gtk.Assistant::prepare] signal of
548 * the progress page.
549 */
550 signals[APPLY] =
551 g_signal_new (I_("apply"),
552 G_TYPE_FROM_CLASS (gobject_class),
553 signal_flags: G_SIGNAL_RUN_LAST,
554 G_STRUCT_OFFSET (GtkAssistantClass, apply),
555 NULL, NULL,
556 NULL,
557 G_TYPE_NONE, n_params: 0);
558
559 /**
560 * GtkAssistant::close:
561 * @assistant: the `GtkAssistant`
562 *
563 * Emitted either when the close button of a summary page is clicked,
564 * or when the apply button in the last page in the flow (of type
565 * %GTK_ASSISTANT_PAGE_CONFIRM) is clicked.
566 */
567 signals[CLOSE] =
568 g_signal_new (I_("close"),
569 G_TYPE_FROM_CLASS (gobject_class),
570 signal_flags: G_SIGNAL_RUN_LAST,
571 G_STRUCT_OFFSET (GtkAssistantClass, close),
572 NULL, NULL,
573 NULL,
574 G_TYPE_NONE, n_params: 0);
575
576 /**
577 * GtkAssistant::escape
578 * @assistant: the `GtkAssistant`
579 *
580 * The action signal for the Escape binding.
581 */
582 signals[ESCAPE] =
583 g_signal_new_class_handler (I_("escape"),
584 G_TYPE_FROM_CLASS (gobject_class),
585 signal_flags: G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
586 G_CALLBACK (escape_cb),
587 NULL, NULL,
588 NULL,
589 G_TYPE_NONE, n_params: 0);
590
591 gtk_widget_class_add_binding_signal (widget_class,
592 GDK_KEY_Escape, mods: 0,
593 signal: "escape",
594 NULL);
595
596 /**
597 * GtkAssistant:use-header-bar:
598 *
599 * %TRUE if the assistant uses a `GtkHeaderBar` for action buttons
600 * instead of the action-area.
601 *
602 * For technical reasons, this property is declared as an integer
603 * property, but you should only set it to %TRUE or %FALSE.
604 */
605 g_object_class_install_property (oclass: gobject_class,
606 property_id: PROP_USE_HEADER_BAR,
607 pspec: g_param_spec_int (name: "use-header-bar",
608 P_("Use Header Bar"),
609 P_("Use Header Bar for actions."),
610 minimum: -1, maximum: 1, default_value: -1,
611 GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
612
613 /**
614 * GtkAssistant:pages:
615 *
616 * `GListModel` containing the pages.
617 */
618 g_object_class_install_property (oclass: gobject_class,
619 property_id: PROP_PAGES,
620 pspec: g_param_spec_object (name: "pages",
621 P_("Pages"),
622 P_("The pages of the assistant."),
623 G_TYPE_LIST_MODEL,
624 GTK_PARAM_READABLE));
625
626 /* Bind class to template
627 */
628 gtk_widget_class_set_template_from_resource (widget_class,
629 resource_name: "/org/gtk/libgtk/ui/gtkassistant.ui");
630
631 gtk_widget_class_bind_template_child_internal (widget_class, GtkAssistant, action_area);
632 gtk_widget_class_bind_template_child_internal (widget_class, GtkAssistant, headerbar);
633 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, content);
634 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, cancel);
635 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, forward);
636 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, back);
637 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, apply);
638 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, close);
639 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, last);
640 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, sidebar);
641 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, button_size_group);
642 gtk_widget_class_bind_template_child (widget_class, GtkAssistant, title_size_group);
643
644 gtk_widget_class_bind_template_callback (widget_class, on_assistant_close);
645 gtk_widget_class_bind_template_callback (widget_class, on_assistant_apply);
646 gtk_widget_class_bind_template_callback (widget_class, on_assistant_forward);
647 gtk_widget_class_bind_template_callback (widget_class, on_assistant_back);
648 gtk_widget_class_bind_template_callback (widget_class, on_assistant_cancel);
649 gtk_widget_class_bind_template_callback (widget_class, on_assistant_last);
650}
651
652static int
653default_forward_function (int current_page, gpointer data)
654{
655 GtkAssistant *assistant = GTK_ASSISTANT (data);
656 GtkAssistantPage *page_info;
657 GList *page_node;
658
659 page_node = g_list_nth (list: assistant->pages, n: ++current_page);
660
661 if (!page_node)
662 return -1;
663
664 page_info = (GtkAssistantPage *) page_node->data;
665
666 while (page_node && !gtk_widget_get_visible (widget: page_info->page))
667 {
668 page_node = page_node->next;
669 current_page++;
670
671 if (page_node)
672 page_info = (GtkAssistantPage *) page_node->data;
673 }
674
675 return current_page;
676}
677
678static gboolean
679last_button_visible (GtkAssistant *assistant, GtkAssistantPage *page)
680{
681 GtkAssistantPage *page_info;
682 int count, page_num, n_pages;
683
684 if (page == NULL)
685 return FALSE;
686
687 if (page->type != GTK_ASSISTANT_PAGE_CONTENT)
688 return FALSE;
689
690 count = 0;
691 page_num = g_list_index (list: assistant->pages, data: page);
692 n_pages = g_list_length (list: assistant->pages);
693 page_info = page;
694
695 while (page_num >= 0 && page_num < n_pages &&
696 page_info->type == GTK_ASSISTANT_PAGE_CONTENT &&
697 (count == 0 || page_info->complete) &&
698 count < n_pages)
699 {
700 page_num = (assistant->forward_function) (page_num, assistant->forward_function_data);
701 page_info = g_list_nth_data (list: assistant->pages, n: page_num);
702
703 count++;
704 }
705
706 /* Make the last button visible if we can skip multiple
707 * pages and end on a confirmation or summary page
708 */
709 if (count > 1 && page_info &&
710 (page_info->type == GTK_ASSISTANT_PAGE_CONFIRM ||
711 page_info->type == GTK_ASSISTANT_PAGE_SUMMARY))
712 return TRUE;
713 else
714 return FALSE;
715}
716
717static void
718update_actions_size (GtkAssistant *assistant)
719{
720 GList *l;
721 GtkAssistantPage *page;
722 int buttons, page_buttons;
723
724 if (!assistant->current_page)
725 return;
726
727 /* Some heuristics to find out how many buttons we should
728 * reserve space for. It is possible to trick this code
729 * with page forward functions and invisible pages, etc.
730 */
731 buttons = 0;
732 for (l = assistant->pages; l; l = l->next)
733 {
734 page = l->data;
735
736 if (!gtk_widget_get_visible (widget: page->page))
737 continue;
738
739 page_buttons = 2; /* cancel, forward/apply/close */
740 if (l != assistant->pages)
741 page_buttons += 1; /* back */
742 if (last_button_visible (assistant, page))
743 page_buttons += 1; /* last */
744
745 buttons = MAX (buttons, page_buttons);
746 }
747
748 buttons += assistant->extra_buttons;
749
750 gtk_widget_set_size_request (widget: assistant->action_area,
751 width: buttons * gtk_widget_get_allocated_width (widget: assistant->cancel) + (buttons - 1) * 6,
752 height: -1);
753}
754
755static void
756compute_last_button_state (GtkAssistant *assistant)
757{
758 gtk_widget_set_sensitive (widget: assistant->last, sensitive: assistant->current_page->complete);
759 if (last_button_visible (assistant, page: assistant->current_page))
760 gtk_widget_show (widget: assistant->last);
761 else
762 gtk_widget_hide (widget: assistant->last);
763}
764
765static void
766compute_progress_state (GtkAssistant *assistant)
767{
768 int page_num, n_pages;
769
770 n_pages = gtk_assistant_get_n_pages (assistant);
771 page_num = gtk_assistant_get_current_page (assistant);
772
773 page_num = (assistant->forward_function) (page_num, assistant->forward_function_data);
774
775 if (page_num >= 0 && page_num < n_pages)
776 gtk_widget_show (widget: assistant->forward);
777 else
778 gtk_widget_hide (widget: assistant->forward);
779}
780
781static void
782update_buttons_state (GtkAssistant *assistant)
783{
784 if (!assistant->current_page)
785 return;
786
787 switch (assistant->current_page->type)
788 {
789 case GTK_ASSISTANT_PAGE_INTRO:
790 gtk_widget_set_sensitive (widget: assistant->cancel, TRUE);
791 gtk_widget_set_sensitive (widget: assistant->forward, sensitive: assistant->current_page->complete);
792 gtk_window_set_default_widget (GTK_WINDOW (assistant), default_widget: assistant->forward);
793 gtk_widget_show (widget: assistant->forward);
794 gtk_widget_hide (widget: assistant->back);
795 gtk_widget_hide (widget: assistant->apply);
796 gtk_widget_hide (widget: assistant->close);
797 compute_last_button_state (assistant);
798 break;
799 case GTK_ASSISTANT_PAGE_CONFIRM:
800 gtk_widget_set_sensitive (widget: assistant->cancel, TRUE);
801 gtk_widget_set_sensitive (widget: assistant->back, TRUE);
802 gtk_widget_set_sensitive (widget: assistant->apply, sensitive: assistant->current_page->complete);
803 gtk_window_set_default_widget (GTK_WINDOW (assistant), default_widget: assistant->apply);
804 gtk_widget_show (widget: assistant->back);
805 gtk_widget_show (widget: assistant->apply);
806 gtk_widget_hide (widget: assistant->forward);
807 gtk_widget_hide (widget: assistant->close);
808 gtk_widget_hide (widget: assistant->last);
809 break;
810 case GTK_ASSISTANT_PAGE_CONTENT:
811 gtk_widget_set_sensitive (widget: assistant->cancel, TRUE);
812 gtk_widget_set_sensitive (widget: assistant->back, TRUE);
813 gtk_widget_set_sensitive (widget: assistant->forward, sensitive: assistant->current_page->complete);
814 gtk_window_set_default_widget (GTK_WINDOW (assistant), default_widget: assistant->forward);
815 gtk_widget_show (widget: assistant->back);
816 gtk_widget_show (widget: assistant->forward);
817 gtk_widget_hide (widget: assistant->apply);
818 gtk_widget_hide (widget: assistant->close);
819 compute_last_button_state (assistant);
820 break;
821 case GTK_ASSISTANT_PAGE_SUMMARY:
822 gtk_widget_set_sensitive (widget: assistant->close, sensitive: assistant->current_page->complete);
823 gtk_window_set_default_widget (GTK_WINDOW (assistant), default_widget: assistant->close);
824 gtk_widget_show (widget: assistant->close);
825 gtk_widget_hide (widget: assistant->back);
826 gtk_widget_hide (widget: assistant->forward);
827 gtk_widget_hide (widget: assistant->apply);
828 gtk_widget_hide (widget: assistant->last);
829 break;
830 case GTK_ASSISTANT_PAGE_PROGRESS:
831 gtk_widget_set_sensitive (widget: assistant->cancel, sensitive: assistant->current_page->complete);
832 gtk_widget_set_sensitive (widget: assistant->back, sensitive: assistant->current_page->complete);
833 gtk_widget_set_sensitive (widget: assistant->forward, sensitive: assistant->current_page->complete);
834 gtk_window_set_default_widget (GTK_WINDOW (assistant), default_widget: assistant->forward);
835 gtk_widget_show (widget: assistant->back);
836 gtk_widget_hide (widget: assistant->apply);
837 gtk_widget_hide (widget: assistant->close);
838 gtk_widget_hide (widget: assistant->last);
839 compute_progress_state (assistant);
840 break;
841 case GTK_ASSISTANT_PAGE_CUSTOM:
842 gtk_widget_hide (widget: assistant->cancel);
843 gtk_widget_hide (widget: assistant->back);
844 gtk_widget_hide (widget: assistant->forward);
845 gtk_widget_hide (widget: assistant->apply);
846 gtk_widget_hide (widget: assistant->last);
847 gtk_widget_hide (widget: assistant->close);
848 break;
849 default:
850 g_assert_not_reached ();
851 }
852
853 if (assistant->committed)
854 gtk_widget_hide (widget: assistant->cancel);
855 else if (assistant->current_page->type == GTK_ASSISTANT_PAGE_SUMMARY ||
856 assistant->current_page->type == GTK_ASSISTANT_PAGE_CUSTOM)
857 gtk_widget_hide (widget: assistant->cancel);
858 else
859 gtk_widget_show (widget: assistant->cancel);
860
861 /* this is quite general, we don't want to
862 * go back if it's the first page
863 */
864 if (!assistant->visited_pages)
865 gtk_widget_hide (widget: assistant->back);
866}
867
868static gboolean
869update_page_title_state (GtkAssistant *assistant, GList *list)
870{
871 GtkAssistantPage *page, *other;
872 gboolean visible;
873 GList *l;
874
875 page = list->data;
876
877 if (page->title == NULL || page->title[0] == 0)
878 visible = FALSE;
879 else
880 visible = gtk_widget_get_visible (widget: page->page);
881
882 if (page == assistant->current_page)
883 {
884 gtk_widget_set_visible (widget: page->regular_title, FALSE);
885 gtk_widget_set_visible (widget: page->current_title, visible);
886 }
887 else
888 {
889 /* If multiple consecutive pages have the same title,
890 * we only show it once, since it would otherwise look
891 * silly. We have to be a little careful, since we
892 * _always_ show the title of the current page.
893 */
894 if (list->prev)
895 {
896 other = list->prev->data;
897 if (g_strcmp0 (str1: page->title, str2: other->title) == 0)
898 visible = FALSE;
899 }
900 for (l = list->next; l; l = l->next)
901 {
902 other = l->data;
903 if (g_strcmp0 (str1: page->title, str2: other->title) != 0)
904 break;
905
906 if (other == assistant->current_page)
907 {
908 visible = FALSE;
909 break;
910 }
911 }
912
913 gtk_widget_set_visible (widget: page->regular_title, visible);
914 gtk_widget_set_visible (widget: page->current_title, FALSE);
915 }
916
917 return visible;
918}
919
920static void
921update_title_state (GtkAssistant *assistant)
922{
923 GList *l;
924 gboolean show_titles;
925
926 show_titles = FALSE;
927 for (l = assistant->pages; l != NULL; l = l->next)
928 {
929 if (update_page_title_state (assistant, list: l))
930 show_titles = TRUE;
931 }
932
933 gtk_widget_set_visible (widget: assistant->sidebar, visible: show_titles);
934}
935
936static void
937set_current_page (GtkAssistant *assistant,
938 int page_num)
939{
940 assistant->current_page = (GtkAssistantPage *)g_list_nth_data (list: assistant->pages, n: page_num);
941
942 g_signal_emit (instance: assistant, signal_id: signals [PREPARE], detail: 0, assistant->current_page->page);
943 /* do not continue if the prepare signal handler has already changed the
944 * current page */
945 if (assistant->current_page != (GtkAssistantPage *)g_list_nth_data (list: assistant->pages, n: page_num))
946 return;
947
948 update_title_state (assistant);
949
950 gtk_window_set_title (GTK_WINDOW (assistant), title: assistant->current_page->title);
951
952 gtk_stack_set_visible_child (GTK_STACK (assistant->content), child: assistant->current_page->page);
953
954 /* update buttons state, flow may have changed */
955 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
956 update_buttons_state (assistant);
957
958 if (!gtk_widget_child_focus (widget: assistant->current_page->page, direction: GTK_DIR_TAB_FORWARD))
959 {
960 GtkWidget *button[6];
961 int i;
962
963 /* find the best button to focus */
964 button[0] = assistant->apply;
965 button[1] = assistant->close;
966 button[2] = assistant->forward;
967 button[3] = assistant->back;
968 button[4] = assistant->cancel;
969 button[5] = assistant->last;
970 for (i = 0; i < 6; i++)
971 {
972 if (gtk_widget_get_visible (widget: button[i]) &&
973 gtk_widget_get_sensitive (widget: button[i]))
974 {
975 gtk_widget_grab_focus (widget: button[i]);
976 break;
977 }
978 }
979 }
980}
981
982static int
983compute_next_step (GtkAssistant *assistant)
984{
985 GtkAssistantPage *page_info;
986 int current_page, n_pages, next_page;
987
988 current_page = gtk_assistant_get_current_page (assistant);
989 page_info = assistant->current_page;
990 n_pages = gtk_assistant_get_n_pages (assistant);
991
992 next_page = (assistant->forward_function) (current_page,
993 assistant->forward_function_data);
994
995 if (next_page >= 0 && next_page < n_pages)
996 {
997 assistant->visited_pages = g_slist_prepend (list: assistant->visited_pages, data: page_info);
998 set_current_page (assistant, page_num: next_page);
999
1000 return TRUE;
1001 }
1002
1003 return FALSE;
1004}
1005
1006static void
1007on_assistant_close (GtkWidget *widget,
1008 GtkAssistant *assistant)
1009{
1010 g_signal_emit (instance: assistant, signal_id: signals [CLOSE], detail: 0, NULL);
1011}
1012
1013static void
1014on_assistant_apply (GtkWidget *widget,
1015 GtkAssistant *assistant)
1016{
1017 gboolean success;
1018
1019 g_signal_emit (instance: assistant, signal_id: signals [APPLY], detail: 0);
1020
1021 success = compute_next_step (assistant);
1022
1023 /* if the assistant hasn't switched to another page, just emit
1024 * the CLOSE signal, it't the last page in the assistant flow
1025 */
1026 if (!success)
1027 g_signal_emit (instance: assistant, signal_id: signals [CLOSE], detail: 0);
1028}
1029
1030static void
1031on_assistant_forward (GtkWidget *widget,
1032 GtkAssistant *assistant)
1033{
1034 gtk_assistant_next_page (assistant);
1035}
1036
1037static void
1038on_assistant_back (GtkWidget *widget,
1039 GtkAssistant *assistant)
1040{
1041 gtk_assistant_previous_page (assistant);
1042}
1043
1044static void
1045on_assistant_cancel (GtkWidget *widget,
1046 GtkAssistant *assistant)
1047{
1048 g_signal_emit (instance: assistant, signal_id: signals [CANCEL], detail: 0, NULL);
1049}
1050
1051static void
1052on_assistant_last (GtkWidget *widget,
1053 GtkAssistant *assistant)
1054{
1055 while (assistant->current_page->type == GTK_ASSISTANT_PAGE_CONTENT &&
1056 assistant->current_page->complete)
1057 compute_next_step (assistant);
1058}
1059
1060static gboolean
1061alternative_button_order (GtkAssistant *assistant)
1062{
1063 gboolean result;
1064
1065 g_object_get (object: gtk_widget_get_settings (GTK_WIDGET (assistant)),
1066 first_property_name: "gtk-alternative-button-order", &result,
1067 NULL);
1068 return result;
1069}
1070
1071static void
1072on_page_page_notify (GtkWidget *widget,
1073 GParamSpec *arg,
1074 gpointer data)
1075{
1076 GtkAssistant *assistant = GTK_ASSISTANT (data);
1077
1078 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1079 {
1080 update_buttons_state (assistant);
1081 update_title_state (assistant);
1082 }
1083}
1084
1085static void
1086on_page_notify (GtkAssistantPage *page,
1087 GParamSpec *arg,
1088 gpointer data)
1089{
1090 if (page->page)
1091 on_page_page_notify (widget: page->page, arg, data);
1092}
1093
1094static void
1095assistant_remove_page (GtkAssistant *assistant,
1096 GtkWidget *page)
1097{
1098 GtkAssistantPage *page_info;
1099 GList *page_node;
1100 GList *element;
1101
1102 element = find_page (assistant, page);
1103 if (!element)
1104 return;
1105
1106 page_info = element->data;
1107
1108 /* If this is the current page, we need to switch away. */
1109 if (page_info == assistant->current_page)
1110 {
1111 if (!compute_next_step (assistant))
1112 {
1113 /* The best we can do at this point is probably to pick
1114 * the first visible page.
1115 */
1116 page_node = assistant->pages;
1117
1118 while (page_node &&
1119 !gtk_widget_get_visible (widget: ((GtkAssistantPage *) page_node->data)->page))
1120 page_node = page_node->next;
1121
1122 if (page_node == element)
1123 page_node = page_node->next;
1124
1125 if (page_node)
1126 assistant->current_page = page_node->data;
1127 else
1128 assistant->current_page = NULL;
1129 }
1130 }
1131
1132 g_signal_handlers_disconnect_by_func (page_info->page, on_page_page_notify, assistant);
1133 g_signal_handlers_disconnect_by_func (page_info, on_page_notify, assistant);
1134
1135 gtk_size_group_remove_widget (size_group: assistant->title_size_group, widget: page_info->regular_title);
1136 gtk_size_group_remove_widget (size_group: assistant->title_size_group, widget: page_info->current_title);
1137
1138 gtk_box_remove (GTK_BOX (assistant->sidebar), child: page_info->regular_title);
1139 gtk_box_remove (GTK_BOX (assistant->sidebar), child: page_info->current_title);
1140
1141 assistant->pages = g_list_remove_link (list: assistant->pages, llink: element);
1142 assistant->visited_pages = g_slist_remove_all (list: assistant->visited_pages, data: page_info);
1143
1144 g_object_unref (object: page_info);
1145
1146 g_list_free_1 (list: element);
1147
1148 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1149 {
1150 update_buttons_state (assistant);
1151 update_actions_size (assistant);
1152 }
1153}
1154
1155static void
1156gtk_assistant_init (GtkAssistant *assistant)
1157{
1158 gtk_widget_add_css_class (GTK_WIDGET (assistant), css_class: "assistant");
1159
1160 assistant->pages = NULL;
1161 assistant->current_page = NULL;
1162 assistant->visited_pages = NULL;
1163
1164 assistant->forward_function = default_forward_function;
1165 assistant->forward_function_data = assistant;
1166 assistant->forward_data_destroy = NULL;
1167
1168 g_object_get (object: gtk_widget_get_settings (GTK_WIDGET (assistant)),
1169 first_property_name: "gtk-dialogs-use-header", &assistant->use_header_bar,
1170 NULL);
1171
1172 gtk_widget_init_template (GTK_WIDGET (assistant));
1173
1174 if (alternative_button_order (assistant))
1175 {
1176 GList *buttons, *l;
1177 GtkWidget *child;
1178
1179 buttons = NULL;
1180 for (child = gtk_widget_get_last_child (widget: assistant->action_area);
1181 child != NULL;
1182 child = gtk_widget_get_prev_sibling (widget: child))
1183 buttons = g_list_prepend (list: buttons, data: child);
1184
1185 for (l = buttons; l; l = l->next)
1186 gtk_box_reorder_child_after (GTK_BOX (assistant->action_area), GTK_WIDGET (l->data), NULL);
1187
1188 g_list_free (list: buttons);
1189 }
1190}
1191
1192static void
1193gtk_assistant_page_set_property (GObject *object,
1194 guint property_id,
1195 const GValue *value,
1196 GParamSpec *pspec)
1197{
1198 GtkAssistantPage *page = GTK_ASSISTANT_PAGE (object);
1199 GtkWidget *assistant = NULL;
1200
1201 if (page->page)
1202 assistant = gtk_widget_get_ancestor (widget: page->page, GTK_TYPE_ASSISTANT);
1203
1204 switch (property_id)
1205 {
1206 case CHILD_PROP_CHILD:
1207 g_set_object (&page->page, g_value_get_object (value));
1208 break;
1209
1210 case CHILD_PROP_PAGE_TYPE:
1211 if (page->type != g_value_get_enum (value))
1212 {
1213 page->type = g_value_get_enum (value);
1214
1215 /* backwards compatibility to the era before fixing bug 604289 */
1216 if (page->type == GTK_ASSISTANT_PAGE_SUMMARY && !page->complete_set)
1217 {
1218 page->complete = TRUE;
1219 page->complete_set = FALSE;
1220 }
1221
1222 /* Always set buttons state, a change in a future page
1223 * might change current page buttons
1224 */
1225 if (assistant)
1226 update_buttons_state (GTK_ASSISTANT (assistant));
1227 g_object_notify (G_OBJECT (page), property_name: "page-type");
1228 }
1229 break;
1230
1231 case CHILD_PROP_PAGE_TITLE:
1232 g_free (mem: page->title);
1233 page->title = g_value_dup_string (value);
1234
1235 if (assistant)
1236 {
1237 gtk_label_set_text (self: (GtkLabel*) page->regular_title, str: page->title);
1238 gtk_label_set_text (self: (GtkLabel*) page->current_title, str: page->title);
1239 update_title_state (GTK_ASSISTANT (assistant));
1240 }
1241
1242 g_object_notify (G_OBJECT (page), property_name: "title");
1243
1244 break;
1245
1246 case CHILD_PROP_PAGE_COMPLETE:
1247 if (page->complete != g_value_get_boolean (value))
1248 {
1249 page->complete = g_value_get_boolean (value);
1250 page->complete_set = TRUE;
1251
1252 /* Always set buttons state, a change in a future page
1253 * might change current page buttons
1254 */
1255 if (assistant)
1256 update_buttons_state (GTK_ASSISTANT (assistant));
1257 g_object_notify (G_OBJECT (page), property_name: "complete");
1258 }
1259 break;
1260
1261 default:
1262 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1263 break;
1264 }
1265}
1266
1267static void
1268gtk_assistant_page_get_property (GObject *object,
1269 guint property_id,
1270 GValue *value,
1271 GParamSpec *pspec)
1272{
1273 GtkAssistantPage *page = GTK_ASSISTANT_PAGE (object);
1274
1275 switch (property_id)
1276 {
1277 case CHILD_PROP_CHILD:
1278 g_value_set_object (value, v_object: page->page);
1279 break;
1280
1281 case CHILD_PROP_PAGE_TYPE:
1282 g_value_set_enum (value, v_enum: page->type);
1283 break;
1284
1285 case CHILD_PROP_PAGE_TITLE:
1286 g_value_set_string (value, v_string: page->title);
1287 break;
1288
1289 case CHILD_PROP_PAGE_COMPLETE:
1290 g_value_set_boolean (value, v_boolean: page->complete);
1291 break;
1292
1293 default:
1294 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1295 break;
1296 }
1297}
1298
1299static void
1300gtk_assistant_dispose (GObject *object)
1301{
1302 GtkAssistant *assistant = GTK_ASSISTANT (object);
1303
1304 if (assistant->model)
1305 g_list_model_items_changed (list: G_LIST_MODEL (ptr: assistant->model), position: 0, removed: g_list_length (list: assistant->pages), added: 0);
1306
1307 /* We set current to NULL so that the remove code doesn't try
1308 * to do anything funny
1309 */
1310 assistant->current_page = NULL;
1311
1312 if (assistant->content)
1313 {
1314 while (assistant->pages)
1315 gtk_assistant_remove_page (assistant, page_num: 0);
1316
1317 assistant->content = NULL;
1318 }
1319
1320 assistant->sidebar = NULL;
1321 assistant->action_area = NULL;
1322
1323 if (assistant->forward_function)
1324 {
1325 if (assistant->forward_function_data &&
1326 assistant->forward_data_destroy)
1327 assistant->forward_data_destroy (assistant->forward_function_data);
1328
1329 assistant->forward_function = NULL;
1330 assistant->forward_function_data = NULL;
1331 assistant->forward_data_destroy = NULL;
1332 }
1333
1334 if (assistant->visited_pages)
1335 {
1336 g_slist_free (list: assistant->visited_pages);
1337 assistant->visited_pages = NULL;
1338 }
1339
1340 G_OBJECT_CLASS (gtk_assistant_parent_class)->dispose (object);
1341}
1342
1343static GList*
1344find_page (GtkAssistant *assistant,
1345 GtkWidget *page)
1346{
1347 GList *child = assistant->pages;
1348
1349 while (child)
1350 {
1351 GtkAssistantPage *page_info = child->data;
1352 if (page_info->page == page)
1353 return child;
1354
1355 child = child->next;
1356 }
1357
1358 return NULL;
1359}
1360
1361static void
1362gtk_assistant_map (GtkWidget *widget)
1363{
1364 GtkAssistant *assistant = GTK_ASSISTANT (widget);
1365 GList *page_node;
1366 GtkAssistantPage *page;
1367 int page_num;
1368
1369 /* if there's no default page, pick the first one */
1370 page = NULL;
1371 page_num = 0;
1372 if (!assistant->current_page)
1373 {
1374 page_node = assistant->pages;
1375
1376 while (page_node && !gtk_widget_get_visible (widget: ((GtkAssistantPage *) page_node->data)->page))
1377 {
1378 page_node = page_node->next;
1379 page_num++;
1380 }
1381
1382 if (page_node)
1383 page = page_node->data;
1384 }
1385
1386 if (page && gtk_widget_get_visible (widget: page->page))
1387 set_current_page (assistant, page_num);
1388
1389 update_buttons_state (assistant);
1390 update_actions_size (assistant);
1391 update_title_state (assistant);
1392
1393 GTK_WIDGET_CLASS (gtk_assistant_parent_class)->map (widget);
1394}
1395
1396static void
1397gtk_assistant_unmap (GtkWidget *widget)
1398{
1399 GtkAssistant *assistant = GTK_ASSISTANT (widget);
1400
1401 g_slist_free (list: assistant->visited_pages);
1402 assistant->visited_pages = NULL;
1403 assistant->current_page = NULL;
1404
1405 GTK_WIDGET_CLASS (gtk_assistant_parent_class)->unmap (widget);
1406}
1407
1408static gboolean
1409gtk_assistant_close_request (GtkWindow *window)
1410{
1411 GtkAssistant *assistant = GTK_ASSISTANT (window);
1412
1413 /* Do not allow cancelling in the middle of a progress page */
1414 if (assistant->current_page &&
1415 (assistant->current_page->type != GTK_ASSISTANT_PAGE_PROGRESS ||
1416 assistant->current_page->complete))
1417 g_signal_emit (instance: assistant, signal_id: signals [CANCEL], detail: 0, NULL);
1418
1419 return TRUE;
1420}
1421
1422/**
1423 * gtk_assistant_new:
1424 *
1425 * Creates a new `GtkAssistant`.
1426 *
1427 * Returns: a newly created `GtkAssistant`
1428 */
1429GtkWidget*
1430gtk_assistant_new (void)
1431{
1432 GtkWidget *assistant;
1433
1434 assistant = g_object_new (GTK_TYPE_ASSISTANT, NULL);
1435
1436 return assistant;
1437}
1438
1439/**
1440 * gtk_assistant_get_current_page:
1441 * @assistant: a `GtkAssistant`
1442 *
1443 * Returns the page number of the current page.
1444 *
1445 * Returns: The index (starting from 0) of the current
1446 * page in the @assistant, or -1 if the @assistant has no pages,
1447 * or no current page
1448 */
1449int
1450gtk_assistant_get_current_page (GtkAssistant *assistant)
1451{
1452 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), -1);
1453
1454 if (!assistant->pages || !assistant->current_page)
1455 return -1;
1456
1457 return g_list_index (list: assistant->pages, data: assistant->current_page);
1458}
1459
1460/**
1461 * gtk_assistant_set_current_page:
1462 * @assistant: a `GtkAssistant`
1463 * @page_num: index of the page to switch to, starting from 0.
1464 * If negative, the last page will be used. If greater
1465 * than the number of pages in the @assistant, nothing
1466 * will be done.
1467 *
1468 * Switches the page to @page_num.
1469 *
1470 * Note that this will only be necessary in custom buttons,
1471 * as the @assistant flow can be set with
1472 * gtk_assistant_set_forward_page_func().
1473 */
1474void
1475gtk_assistant_set_current_page (GtkAssistant *assistant,
1476 int page_num)
1477{
1478 GtkAssistantPage *page;
1479
1480 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1481 g_return_if_fail (assistant->pages != NULL);
1482
1483 if (page_num >= 0)
1484 page = (GtkAssistantPage *) g_list_nth_data (list: assistant->pages, n: page_num);
1485 else
1486 {
1487 page = (GtkAssistantPage *) g_list_last (list: assistant->pages)->data;
1488 page_num = g_list_length (list: assistant->pages);
1489 }
1490
1491 g_return_if_fail (page != NULL);
1492
1493 if (assistant->current_page == page)
1494 return;
1495
1496 /* only add the page to the visited list if the assistant is mapped,
1497 * if not, just use it as an initial page setting, for the cases where
1498 * the initial page is != to 0
1499 */
1500 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1501 assistant->visited_pages = g_slist_prepend (list: assistant->visited_pages,
1502 data: assistant->current_page);
1503
1504 set_current_page (assistant, page_num);
1505}
1506
1507/**
1508 * gtk_assistant_next_page:
1509 * @assistant: a `GtkAssistant`
1510 *
1511 * Navigate to the next page.
1512 *
1513 * It is a programming error to call this function when
1514 * there is no next page.
1515 *
1516 * This function is for use when creating pages of the
1517 * %GTK_ASSISTANT_PAGE_CUSTOM type.
1518 */
1519void
1520gtk_assistant_next_page (GtkAssistant *assistant)
1521{
1522 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1523
1524 if (!compute_next_step (assistant))
1525 g_critical ("Page flow is broken.\n"
1526 "You may want to end it with a page of type\n"
1527 "GTK_ASSISTANT_PAGE_CONFIRM or GTK_ASSISTANT_PAGE_SUMMARY");
1528}
1529
1530/**
1531 * gtk_assistant_previous_page:
1532 * @assistant: a `GtkAssistant`
1533 *
1534 * Navigate to the previous visited page.
1535 *
1536 * It is a programming error to call this function when
1537 * no previous page is available.
1538 *
1539 * This function is for use when creating pages of the
1540 * %GTK_ASSISTANT_PAGE_CUSTOM type.
1541 */
1542void
1543gtk_assistant_previous_page (GtkAssistant *assistant)
1544{
1545 GtkAssistantPage *page_info;
1546 GSList *page_node;
1547
1548 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1549
1550 /* skip the progress pages when going back */
1551 do
1552 {
1553 page_node = assistant->visited_pages;
1554
1555 g_return_if_fail (page_node != NULL);
1556
1557 assistant->visited_pages = assistant->visited_pages->next;
1558 page_info = (GtkAssistantPage *) page_node->data;
1559 g_slist_free_1 (list: page_node);
1560 }
1561 while (page_info->type == GTK_ASSISTANT_PAGE_PROGRESS ||
1562 !gtk_widget_get_visible (widget: page_info->page));
1563
1564 set_current_page (assistant, page_num: g_list_index (list: assistant->pages, data: page_info));
1565}
1566
1567/**
1568 * gtk_assistant_get_n_pages:
1569 * @assistant: a `GtkAssistant`
1570 *
1571 * Returns the number of pages in the @assistant
1572 *
1573 * Returns: the number of pages in the @assistant
1574 */
1575int
1576gtk_assistant_get_n_pages (GtkAssistant *assistant)
1577{
1578 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1579
1580 return g_list_length (list: assistant->pages);
1581}
1582
1583/**
1584 * gtk_assistant_get_nth_page:
1585 * @assistant: a `GtkAssistant`
1586 * @page_num: the index of a page in the @assistant,
1587 * or -1 to get the last page
1588 *
1589 * Returns the child widget contained in page number @page_num.
1590 *
1591 * Returns: (nullable) (transfer none): the child widget, or %NULL
1592 * if @page_num is out of bounds
1593 */
1594GtkWidget*
1595gtk_assistant_get_nth_page (GtkAssistant *assistant,
1596 int page_num)
1597{
1598 GtkAssistantPage *page;
1599 GList *elem;
1600
1601 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1602 g_return_val_if_fail (page_num >= -1, NULL);
1603
1604 if (page_num == -1)
1605 elem = g_list_last (list: assistant->pages);
1606 else
1607 elem = g_list_nth (list: assistant->pages, n: page_num);
1608
1609 if (!elem)
1610 return NULL;
1611
1612 page = (GtkAssistantPage *) elem->data;
1613
1614 return page->page;
1615}
1616
1617/**
1618 * gtk_assistant_prepend_page:
1619 * @assistant: a `GtkAssistant`
1620 * @page: a `GtkWidget`
1621 *
1622 * Prepends a page to the @assistant.
1623 *
1624 * Returns: the index (starting at 0) of the inserted page
1625 */
1626int
1627gtk_assistant_prepend_page (GtkAssistant *assistant,
1628 GtkWidget *page)
1629{
1630 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1631 g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1632
1633 return gtk_assistant_insert_page (assistant, page, position: 0);
1634}
1635
1636/**
1637 * gtk_assistant_append_page:
1638 * @assistant: a `GtkAssistant`
1639 * @page: a `GtkWidget`
1640 *
1641 * Appends a page to the @assistant.
1642 *
1643 * Returns: the index (starting at 0) of the inserted page
1644 */
1645int
1646gtk_assistant_append_page (GtkAssistant *assistant,
1647 GtkWidget *page)
1648{
1649 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1650 g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1651
1652 return gtk_assistant_insert_page (assistant, page, position: -1);
1653}
1654
1655/**
1656 * gtk_assistant_insert_page:
1657 * @assistant: a `GtkAssistant`
1658 * @page: a `GtkWidget`
1659 * @position: the index (starting at 0) at which to insert the page,
1660 * or -1 to append the page to the @assistant
1661 *
1662 * Inserts a page in the @assistant at a given position.
1663 *
1664 * Returns: the index (starting from 0) of the inserted page
1665 */
1666int
1667gtk_assistant_insert_page (GtkAssistant *assistant,
1668 GtkWidget *page,
1669 int position)
1670{
1671 GtkAssistantPage *page_info;
1672
1673 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), 0);
1674 g_return_val_if_fail (GTK_IS_WIDGET (page), 0);
1675 g_return_val_if_fail (gtk_widget_get_parent (page) == NULL, 0);
1676
1677 page_info = g_object_new (GTK_TYPE_ASSISTANT_PAGE, NULL);
1678 page_info->page = g_object_ref (page);
1679
1680 return gtk_assistant_add_page (assistant, page_info, position);
1681
1682 g_object_unref (object: page_info);
1683}
1684
1685static int
1686gtk_assistant_add_page (GtkAssistant *assistant,
1687 GtkAssistantPage *page_info,
1688 int position)
1689{
1690 int n_pages;
1691 GtkWidget *sibling;
1692 char *name;
1693
1694 page_info->regular_title = gtk_label_new (str: page_info->title);
1695 page_info->current_title = gtk_label_new (str: page_info->title);
1696
1697 gtk_label_set_xalign (GTK_LABEL (page_info->regular_title), xalign: 0.0);
1698 gtk_label_set_xalign (GTK_LABEL (page_info->current_title), xalign: 0.0);
1699
1700 gtk_widget_show (widget: page_info->regular_title);
1701 gtk_widget_hide (widget: page_info->current_title);
1702
1703 gtk_widget_add_css_class (widget: page_info->current_title, css_class: "highlight");
1704
1705 gtk_size_group_add_widget (size_group: assistant->title_size_group, widget: page_info->regular_title);
1706 gtk_size_group_add_widget (size_group: assistant->title_size_group, widget: page_info->current_title);
1707
1708 g_signal_connect (G_OBJECT (page_info->page), "notify::visible",
1709 G_CALLBACK (on_page_page_notify), assistant);
1710
1711 g_signal_connect (G_OBJECT (page_info), "notify::page-title",
1712 G_CALLBACK (on_page_notify), assistant);
1713
1714 g_signal_connect (G_OBJECT (page_info), "notify::page-type",
1715 G_CALLBACK (on_page_notify), assistant);
1716
1717 n_pages = g_list_length (list: assistant->pages);
1718
1719 if (position < 0 || position > n_pages)
1720 position = n_pages;
1721
1722 assistant->pages = g_list_insert (list: assistant->pages, g_object_ref (page_info), position);
1723
1724 if (position == 0)
1725 sibling = NULL;
1726 else
1727 {
1728 int i;
1729 sibling = gtk_widget_get_first_child (widget: assistant->sidebar);
1730 for (i = 1; i < 2 * position; i++)
1731 sibling = gtk_widget_get_next_sibling (widget: sibling);
1732 }
1733
1734 gtk_box_insert_child_after (GTK_BOX (assistant->sidebar), child: page_info->current_title, sibling);
1735 gtk_box_insert_child_after (GTK_BOX (assistant->sidebar), child: page_info->regular_title, sibling);
1736
1737 name = g_strdup_printf (format: "%p", page_info->page);
1738 gtk_stack_add_named (GTK_STACK (assistant->content), child: page_info->page, name);
1739 g_free (mem: name);
1740
1741 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1742 {
1743 update_buttons_state (assistant);
1744 update_actions_size (assistant);
1745 }
1746
1747 if (assistant->model)
1748 g_list_model_items_changed (list: assistant->model, position, removed: 0, added: 1);
1749
1750 return position;
1751}
1752
1753/**
1754 * gtk_assistant_remove_page:
1755 * @assistant: a `GtkAssistant`
1756 * @page_num: the index of a page in the @assistant,
1757 * or -1 to remove the last page
1758 *
1759 * Removes the @page_num’s page from @assistant.
1760 */
1761void
1762gtk_assistant_remove_page (GtkAssistant *assistant,
1763 int page_num)
1764{
1765 GtkWidget *page;
1766
1767 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1768
1769 page = gtk_assistant_get_nth_page (assistant, page_num);
1770 if (page)
1771 assistant_remove_page (assistant, page);
1772
1773 if (assistant->model)
1774 g_list_model_items_changed (list: assistant->model, position: page_num, removed: 1, added: 0);
1775}
1776
1777/**
1778 * gtk_assistant_set_forward_page_func:
1779 * @assistant: a `GtkAssistant`
1780 * @page_func: (nullable): the `GtkAssistantPageFunc`, or %NULL
1781 * to use the default one
1782 * @data: user data for @page_func
1783 * @destroy: destroy notifier for @data
1784 *
1785 * Sets the page forwarding function to be @page_func.
1786 *
1787 * This function will be used to determine what will be
1788 * the next page when the user presses the forward button.
1789 * Setting @page_func to %NULL will make the assistant to
1790 * use the default forward function, which just goes to the
1791 * next visible page.
1792 */
1793void
1794gtk_assistant_set_forward_page_func (GtkAssistant *assistant,
1795 GtkAssistantPageFunc page_func,
1796 gpointer data,
1797 GDestroyNotify destroy)
1798{
1799 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1800
1801 if (assistant->forward_data_destroy &&
1802 assistant->forward_function_data)
1803 (*assistant->forward_data_destroy) (assistant->forward_function_data);
1804
1805 if (page_func)
1806 {
1807 assistant->forward_function = page_func;
1808 assistant->forward_function_data = data;
1809 assistant->forward_data_destroy = destroy;
1810 }
1811 else
1812 {
1813 assistant->forward_function = default_forward_function;
1814 assistant->forward_function_data = assistant;
1815 assistant->forward_data_destroy = NULL;
1816 }
1817
1818 /* Page flow has possibly changed, so the
1819 * buttons state might need to change too
1820 */
1821 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1822 update_buttons_state (assistant);
1823}
1824
1825static void
1826add_to_action_area (GtkAssistant *assistant,
1827 GtkWidget *child)
1828{
1829 gtk_widget_set_valign (widget: child, align: GTK_ALIGN_BASELINE);
1830
1831 gtk_box_append (GTK_BOX (assistant->action_area), child);
1832}
1833
1834/**
1835 * gtk_assistant_add_action_widget:
1836 * @assistant: a `GtkAssistant`
1837 * @child: a `GtkWidget`
1838 *
1839 * Adds a widget to the action area of a `GtkAssistant`.
1840 */
1841void
1842gtk_assistant_add_action_widget (GtkAssistant *assistant,
1843 GtkWidget *child)
1844{
1845 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1846 g_return_if_fail (GTK_IS_WIDGET (child));
1847
1848 if (GTK_IS_BUTTON (child))
1849 {
1850 gtk_size_group_add_widget (size_group: assistant->button_size_group, widget: child);
1851 assistant->extra_buttons += 1;
1852 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1853 update_actions_size (assistant);
1854 }
1855
1856 if (assistant->constructed && assistant->use_header_bar)
1857 add_to_header_bar (assistant, child);
1858 else
1859 add_to_action_area (assistant, child);
1860}
1861
1862/**
1863 * gtk_assistant_remove_action_widget:
1864 * @assistant: a `GtkAssistant`
1865 * @child: a `GtkWidget`
1866 *
1867 * Removes a widget from the action area of a `GtkAssistant`.
1868 */
1869void
1870gtk_assistant_remove_action_widget (GtkAssistant *assistant,
1871 GtkWidget *child)
1872{
1873 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1874 g_return_if_fail (GTK_IS_WIDGET (child));
1875
1876 if (GTK_IS_BUTTON (child))
1877 {
1878 gtk_size_group_remove_widget (size_group: assistant->button_size_group, widget: child);
1879 assistant->extra_buttons -= 1;
1880 if (gtk_widget_get_mapped (GTK_WIDGET (assistant)))
1881 update_actions_size (assistant);
1882 }
1883
1884 gtk_box_remove (GTK_BOX (assistant->action_area), child);
1885}
1886
1887/**
1888 * gtk_assistant_set_page_title:
1889 * @assistant: a `GtkAssistant`
1890 * @page: a page of @assistant
1891 * @title: the new title for @page
1892 *
1893 * Sets a title for @page.
1894 *
1895 * The title is displayed in the header area of the assistant
1896 * when @page is the current page.
1897 */
1898void
1899gtk_assistant_set_page_title (GtkAssistant *assistant,
1900 GtkWidget *page,
1901 const char *title)
1902{
1903 GtkAssistantPage *page_info;
1904 GList *child;
1905
1906 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1907 g_return_if_fail (GTK_IS_WIDGET (page));
1908
1909 child = find_page (assistant, page);
1910
1911 g_return_if_fail (child != NULL);
1912
1913 page_info = (GtkAssistantPage*) child->data;
1914
1915 g_object_set (object: page_info, first_property_name: "title", title, NULL);
1916}
1917
1918/**
1919 * gtk_assistant_get_page_title:
1920 * @assistant: a `GtkAssistant`
1921 * @page: a page of @assistant
1922 *
1923 * Gets the title for @page.
1924 *
1925 * Returns: the title for @page
1926 */
1927const char *
1928gtk_assistant_get_page_title (GtkAssistant *assistant,
1929 GtkWidget *page)
1930{
1931 GtkAssistantPage *page_info;
1932 GList *child;
1933
1934 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
1935 g_return_val_if_fail (GTK_IS_WIDGET (page), NULL);
1936
1937 child = find_page (assistant, page);
1938
1939 g_return_val_if_fail (child != NULL, NULL);
1940
1941 page_info = (GtkAssistantPage*) child->data;
1942
1943 return page_info->title;
1944}
1945
1946/**
1947 * gtk_assistant_set_page_type:
1948 * @assistant: a `GtkAssistant`
1949 * @page: a page of @assistant
1950 * @type: the new type for @page
1951 *
1952 * Sets the page type for @page.
1953 *
1954 * The page type determines the page behavior in the @assistant.
1955 */
1956void
1957gtk_assistant_set_page_type (GtkAssistant *assistant,
1958 GtkWidget *page,
1959 GtkAssistantPageType type)
1960{
1961 GtkAssistantPage *page_info;
1962 GList *child;
1963
1964 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
1965 g_return_if_fail (GTK_IS_WIDGET (page));
1966
1967 child = find_page (assistant, page);
1968
1969 g_return_if_fail (child != NULL);
1970
1971 page_info = (GtkAssistantPage*) child->data;
1972
1973 g_object_set (object: page_info, first_property_name: "page-type", type, NULL);
1974}
1975
1976/**
1977 * gtk_assistant_get_page_type:
1978 * @assistant: a `GtkAssistant`
1979 * @page: a page of @assistant
1980 *
1981 * Gets the page type of @page.
1982 *
1983 * Returns: the page type of @page
1984 */
1985GtkAssistantPageType
1986gtk_assistant_get_page_type (GtkAssistant *assistant,
1987 GtkWidget *page)
1988{
1989 GtkAssistantPage *page_info;
1990 GList *child;
1991
1992 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), GTK_ASSISTANT_PAGE_CONTENT);
1993 g_return_val_if_fail (GTK_IS_WIDGET (page), GTK_ASSISTANT_PAGE_CONTENT);
1994
1995 child = find_page (assistant, page);
1996
1997 g_return_val_if_fail (child != NULL, GTK_ASSISTANT_PAGE_CONTENT);
1998
1999 page_info = (GtkAssistantPage*) child->data;
2000
2001 return page_info->type;
2002}
2003
2004/**
2005 * gtk_assistant_set_page_complete:
2006 * @assistant: a `GtkAssistant`
2007 * @page: a page of @assistant
2008 * @complete: the completeness status of the page
2009 *
2010 * Sets whether @page contents are complete.
2011 *
2012 * This will make @assistant update the buttons state
2013 * to be able to continue the task.
2014 */
2015void
2016gtk_assistant_set_page_complete (GtkAssistant *assistant,
2017 GtkWidget *page,
2018 gboolean complete)
2019{
2020 GtkAssistantPage *page_info;
2021 GList *child;
2022
2023 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2024 g_return_if_fail (GTK_IS_WIDGET (page));
2025
2026 child = find_page (assistant, page);
2027
2028 g_return_if_fail (child != NULL);
2029
2030 page_info = (GtkAssistantPage*) child->data;
2031
2032 g_object_set (object: page_info, first_property_name: "complete", complete, NULL);
2033}
2034
2035/**
2036 * gtk_assistant_get_page_complete:
2037 * @assistant: a `GtkAssistant`
2038 * @page: a page of @assistant
2039 *
2040 * Gets whether @page is complete.
2041 *
2042 * Returns: %TRUE if @page is complete.
2043 */
2044gboolean
2045gtk_assistant_get_page_complete (GtkAssistant *assistant,
2046 GtkWidget *page)
2047{
2048 GtkAssistantPage *page_info;
2049 GList *child;
2050
2051 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), FALSE);
2052 g_return_val_if_fail (GTK_IS_WIDGET (page), FALSE);
2053
2054 child = find_page (assistant, page);
2055
2056 g_return_val_if_fail (child != NULL, FALSE);
2057
2058 page_info = (GtkAssistantPage*) child->data;
2059
2060 return page_info->complete;
2061}
2062
2063/**
2064 * gtk_assistant_update_buttons_state:
2065 * @assistant: a `GtkAssistant`
2066 *
2067 * Forces @assistant to recompute the buttons state.
2068 *
2069 * GTK automatically takes care of this in most situations,
2070 * e.g. when the user goes to a different page, or when the
2071 * visibility or completeness of a page changes.
2072 *
2073 * One situation where it can be necessary to call this
2074 * function is when changing a value on the current page
2075 * affects the future page flow of the assistant.
2076 */
2077void
2078gtk_assistant_update_buttons_state (GtkAssistant *assistant)
2079{
2080 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2081
2082 update_buttons_state (assistant);
2083}
2084
2085/**
2086 * gtk_assistant_commit:
2087 * @assistant: a `GtkAssistant`
2088 *
2089 * Erases the visited page history.
2090 *
2091 * GTK will then hide the back button on the current page,
2092 * and removes the cancel button from subsequent pages.
2093 *
2094 * Use this when the information provided up to the current
2095 * page is hereafter deemed permanent and cannot be modified
2096 * or undone. For example, showing a progress page to track
2097 * a long-running, unreversible operation after the user has
2098 * clicked apply on a confirmation page.
2099 */
2100void
2101gtk_assistant_commit (GtkAssistant *assistant)
2102{
2103 g_return_if_fail (GTK_IS_ASSISTANT (assistant));
2104
2105 g_slist_free (list: assistant->visited_pages);
2106 assistant->visited_pages = NULL;
2107
2108 assistant->committed = TRUE;
2109
2110 update_buttons_state (assistant);
2111}
2112
2113/* buildable implementation */
2114
2115static GtkBuildableIface *parent_buildable_iface;
2116
2117static void
2118gtk_assistant_buildable_interface_init (GtkBuildableIface *iface)
2119{
2120 parent_buildable_iface = g_type_interface_peek_parent (g_iface: iface);
2121
2122 iface->custom_tag_start = gtk_assistant_buildable_custom_tag_start;
2123 iface->custom_finished = gtk_assistant_buildable_custom_finished;
2124 iface->add_child = gtk_assistant_buildable_add_child;
2125}
2126
2127static void
2128gtk_assistant_buildable_add_child (GtkBuildable *buildable,
2129 GtkBuilder *builder,
2130 GObject *child,
2131 const char *type)
2132{
2133 if (GTK_IS_ASSISTANT_PAGE (child))
2134 gtk_assistant_add_page (GTK_ASSISTANT (buildable), GTK_ASSISTANT_PAGE (child), position: -1);
2135 else if (type && g_str_equal (v1: type, v2: "titlebar"))
2136 {
2137 GtkAssistant *assistant = GTK_ASSISTANT (buildable);
2138 assistant->headerbar = GTK_WIDGET (child);
2139 gtk_window_set_titlebar (GTK_WINDOW (buildable), titlebar: assistant->headerbar);
2140 }
2141 else
2142 parent_buildable_iface->add_child (buildable, builder, child, type);
2143}
2144
2145gboolean
2146gtk_assistant_buildable_custom_tag_start (GtkBuildable *buildable,
2147 GtkBuilder *builder,
2148 GObject *child,
2149 const char *tagname,
2150 GtkBuildableParser *parser,
2151 gpointer *data)
2152{
2153 return parent_buildable_iface->custom_tag_start (buildable, builder, child,
2154 tagname, parser, data);
2155}
2156
2157static void
2158gtk_assistant_buildable_custom_finished (GtkBuildable *buildable,
2159 GtkBuilder *builder,
2160 GObject *child,
2161 const char *tagname,
2162 gpointer user_data)
2163{
2164 parent_buildable_iface->custom_finished (buildable, builder, child,
2165 tagname, user_data);
2166}
2167
2168/**
2169 * gtk_assistant_get_page:
2170 * @assistant: a `GtkAssistant`
2171 * @child: a child of @assistant
2172 *
2173 * Returns the `GtkAssistantPage` object for @child.
2174 *
2175 * Returns: (transfer none): the `GtkAssistantPage` for @child
2176 */
2177GtkAssistantPage *
2178gtk_assistant_get_page (GtkAssistant *assistant,
2179 GtkWidget *child)
2180{
2181 GList *page_info = find_page (assistant, page: child);
2182 return (GtkAssistantPage *) (page_info ? page_info->data : NULL);
2183}
2184
2185/**
2186 * gtk_assistant_page_get_child:
2187 * @page: a `GtkAssistantPage`
2188 *
2189 * Returns the child to which @page belongs.
2190 *
2191 * Returns: (transfer none): the child to which @page belongs
2192 */
2193GtkWidget *
2194gtk_assistant_page_get_child (GtkAssistantPage *page)
2195{
2196 return page->page;
2197}
2198
2199#define GTK_TYPE_ASSISTANT_PAGES (gtk_assistant_pages_get_type ())
2200G_DECLARE_FINAL_TYPE (GtkAssistantPages, gtk_assistant_pages, GTK, ASSISTANT_PAGES, GObject)
2201
2202struct _GtkAssistantPages
2203{
2204 GObject parent_instance;
2205 GtkAssistant *assistant;
2206};
2207
2208struct _GtkAssistantPagesClass
2209{
2210 GObjectClass parent_class;
2211};
2212
2213static GType
2214gtk_assistant_pages_get_item_type (GListModel *model)
2215{
2216 return GTK_TYPE_ASSISTANT_PAGE;
2217}
2218
2219static guint
2220gtk_assistant_pages_get_n_items (GListModel *model)
2221{
2222 GtkAssistantPages *pages = GTK_ASSISTANT_PAGES (ptr: model);
2223
2224 return g_list_length (list: pages->assistant->pages);
2225}
2226
2227static gpointer
2228gtk_assistant_pages_get_item (GListModel *model,
2229 guint position)
2230{
2231 GtkAssistantPages *pages = GTK_ASSISTANT_PAGES (ptr: model);
2232 GtkAssistantPage *page;
2233
2234 page = g_list_nth_data (list: pages->assistant->pages, n: position);
2235
2236 return g_object_ref (page);
2237}
2238
2239static void
2240gtk_assistant_pages_list_model_init (GListModelInterface *iface)
2241{
2242 iface->get_item_type = gtk_assistant_pages_get_item_type;
2243 iface->get_n_items = gtk_assistant_pages_get_n_items;
2244 iface->get_item = gtk_assistant_pages_get_item;
2245}
2246G_DEFINE_TYPE_WITH_CODE (GtkAssistantPages, gtk_assistant_pages, G_TYPE_OBJECT,
2247 G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_assistant_pages_list_model_init))
2248
2249static void
2250gtk_assistant_pages_init (GtkAssistantPages *pages)
2251{
2252}
2253
2254static void
2255gtk_assistant_pages_class_init (GtkAssistantPagesClass *class)
2256{
2257}
2258
2259static GtkAssistantPages *
2260gtk_assistant_pages_new (GtkAssistant *assistant)
2261{
2262 GtkAssistantPages *pages;
2263
2264 pages = g_object_new (GTK_TYPE_ASSISTANT_PAGES, NULL);
2265 pages->assistant = assistant;
2266
2267 return pages;
2268}
2269
2270/**
2271 * gtk_assistant_get_pages:
2272 * @assistant: a `GtkAssistant`
2273 *
2274 * Gets a list model of the assistant pages.
2275 *
2276 * Returns: (transfer full) (attributes element-type=GtkAssistantPage): A list model of the pages.
2277 */
2278GListModel *
2279gtk_assistant_get_pages (GtkAssistant *assistant)
2280{
2281 g_return_val_if_fail (GTK_IS_ASSISTANT (assistant), NULL);
2282
2283 if (assistant->model)
2284 return g_object_ref (assistant->model);
2285
2286 assistant->model = G_LIST_MODEL (ptr: gtk_assistant_pages_new (assistant));
2287
2288 g_object_add_weak_pointer (G_OBJECT (assistant->model), weak_pointer_location: (gpointer *)&assistant->model);
2289
2290 return assistant->model;
2291}
2292

source code of gtk/gtk/gtkassistant.c