diff options
Diffstat (limited to 'gnome-initial-setup/gis-assistant.c')
-rw-r--r-- | gnome-initial-setup/gis-assistant.c | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/gnome-initial-setup/gis-assistant.c b/gnome-initial-setup/gis-assistant.c new file mode 100644 index 0000000..14887f6 --- /dev/null +++ b/gnome-initial-setup/gis-assistant.c @@ -0,0 +1,538 @@ +/* -*- 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 <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include "config.h" + +#include <glib-object.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#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 _GisAssistantPrivate +{ + GtkWidget *forward; + GtkWidget *accept; + GtkWidget *skip; + GtkWidget *back; + GtkWidget *cancel; + + GtkWidget *main_layout; + GtkWidget *spinner; + GtkWidget *titlebar; + GtkWidget *title; + GtkWidget *stack; + + GList *pages; + GisPage *current_page; +}; +typedef struct _GisAssistantPrivate GisAssistantPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (GisAssistant, gis_assistant, GTK_TYPE_BOX) + +struct _GisAssistantPagePrivate +{ + GList *link; +}; + +static void +visible_child_changed (GisAssistant *assistant) +{ + g_signal_emit (assistant, signals[PAGE_CHANGED], 0); +} + +static void +widget_destroyed (GtkWidget *widget, + GisAssistant *assistant) +{ + GisPage *page = GIS_PAGE (widget); + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + priv->pages = g_list_delete_link (priv->pages, page->assistant_priv->link); + if (page == priv->current_page) + priv->current_page = NULL; + + g_slice_free (GisAssistantPagePrivate, page->assistant_priv); + page->assistant_priv = NULL; +} + +static void +switch_to (GisAssistant *assistant, + GisPage *page) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + g_return_if_fail (page != NULL); + + gtk_stack_set_visible_child (GTK_STACK (priv->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 (GisPage *page) +{ + GList *l = page->assistant_priv->link->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + switch_to (assistant, find_next_page (priv->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + if (priv->current_page) + gis_page_apply_begin (priv->current_page, on_apply_done, assistant); + else + switch_to_next_page (assistant); +} + +static GisPage * +find_prev_page (GisPage *page) +{ + GList *l = page->assistant_priv->link->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + g_return_if_fail (priv->current_page != NULL); + switch_to (assistant, find_prev_page (priv->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + gtk_widget_set_visible (priv->forward, (widget == priv->forward)); + gtk_widget_set_visible (priv->accept, (widget == priv->accept)); + gtk_widget_set_visible (priv->skip, (widget == priv->skip)); +} + +void +update_navigation_buttons (GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + GisPage *page = priv->current_page; + GisAssistantPagePrivate *page_priv; + gboolean is_last_page; + + if (page == NULL) + return; + + page_priv = page->assistant_priv; + + is_last_page = (page_priv->link->next == NULL); + + if (is_last_page) + { + gtk_widget_hide (priv->back); + gtk_widget_hide (priv->forward); + gtk_widget_hide (priv->skip); + gtk_widget_hide (priv->cancel); + gtk_widget_hide (priv->accept); + /* FIXME: workaround for a GTK+ issue */ + gtk_widget_queue_resize (priv->titlebar); + } + else + { + gboolean is_first_page; + GtkWidget *next_widget; + + is_first_page = (page_priv->link->prev == NULL); + gtk_widget_set_visible (priv->back, !is_first_page); + + if (gis_page_get_needs_accept (page)) + next_widget = priv->accept; + else + next_widget = priv->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, priv->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; + + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + if (priv->current_page) + { + applying = gis_page_get_applying (priv->current_page); + is_first_page = priv->current_page->assistant_priv->link->prev == NULL; + } + gtk_widget_set_sensitive (priv->forward, !applying); + gtk_widget_set_visible (priv->back, !applying && !is_first_page); + gtk_widget_set_visible (priv->cancel, applying); + gtk_widget_set_visible (priv->spinner, applying); + + if (applying) + gtk_spinner_start (GTK_SPINNER (priv->spinner)); + else + gtk_spinner_stop (GTK_SPINNER (priv->spinner)); +} + +static void +update_titlebar (GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + gtk_label_set_label (GTK_LABEL (priv->title), + gis_assistant_get_title (assistant)); +} + +static void +page_notify (GisPage *page, + GParamSpec *pspec, + GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + if (page != priv->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + GList *link; + + g_return_if_fail (page->assistant_priv == NULL); + + page->assistant_priv = g_slice_new0 (GisAssistantPagePrivate); + priv->pages = g_list_append (priv->pages, page); + link = page->assistant_priv->link = g_list_last (priv->pages); + + g_signal_connect (page, "destroy", G_CALLBACK (widget_destroyed), assistant); + g_signal_connect (page, "notify", G_CALLBACK (page_notify), assistant); + + gtk_container_add (GTK_CONTAINER (priv->stack), GTK_WIDGET (page)); + + if (priv->current_page && + priv->current_page->assistant_priv->link == link->prev) + update_navigation_buttons (assistant); +} + +GisPage * +gis_assistant_get_current_page (GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + return priv->current_page; +} + +GList * +gis_assistant_get_all_pages (GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + return priv->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + if (priv->current_page) + gis_page_apply_cancel (priv->current_page); +} + +const gchar * +gis_assistant_get_title (GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + if (priv->current_page != NULL) + return gis_page_get_title (priv->current_page); + else + return ""; +} + +GtkWidget * +gis_assistant_get_titlebar (GisAssistant *assistant) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + return priv->titlebar; +} + +static void +update_current_page (GisAssistant *assistant, + GisPage *page) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + if (priv->current_page == page) + return; + + priv->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 (priv->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + GList *l; + + gtk_button_set_label (GTK_BUTTON (priv->forward), _("_Next")); + gtk_button_set_label (GTK_BUTTON (priv->accept), _("_Accept")); + gtk_button_set_label (GTK_BUTTON (priv->skip), _("_Skip")); + gtk_button_set_label (GTK_BUTTON (priv->back), _("_Previous")); + gtk_button_set_label (GTK_BUTTON (priv->cancel), _("_Cancel")); + + for (l = priv->pages; l != NULL; l = l->next) + gis_page_locale_changed (l->data); + + update_titlebar (assistant); +} + +gboolean +gis_assistant_save_data (GisAssistant *assistant, + GError **error) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + GList *l; + + for (l = priv->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) +{ + GisAssistantPrivate *priv = gis_assistant_get_instance_private (assistant); + + gtk_widget_init_template (GTK_WIDGET (assistant)); + + g_signal_connect (priv->stack, "notify::visible-child", + G_CALLBACK (current_page_changed), assistant); + + g_signal_connect (priv->forward, "clicked", G_CALLBACK (go_forward), assistant); + g_signal_connect (priv->accept, "clicked", G_CALLBACK (go_forward), assistant); + g_signal_connect (priv->skip, "clicked", G_CALLBACK (go_forward), assistant); + + g_signal_connect (priv->back, "clicked", G_CALLBACK (go_backward), assistant); + g_signal_connect (priv->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_private (GTK_WIDGET_CLASS (klass), GisAssistant, forward); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, accept); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, skip); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, back); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, cancel); + + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, main_layout); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, spinner); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, titlebar); + gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), GisAssistant, title); + gtk_widget_class_bind_template_child_private (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); +} |