/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* -*- encoding: utf8 -*- */ /* * Copyright (C) 2012 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Written by: * Jasper St. Pierre */ #include "config.h" #include #include #include #include "gis-assistant.h" enum { PROP_0, PROP_TITLE, PROP_LAST, }; enum { PAGE_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static GParamSpec *obj_props[PROP_LAST]; struct _GisAssistant { GtkBox parent_instance; GtkWidget *forward; GtkWidget *accept; GtkWidget *skip; GtkWidget *back; GtkWidget *cancel; GtkWidget *spinner; GtkWidget *titlebar; GtkWidget *title; GtkWidget *stack; GList *pages; GisPage *current_page; }; G_DEFINE_TYPE (GisAssistant, gis_assistant, GTK_TYPE_BOX) static void visible_child_changed (GisAssistant *assistant) { g_signal_emit (assistant, signals[PAGE_CHANGED], 0); } static void switch_to (GisAssistant *assistant, GisPage *page) { g_return_if_fail (page != NULL); gtk_stack_set_visible_child (GTK_STACK (assistant->stack), GTK_WIDGET (page)); } static inline gboolean should_show_page (GisPage *page) { return gtk_widget_get_visible (GTK_WIDGET (page)); } static GisPage * find_next_page (GisAssistant *self, GisPage *page) { GList *l = g_list_find (self->pages, page); g_return_val_if_fail (l != NULL, NULL); /* We need the next page */ l = l->next; for (; l != NULL; l = l->next) { GisPage *page = GIS_PAGE (l->data); if (should_show_page (page)) return page; } return NULL; } static void switch_to_next_page (GisAssistant *assistant) { switch_to (assistant, find_next_page (assistant, assistant->current_page)); } static void on_apply_done (GisPage *page, gboolean valid, gpointer user_data) { GisAssistant *assistant = GIS_ASSISTANT (user_data); if (valid) switch_to_next_page (assistant); } void gis_assistant_next_page (GisAssistant *assistant) { if (assistant->current_page) gis_page_apply_begin (assistant->current_page, on_apply_done, assistant); else switch_to_next_page (assistant); } static GisPage * find_prev_page (GisAssistant *self, GisPage *page) { GList *l = g_list_find (self->pages, page); g_return_val_if_fail (l != NULL, NULL); /* We need the previous page */ l = l->prev; for (; l != NULL; l = l->prev) { GisPage *page = GIS_PAGE (l->data); if (should_show_page (page)) return page; } return NULL; } void gis_assistant_previous_page (GisAssistant *assistant) { g_return_if_fail (assistant->current_page != NULL); switch_to (assistant, find_prev_page (assistant, assistant->current_page)); } static void set_suggested_action_sensitive (GtkWidget *widget, gboolean sensitive) { gtk_widget_set_sensitive (widget, sensitive); } static void set_navigation_button (GisAssistant *assistant, GtkWidget *widget) { gtk_widget_set_visible (assistant->forward, (widget == assistant->forward)); gtk_widget_set_visible (assistant->accept, (widget == assistant->accept)); gtk_widget_set_visible (assistant->skip, (widget == assistant->skip)); } void update_navigation_buttons (GisAssistant *assistant) { GisPage *page = assistant->current_page; GList *l; gboolean is_last_page; if (page == NULL) return; l = g_list_find (assistant->pages, page); is_last_page = (l->next == NULL); if (is_last_page) { gtk_widget_hide (assistant->back); gtk_widget_hide (assistant->forward); gtk_widget_hide (assistant->skip); gtk_widget_hide (assistant->cancel); gtk_widget_hide (assistant->accept); } else { gboolean is_first_page; GtkWidget *next_widget; is_first_page = (l->prev == NULL); gtk_widget_set_visible (assistant->back, !is_first_page); if (gis_page_get_needs_accept (page)) next_widget = assistant->accept; else next_widget = assistant->forward; if (gis_page_get_complete (page)) { set_suggested_action_sensitive (next_widget, TRUE); set_navigation_button (assistant, next_widget); } else if (gis_page_get_skippable (page)) { set_navigation_button (assistant, assistant->skip); } else { set_suggested_action_sensitive (next_widget, FALSE); set_navigation_button (assistant, next_widget); } if (gis_page_get_has_forward (page)) { gtk_widget_hide (next_widget); } } } static void update_applying_state (GisAssistant *assistant) { gboolean applying = FALSE; gboolean is_first_page = FALSE; if (assistant->current_page) { applying = gis_page_get_applying (assistant->current_page); is_first_page = assistant->pages->data == assistant->current_page; } gtk_widget_set_sensitive (assistant->forward, !applying); gtk_widget_set_visible (assistant->back, !applying && !is_first_page); gtk_widget_set_visible (assistant->cancel, applying); gtk_widget_set_visible (assistant->spinner, applying); if (applying) gtk_spinner_start (GTK_SPINNER (assistant->spinner)); else gtk_spinner_stop (GTK_SPINNER (assistant->spinner)); } static void update_titlebar (GisAssistant *assistant) { gtk_label_set_label (GTK_LABEL (assistant->title), gis_assistant_get_title (assistant)); } static void page_notify (GisPage *page, GParamSpec *pspec, GisAssistant *assistant) { if (page != assistant->current_page) return; if (strcmp (pspec->name, "title") == 0) { g_object_notify_by_pspec (G_OBJECT (assistant), obj_props[PROP_TITLE]); update_titlebar (assistant); } else if (strcmp (pspec->name, "applying") == 0) { update_applying_state (assistant); } else { update_navigation_buttons (assistant); } } void gis_assistant_add_page (GisAssistant *assistant, GisPage *page) { GList *link; /* Page shouldn't already exist */ g_return_if_fail (!g_list_find (assistant->pages, page)); assistant->pages = g_list_append (assistant->pages, page); link = g_list_last (assistant->pages); link = link->prev; g_signal_connect (page, "notify", G_CALLBACK (page_notify), assistant); gtk_stack_add_child (GTK_STACK (assistant->stack), GTK_WIDGET (page)); /* Update buttons if current page is now the second last page */ if (assistant->current_page && link && link->data == assistant->current_page) update_navigation_buttons (assistant); } void gis_assistant_remove_page (GisAssistant *assistant, GisPage *page) { assistant->pages = g_list_remove (assistant->pages, page); if (page == assistant->current_page) assistant->current_page = NULL; gtk_stack_remove (GTK_STACK (assistant->stack), GTK_WIDGET (page)); } GisPage * gis_assistant_get_current_page (GisAssistant *assistant) { return assistant->current_page; } GList * gis_assistant_get_all_pages (GisAssistant *assistant) { return assistant->pages; } static void go_forward (GtkWidget *button, GisAssistant *assistant) { gis_assistant_next_page (assistant); } static void go_backward (GtkWidget *button, GisAssistant *assistant) { gis_assistant_previous_page (assistant); } static void do_cancel (GtkWidget *button, GisAssistant *assistant) { if (assistant->current_page) gis_page_apply_cancel (assistant->current_page); } const gchar * gis_assistant_get_title (GisAssistant *assistant) { if (assistant->current_page != NULL) return gis_page_get_title (assistant->current_page); else return ""; } GtkWidget * gis_assistant_get_titlebar (GisAssistant *assistant) { return assistant->titlebar; } static void update_current_page (GisAssistant *assistant, GisPage *page) { if (assistant->current_page == page) return; assistant->current_page = page; g_object_notify_by_pspec (G_OBJECT (assistant), obj_props[PROP_TITLE]); update_titlebar (assistant); update_applying_state (assistant); update_navigation_buttons (assistant); gtk_widget_grab_focus (assistant->forward); if (page) gis_page_shown (page); } static void current_page_changed (GObject *gobject, GParamSpec *pspec, gpointer user_data) { GisAssistant *assistant = GIS_ASSISTANT (user_data); GtkStack *stack = GTK_STACK (gobject); GtkWidget *new_page = gtk_stack_get_visible_child (stack); update_current_page (assistant, GIS_PAGE (new_page)); } void gis_assistant_locale_changed (GisAssistant *assistant) { GList *l; gtk_button_set_label (GTK_BUTTON (assistant->forward), _("_Next")); gtk_button_set_label (GTK_BUTTON (assistant->accept), _("_Accept")); gtk_button_set_label (GTK_BUTTON (assistant->skip), _("_Skip")); gtk_button_set_label (GTK_BUTTON (assistant->back), _("_Previous")); gtk_button_set_label (GTK_BUTTON (assistant->cancel), _("_Cancel")); for (l = assistant->pages; l != NULL; l = l->next) gis_page_locale_changed (l->data); update_titlebar (assistant); } gboolean gis_assistant_save_data (GisAssistant *assistant, GError **error) { GList *l; for (l = assistant->pages; l != NULL; l = l->next) { if (!gis_page_save_data (l->data, error)) return FALSE; } return TRUE; } static void gis_assistant_init (GisAssistant *assistant) { gtk_widget_init_template (GTK_WIDGET (assistant)); g_signal_connect (assistant->stack, "notify::visible-child", G_CALLBACK (current_page_changed), assistant); g_signal_connect (assistant->forward, "clicked", G_CALLBACK (go_forward), assistant); g_signal_connect (assistant->accept, "clicked", G_CALLBACK (go_forward), assistant); g_signal_connect (assistant->skip, "clicked", G_CALLBACK (go_forward), assistant); g_signal_connect (assistant->back, "clicked", G_CALLBACK (go_backward), assistant); g_signal_connect (assistant->cancel, "clicked", G_CALLBACK (do_cancel), assistant); gis_assistant_locale_changed (assistant); update_applying_state (assistant); } static void gis_assistant_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GisAssistant *assistant = GIS_ASSISTANT (object); switch (prop_id) { case PROP_TITLE: g_value_set_string (value, gis_assistant_get_title (assistant)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gis_assistant_class_init (GisAssistantClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gtk_widget_class_set_template_from_resource (GTK_WIDGET_CLASS (klass), "/org/gnome/initial-setup/gis-assistant.ui"); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, forward); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, accept); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, skip); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, back); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, cancel); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, spinner); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, titlebar); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, title); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), GisAssistant, stack); gtk_widget_class_bind_template_callback (GTK_WIDGET_CLASS (klass), visible_child_changed); gobject_class->get_property = gis_assistant_get_property; obj_props[PROP_TITLE] = g_param_spec_string ("title", "", "", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * GisAssistant::page-changed: * @assistant: the #GisAssistant * * The ::page-changed signal is emitted when the visible page * changed. */ signals[PAGE_CHANGED] = g_signal_new ("page-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); }