[PATCH 05/10] Add a class BaseStandalone.

David Shea dshea at redhat.com
Mon Jun 30 19:41:11 UTC 2014


This class is an abstract class containing the shared parts of HubWindow
and StandaloneWindow. It provides quit-button and continue-button
properties, quit-clicked and continue-clicked signals emitted when the
buttons are pressed, and the sidebar sizing and drawing methods.

Glade files using StandaloneWindow do not require any change. Glade files
using HubWindow may need to be modified to connect the quit-button or
continue-button properties.

On the python side, this removes the need for the register_event_cb
proxy for quit and continue events. Connect GraphicalUserInterface
directly to the now-shared signals.
---
 po/POTFILES.in                        |   1 +
 pyanaconda/ui/gui/__init__.py         |  17 +-
 pyanaconda/ui/gui/hubs/__init__.py    |  27 +--
 pyanaconda/ui/gui/hubs/progress.glade |   5 +-
 pyanaconda/ui/gui/hubs/progress.py    |  11 +-
 pyanaconda/ui/gui/hubs/summary.glade  |   6 +-
 pyanaconda/ui/gui/hubs/summary.py     |   7 -
 pyanaconda/ui/gui/spokes/__init__.py  |  12 +-
 pyanaconda/ui/gui/spokes/network.py   |   4 +-
 pyanaconda/ui/gui/spokes/welcome.py   |   4 +-
 pyanaconda/ui/gui/tools/run-spoke.py  |   7 +-
 widgets/doc/AnacondaWidgets-docs.xml  |   1 +
 widgets/glade/AnacondaWidgets.xml     |  10 +
 widgets/src/BaseStandalone.c          | 377 ++++++++++++++++++++++++++++++++++
 widgets/src/BaseStandalone.h          |  81 ++++++++
 widgets/src/HubWindow.c               |  83 +-------
 widgets/src/HubWindow.h               |   6 +-
 widgets/src/Makefile.am               |   2 +
 widgets/src/StandaloneWindow.c        | 221 +++++---------------
 widgets/src/StandaloneWindow.h        |  15 +-
 20 files changed, 569 insertions(+), 328 deletions(-)
 create mode 100644 widgets/src/BaseStandalone.c
 create mode 100644 widgets/src/BaseStandalone.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 943d15a..fc832df 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -99,6 +99,7 @@ pyanaconda/ui/gui/spokes/lib/summary.py
 
 # Custom widgets.
 widgets/src/BaseWindow.c
+widgets/src/BaseStandalone.c
 widgets/src/DiskOverview.c
 widgets/src/HubWindow.c
 widgets/src/MountpointSelector.c
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py
index 2a1dad2..604a3b5 100644
--- a/pyanaconda/ui/gui/__init__.py
+++ b/pyanaconda/ui/gui/__init__.py
@@ -370,8 +370,9 @@ class GraphicalUserInterface(UserInterface):
             del(obj)
             return None
 
-        obj.register_event_cb("continue", self._on_continue_clicked)
-        obj.register_event_cb("quit", self._on_quit_clicked)
+        # Use connect_after so classes can add actions before we change screens
+        obj.window.connect_after("continue-clicked", self._on_continue_clicked)
+        obj.window.connect_after("quit-clicked", self._on_quit_clicked)
 
         return obj
 
@@ -471,7 +472,10 @@ class GraphicalUserInterface(UserInterface):
     ###
     ### SIGNAL HANDLING METHODS
     ###
-    def _on_continue_clicked(self):
+    def _on_continue_clicked(self, win, user_data=None):
+        if not win.get_may_continue():
+            return
+
         # If we're on the last screen, clicking Continue quits.
         if len(self._actions) == 1:
             Gtk.main_quit()
@@ -515,7 +519,7 @@ class GraphicalUserInterface(UserInterface):
         if not nextAction.showable:
             self._currentAction.window.hide()
             self._actions.pop(0)
-            self._on_continue_clicked()
+            self._on_continue_clicked(nextAction)
             return
 
         self._currentAction.exit_logger()
@@ -530,7 +534,10 @@ class GraphicalUserInterface(UserInterface):
         self._currentAction = nextAction
         self._actions.pop(0)
 
-    def _on_quit_clicked(self):
+    def _on_quit_clicked(self, win, userData=None):
+        if not win.get_quit_button():
+            return
+
         dialog = self._quitDialog(None)
         with enlightbox(self._currentAction.window, dialog.window):
             rc = dialog.run()
diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py
index b42bd91..3645759 100644
--- a/pyanaconda/ui/gui/hubs/__init__.py
+++ b/pyanaconda/ui/gui/hubs/__init__.py
@@ -273,10 +273,7 @@ class Hub(GUIObject, common.Hub):
         return len(self._incompleteSpokes) == 0 and len(self._notReadySpokes) == 0 and getattr(self._checker, "success", True)
 
     def _updateContinueButton(self):
-        if not self.continueButton:
-            return
-
-        self.continueButton.set_sensitive(self.continuePossible)
+        self.window.set_may_continue(self.continuePossible)
 
     def _update_spokes(self):
         from pyanaconda.ui.communication import hubQ
@@ -284,10 +281,10 @@ class Hub(GUIObject, common.Hub):
 
         q = hubQ.q
 
-        if not self._spokes and self.continueButton:
+        if not self._spokes and self.window.get_may_continue():
             # no spokes, move on
             log.info("no spokes available on %s, continuing automatically", self)
-            gtk_call_once(self.continueButton.emit, "clicked")
+            gtk_call_once(self.window.emit, "continue-clicked")
 
         click_continue = False
         # Grab all messages that may have appeared since last time this method ran.
@@ -347,10 +344,10 @@ class Hub(GUIObject, common.Hub):
             q.task_done()
 
         # queue is now empty, should continue be clicked?
-        if self._autoContinue and click_continue and self.continueButton:
+        if self._autoContinue and click_continue and self.window.get_may_continue():
             # enqueue the emit to the Gtk message queue
             log.info("_autoContinue clicking continue button")
-            gtk_call_once(self.continueButton.emit, "clicked")
+            gtk_call_once(self.window.emit, "continue-clicked")
 
         return True
 
@@ -360,22 +357,8 @@ class Hub(GUIObject, common.Hub):
 
         GLib.timeout_add(100, self._update_spokes)
 
-    @property
-    def continueButton(self):
-        return None
-
-    @property
-    def quitButton(self):
-        return None
-
     ### SIGNAL HANDLERS
 
-    def register_event_cb(self, event, cb):
-        if event == "continue" and self.continueButton:
-            self.continueButton.connect("clicked", lambda *args: cb())
-        elif event == "quit" and self.quitButton:
-            self.quitButton.connect("clicked", lambda *args: cb())
-
     def _on_spoke_clicked(self, selector, event, spoke):
         from gi.repository import Gdk
 
diff --git a/pyanaconda/ui/gui/hubs/progress.glade b/pyanaconda/ui/gui/hubs/progress.glade
index b15e253..1dd82f4 100644
--- a/pyanaconda/ui/gui/hubs/progress.glade
+++ b/pyanaconda/ui/gui/hubs/progress.glade
@@ -1,11 +1,12 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.18.1 -->
+<!-- Generated with glade 3.18.3 -->
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <requires lib="AnacondaWidgets" version="1.0"/>
+  <requires lib="AnacondaWidgets" version="3.0"/>
   <object class="AnacondaHubWindow" id="progressWindow">
     <property name="can_focus">False</property>
     <property name="window_name" translatable="yes">CONFIGURATION</property>
+    <property name="continue_button">rebootButton</property>
     <child internal-child="main_box">
       <object class="GtkBox" id="AnacondaHubWindow-main_box1">
         <property name="can_focus">False</property>
diff --git a/pyanaconda/ui/gui/hubs/progress.py b/pyanaconda/ui/gui/hubs/progress.py
index 1c33abd..2fa9dac 100644
--- a/pyanaconda/ui/gui/hubs/progress.py
+++ b/pyanaconda/ui/gui/hubs/progress.py
@@ -133,8 +133,7 @@ class ProgressHub(Hub):
 
         # kickstart install, continue automatically if reboot or shutdown selected
         if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]:
-            self.continueButton.emit("clicked")
-
+            self.window.emit("continue-clicked")
 
     def _install_done(self):
         # package installation done, check personalization spokes
@@ -208,7 +207,7 @@ class ProgressHub(Hub):
             continueText.set_text(_("%s is now successfully installed on your system and ready "
                                     "for you to use!  When you are ready, reboot your system to start using it!"))
             continueText.set_line_wrap(True)
-            self.continueButton.set_label(C_("GUI|Progress", "_Quit"))
+            self.window.get_continue_button().set_label(C_("GUI|Progress", "_Quit"))
 
         self._progressBar = self.builder.get_object("progressBar")
         self._progressLabel = self.builder.get_object("progressLabel")
@@ -258,14 +257,10 @@ class ProgressHub(Hub):
 
     def _updateContinueButton(self):
         if self._configurationDone:
-            self.continueButton.set_sensitive(self.continuePossible)
+            self.window.set_may_continue(self.continuePossible)
         else:
             self.builder.get_object("configureButton").set_sensitive(self.continuePossible)
 
-    @property
-    def continueButton(self):
-        return self.builder.get_object("rebootButton")
-
     def _init_progress_bar(self, steps):
         self._totalSteps = steps
         self._currentStep = 0
diff --git a/pyanaconda/ui/gui/hubs/summary.glade b/pyanaconda/ui/gui/hubs/summary.glade
index 3d896a9..cae6cb7 100644
--- a/pyanaconda/ui/gui/hubs/summary.glade
+++ b/pyanaconda/ui/gui/hubs/summary.glade
@@ -1,11 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.18.1 -->
+<!-- Generated with glade 3.18.3 -->
 <interface>
   <requires lib="gtk+" version="3.0"/>
-  <requires lib="AnacondaWidgets" version="1.0"/>
+  <requires lib="AnacondaWidgets" version="3.0"/>
   <object class="AnacondaHubWindow" id="summaryWindow">
     <property name="can_focus">False</property>
     <property name="window_name" translatable="yes">INSTALLATION SUMMARY</property>
+    <property name="quit_button">quitButton</property>
+    <property name="continue_button">continueButton</property>
     <child internal-child="main_box">
       <object class="GtkBox" id="AnacondaHubWindow-main_box1">
         <property name="can_focus">False</property>
diff --git a/pyanaconda/ui/gui/hubs/summary.py b/pyanaconda/ui/gui/hubs/summary.py
index 2615377..28f93be 100644
--- a/pyanaconda/ui/gui/hubs/summary.py
+++ b/pyanaconda/ui/gui/hubs/summary.py
@@ -59,10 +59,3 @@ class SummaryHub(Hub):
         else:
             self._checker = DirInstallSpaceChecker(storage, payload)
 
-    @property
-    def continueButton(self):
-        return self.builder.get_object("continueButton")
-
-    @property
-    def quitButton(self):
-        return self.builder.get_object("quitButton")
diff --git a/pyanaconda/ui/gui/spokes/__init__.py b/pyanaconda/ui/gui/spokes/__init__.py
index 658e6f7..9a620b6 100644
--- a/pyanaconda/ui/gui/spokes/__init__.py
+++ b/pyanaconda/ui/gui/spokes/__init__.py
@@ -67,15 +67,11 @@ class StandaloneSpoke(Spoke, common.StandaloneSpoke):
         Spoke.__init__(self, data)
         common.StandaloneSpoke.__init__(self, data, storage, payload, instclass)
 
-    def _on_continue_clicked(self, cb):
+        # Add a continue-clicked handler to save the data before leaving the window
+        self.window.connect("continue-clicked", self._on_continue_clicked)
+
+    def _on_continue_clicked(self, win, user_data=None):
         self.apply()
-        cb()
-
-    def register_event_cb(self, event, cb):
-        if event == "continue":
-            self.window.connect("continue-clicked", lambda *args: self._on_continue_clicked(cb))
-        elif event == "quit":
-            self.window.connect("quit-clicked", lambda *args: cb())
 
 # Inherit abstract methods from common.NormalSpoke
 # pylint: disable=abstract-method
diff --git a/pyanaconda/ui/gui/spokes/network.py b/pyanaconda/ui/gui/spokes/network.py
index 2275c6e..73390e1 100644
--- a/pyanaconda/ui/gui/spokes/network.py
+++ b/pyanaconda/ui/gui/spokes/network.py
@@ -1446,7 +1446,7 @@ class NetworkStandaloneSpoke(StandaloneSpoke):
         StandaloneSpoke.refresh(self)
         self.network_control_box.refresh()
 
-    def _on_continue_clicked(self, cb):
+    def _on_continue_clicked(self, window, user_data=None):
         hostname = self.network_control_box.hostname
         (valid, error) = network.sanityCheckHostname(hostname)
         if not valid:
@@ -1457,7 +1457,7 @@ class NetworkStandaloneSpoke(StandaloneSpoke):
             self.window.show_all()
         else:
             self.clear_info()
-            StandaloneSpoke._on_continue_clicked(self, cb)
+            StandaloneSpoke._on_continue_clicked(self, window, user_data)
 
     # Use case: slow dhcp has connected when on spoke
     def on_nm_state_changed(self, *args):
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index 31603b8..5c0ff83 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -305,7 +305,7 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
 
     # Override the default in StandaloneSpoke so we can display the beta
     # warning dialog first.
-    def _on_continue_clicked(self, cb):
+    def _on_continue_clicked(self, window, user_data=None):
         # Don't display the betanag dialog if this is the final release.
         if not isFinal:
             dlg = self.builder.get_object("betaWarnDialog")
@@ -324,4 +324,4 @@ class WelcomeLanguageSpoke(LangLocaleHandler, StandaloneSpoke):
             if rc != 1:
                 sys.exit(0)
 
-        StandaloneSpoke._on_continue_clicked(self, cb)
+        StandaloneSpoke._on_continue_clicked(self, window, user_data)
diff --git a/pyanaconda/ui/gui/tools/run-spoke.py b/pyanaconda/ui/gui/tools/run-spoke.py
index 4aa3d96..4edaacd 100755
--- a/pyanaconda/ui/gui/tools/run-spoke.py
+++ b/pyanaconda/ui/gui/tools/run-spoke.py
@@ -96,10 +96,11 @@ instclass = DefaultInstall()
 payload = YumPayload(ksdata)
 payload.setup(storage)
 
+from pyanaconda.ui.gui.spokes import StandaloneSpoke
 spoke = spokeClass(ksdata, storage, payload, instclass)
-if hasattr(spoke, "register_event_cb"):
-    spoke.register_event_cb("continue", Gtk.main_quit)
-    spoke.register_event_cb("quit", Gtk.main_quit)
+if isinstance(spoke, StandaloneSpoke):
+    spoke.window.connect_after("continue-clicked", Gtk.main_quit)
+    spoke.window.connect("quit-clicked", Gtk.main_quit)
 
 if hasattr(spoke, "set_path"):
     spoke.set_path("categories", [
diff --git a/widgets/doc/AnacondaWidgets-docs.xml b/widgets/doc/AnacondaWidgets-docs.xml
index 5588b07..7282a24 100644
--- a/widgets/doc/AnacondaWidgets-docs.xml
+++ b/widgets/doc/AnacondaWidgets-docs.xml
@@ -24,6 +24,7 @@
     <chapter id="windows">
         <title>Window Widgets</title>
         <xi:include href="xml/BaseWindow.xml" />
+        <xi:include href="xml/BaseStandalone.xml" />
         <xi:include href="xml/HubWindow.xml" />
         <xi:include href="xml/SpokeWindow.xml" />
         <xi:include href="xml/StandaloneWindow.xml" />
diff --git a/widgets/glade/AnacondaWidgets.xml b/widgets/glade/AnacondaWidgets.xml
index b061609..42c4194 100644
--- a/widgets/glade/AnacondaWidgets.xml
+++ b/widgets/glade/AnacondaWidgets.xml
@@ -56,6 +56,16 @@
             <!-- Not a typo - we really can use the same functions here (for now) -->
             <post-create-function>anaconda_standalone_window_post_create</post-create-function>
 
+            <properties>
+              <property since="3.0" id="quit-button" />
+              <property since="3.0" id="continue-button" />
+            </properties>
+
+            <signals>
+              <signal since="3.0" id="quit-clicked" />
+              <signal since="3.0" id="continue-clicked" />
+            </signals>
+
             <internal-children>
                 <object name="main_box">
                     <object name="nav_box">
diff --git a/widgets/src/BaseStandalone.c b/widgets/src/BaseStandalone.c
new file mode 100644
index 0000000..ab4bb05
--- /dev/null
+++ b/widgets/src/BaseStandalone.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014  Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author: David Shea <dshea at redhat.com>
+ */
+
+#include "BaseWindow.h"
+#include "BaseStandalone.h"
+#include "intl.h"
+
+/**
+ * SECTION: BaseStandalone
+ * @title: AnacondaBaseStandalone
+ * @short_description: Abstract base class for standalone Anaconda windows.
+ *
+ * #AnacondaBaseStandalone is an abstract base class for standalone windows
+ * in Anaconda; i.e., windows that do not appear in or depend upon a hub.
+ * A #AnacondaBaseStandalone can continue to the next #AnacondaBaseStandalone
+ * or quit the installer.
+ *
+ * Since: 3.0
+ */
+
+#define STANDALONE_SIDEBAR_WIDTH_PCT    (0.15)
+
+enum {
+    SIGNAL_QUIT_CLICKED,
+    SIGNAL_CONTINUE_CLICKED,
+    LAST_SIGNAL
+};
+
+static guint standalone_signals[LAST_SIGNAL] = { 0 };
+
+enum {
+    PROP_QUIT_BUTTON = 1,
+    PROP_CONTINUE_BUTTON
+};
+
+struct _AnacondaBaseStandalonePrivate {
+    GtkWidget *quit_button, *continue_button;
+    gulong quit_clicked_handler_id, continue_clicked_handler_id;
+};
+
+static void anaconda_base_standalone_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
+static void anaconda_base_standalone_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
+static void anaconda_base_standalone_size_allocate(GtkWidget *window, GtkAllocation *allocation);
+static gboolean anaconda_base_standalone_on_draw(GtkWidget *window, cairo_t *cr);
+static void anaconda_base_standalone_dispose(GObject *object);
+static void anaconda_base_standalone_set_quit_button(AnacondaBaseStandalone *win, GtkButton *button);
+static void anaconda_base_standalone_set_continue_button(AnacondaBaseStandalone *win, GtkButton *button);
+static void anaconda_base_standalone_quit_clicked(GtkButton *button, gpointer user_data);
+static void anaconda_base_standalone_continue_clicked(GtkButton *button, gpointer user_data);
+
+G_DEFINE_ABSTRACT_TYPE(AnacondaBaseStandalone, anaconda_base_standalone, ANACONDA_TYPE_BASE_WINDOW)
+
+static void anaconda_base_standalone_class_init(AnacondaBaseStandaloneClass *klass) {
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+    object_class->set_property = anaconda_base_standalone_set_property;
+    object_class->get_property = anaconda_base_standalone_get_property;
+    object_class->dispose = anaconda_base_standalone_dispose;
+
+    widget_class->draw = anaconda_base_standalone_on_draw;
+    widget_class->size_allocate = anaconda_base_standalone_size_allocate;
+
+    /**
+     * AnacondaBaseStandalone::quit-clicked:
+     * @window: the window that received the signal
+     *
+     * Emitted when the quit button has been activated (pressed and released).
+     *
+     * Since: 3.0
+     */
+    standalone_signals[SIGNAL_QUIT_CLICKED] = g_signal_new("quit-clicked",
+                                                           G_TYPE_FROM_CLASS(object_class),
+                                                           G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                                                           G_STRUCT_OFFSET(AnacondaBaseStandaloneClass, quit_clicked),
+                                                           NULL, NULL,
+                                                           g_cclosure_marshal_VOID__VOID,
+                                                           G_TYPE_NONE, 0);
+
+    /**
+     * AnacondaBaseStandalone::continue-clicked:
+     * @window: the window that received the signal
+     *
+     * Emitted when the continue button has been activated (pressed and released).
+     *
+     * Since: 3.0
+     */
+    standalone_signals[SIGNAL_CONTINUE_CLICKED] = g_signal_new("continue-clicked",
+                                                               G_TYPE_FROM_CLASS(object_class),
+                                                               G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
+                                                               G_STRUCT_OFFSET(AnacondaBaseStandaloneClass, continue_clicked),
+                                                               NULL, NULL,
+                                                               g_cclosure_marshal_VOID__VOID,
+                                                               G_TYPE_NONE, 0);
+
+    /*
+     * These two properties can't be CONSTRUCT_ONLY, since GtkBuilder will normally
+     * set them after construction. For a window that sets its quit-button or continue-button
+     * property to a GtkButton defined as a child of the window, Builder will construct
+     * the window, then construct the Button and set the property on the window.
+     */
+
+    /**
+     * AnacondaBaseStandalone::quit-button:
+     *
+     * The button to quit anaconda.
+     *
+     * Since: 3.0
+     */
+    g_object_class_install_property(object_class,
+                                    PROP_QUIT_BUTTON,
+                                    g_param_spec_object("quit-button",
+                                                        P_("Quit button"),
+                                                        P_("The button to quit Anaconda"),
+                                                        GTK_TYPE_BUTTON,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+    /**
+     * AnacondaBaseStandalone::continue-button:
+     *
+     * The button to continue to the next window.
+     *
+     * Since: 3.0
+     */
+    g_object_class_install_property(object_class,
+                                    PROP_CONTINUE_BUTTON,
+                                    g_param_spec_object("continue-button",
+                                                        P_("Continue button"),
+                                                        P_("The button to continue to the next window"),
+                                                        GTK_TYPE_BUTTON,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+    g_type_class_add_private(object_class, sizeof(AnacondaBaseStandalonePrivate));
+}
+
+static void anaconda_base_standalone_init(AnacondaBaseStandalone *win) {
+    win->priv = G_TYPE_INSTANCE_GET_PRIVATE(win,
+                                            ANACONDA_TYPE_BASE_STANDALONE,
+                                            AnacondaBaseStandalonePrivate);
+}
+
+static void anaconda_base_standalone_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
+    AnacondaBaseStandalone *win = ANACONDA_BASE_STANDALONE(object);
+    AnacondaBaseStandalonePrivate *priv = win->priv;
+
+    switch (prop_id) {
+        case PROP_QUIT_BUTTON:
+            g_value_set_object(value, priv->quit_button);
+            break;
+        case PROP_CONTINUE_BUTTON:
+            g_value_set_object(value, priv->continue_button);
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+            break;
+    }
+}
+
+static void anaconda_base_standalone_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
+    AnacondaBaseStandalone *win = ANACONDA_BASE_STANDALONE(object);
+
+    switch (prop_id) {
+        case PROP_QUIT_BUTTON:
+            anaconda_base_standalone_set_quit_button(win, g_value_get_object(value));
+            break;
+        case PROP_CONTINUE_BUTTON:
+            anaconda_base_standalone_set_continue_button(win, g_value_get_object(value));
+            break;
+        default:
+            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+            break;
+    }
+}
+
+static int get_sidebar_width(GtkWidget *window) {
+    GtkAllocation allocation;
+
+    /* change value below to make sidebar bigger / smaller */
+    float sidebar_width_percentage = STANDALONE_SIDEBAR_WIDTH_PCT;
+
+    gtk_widget_get_allocation(window, &allocation);
+    return allocation.width * sidebar_width_percentage;
+}
+
+static int get_sidebar_height(GtkWidget *window) {
+    GtkAllocation allocation;
+    gtk_widget_get_allocation(window, &allocation);
+    return allocation.height;
+}
+
+/* Move base window content appropriate amount of space to make room for sidebar */
+static void anaconda_base_standalone_size_allocate(GtkWidget *window, GtkAllocation *allocation) {
+    GtkAllocation child_allocation;
+    GtkWidget *child;
+    int sidebar_width;
+
+    GTK_WIDGET_CLASS(anaconda_base_standalone_parent_class)->size_allocate(window, allocation);
+
+    /*
+     * For RTL languages, the width is reduced by the same amount, but the
+     * start of the window does not need to move.
+     */
+    gtk_widget_set_allocation(window, allocation);
+    sidebar_width = get_sidebar_width(window);
+    child_allocation.y = allocation->y;
+    child_allocation.width = allocation->width-sidebar_width;
+    child_allocation.height = allocation->height;
+
+    if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR)
+        child_allocation.x = allocation->x+sidebar_width;
+    else
+        child_allocation.x = allocation->x;
+
+    child = gtk_bin_get_child (GTK_BIN (window));
+    if (child && gtk_widget_get_visible (child))
+        gtk_widget_size_allocate (child, &child_allocation);
+}
+
+/* function to override default drawing to insert sidebar image */
+static gboolean anaconda_base_standalone_on_draw(GtkWidget *win, cairo_t *cr) {
+    GtkStyleContext *context;
+    gdouble sidebar_x;
+    gdouble sidebar_width;
+
+    /* calls parent class' draw handler */
+    GTK_WIDGET_CLASS(anaconda_base_standalone_parent_class)->draw(win,cr);
+
+    sidebar_width = get_sidebar_width(win);
+
+    /* For RTL languages, move the sidebar to the right edge */
+    if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) {
+        sidebar_x = 0;
+    } else {
+        GtkAllocation allocation;
+        gtk_widget_get_allocation(win, &allocation);
+        sidebar_x = allocation.width - sidebar_width;
+    }
+
+    context = gtk_widget_get_style_context(win);
+    gtk_style_context_save (context);
+
+    gtk_style_context_add_class(context, "logo-sidebar");
+    gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win));
+    gtk_style_context_remove_class(context, "logo-sidebar");
+
+    gtk_style_context_add_class(context, "logo");
+    gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win));
+    gtk_style_context_remove_class(context, "logo");
+
+    gtk_style_context_restore (context);
+
+    return TRUE; /* TRUE to avoid default draw handler */
+}
+
+static void anaconda_base_standalone_dispose(GObject *object) {
+    AnacondaBaseStandalone *win = ANACONDA_BASE_STANDALONE(object);
+
+    anaconda_base_standalone_set_quit_button(win, NULL);
+    anaconda_base_standalone_set_continue_button(win, NULL);
+
+    G_OBJECT_CLASS(anaconda_base_standalone_parent_class)->dispose(object);
+}
+
+static void anaconda_base_standalone_set_quit_button(AnacondaBaseStandalone *win, GtkButton *button) {
+    AnacondaBaseStandalonePrivate *priv = win->priv;
+
+    if (priv->quit_button) {
+        g_signal_handler_disconnect(priv->quit_button, priv->quit_clicked_handler_id);
+        g_object_unref(priv->quit_button);
+    }
+
+    priv->quit_button = GTK_WIDGET(button);
+
+    if (priv->quit_button) {
+        g_object_ref(priv->quit_button);
+        priv->quit_clicked_handler_id = g_signal_connect(priv->quit_button, "clicked", G_CALLBACK(anaconda_base_standalone_quit_clicked), win);
+    }
+}
+
+static void anaconda_base_standalone_set_continue_button(AnacondaBaseStandalone *win, GtkButton *button) {
+    AnacondaBaseStandalonePrivate *priv = win->priv;
+
+    if (priv->continue_button) {
+        g_signal_handler_disconnect(priv->continue_button, priv->continue_clicked_handler_id);
+        g_object_unref(priv->continue_button);
+    }
+
+    priv->continue_button = GTK_WIDGET(button);
+
+    if (priv->continue_button) {
+        g_object_ref(priv->continue_button);
+        priv->continue_clicked_handler_id = g_signal_connect(priv->continue_button, "clicked", G_CALLBACK(anaconda_base_standalone_continue_clicked), win);
+    }
+}
+
+static void anaconda_base_standalone_quit_clicked(GtkButton *button, gpointer user_data) {
+    g_signal_emit(user_data, standalone_signals[SIGNAL_QUIT_CLICKED], 0);
+}
+
+static void anaconda_base_standalone_continue_clicked(GtkButton *button, gpointer user_data) {
+    g_signal_emit(user_data, standalone_signals[SIGNAL_CONTINUE_CLICKED], 0);
+}
+
+/**
+ * anaconda_base_standalone_get_may_continue:
+ * @win: a #AnacondaBaseStandalone
+ *
+ * Returns: Whether or not the continue button is sensitive (thus, whether the
+ * user may continue forward from this window).
+ *
+ * Since: 3.0
+ */
+gboolean anaconda_base_standalone_get_may_continue(AnacondaBaseStandalone *win) {
+    if (win->priv->continue_button) {
+        return gtk_widget_get_sensitive(win->priv->continue_button);
+    }
+    return FALSE;
+}
+
+/**
+ * anaconda_base_standalone_set_may_continue:
+ * @win: a #AnacondaBaseStandalone
+ * @may_continue: %TRUE if this window's continue buttons should be sensitive
+ *
+ * Specifies whether the user may continue forward from this window.  If so,
+ * the continue button will be made sensitive.  Windows default to continuable
+ * so you must set it as false if you want.  The reason the user may not be
+ * able to continue is if there is required information the user must enter
+ * when no reasonable default may be given.
+ *
+ * Since: 3.0
+ */
+void anaconda_base_standalone_set_may_continue(AnacondaBaseStandalone *win, gboolean may_continue) {
+    if (win->priv->continue_button) {
+        gtk_widget_set_sensitive(win->priv->continue_button, may_continue);
+    }
+}
+
+/**
+ * anaconda_base_standalone_get_quit_button:
+ * @win: a #AnacondaBaseStandalone
+ *
+ * Returns: (transfer none): the quit button
+ *
+ * Since: 3.0
+ */
+GtkButton * anaconda_base_standalone_get_quit_button(AnacondaBaseStandalone *win) {
+    return GTK_BUTTON(win->priv->quit_button);
+}
+
+/**
+ * anaconda_base_standalone_get_continue_button:
+ * @win: a #AnacondaBaseStandalone
+ *
+ * Returns: (transfer none): the continue button
+ *
+ * Since: 3.0
+ */
+GtkButton * anaconda_base_standalone_get_continue_button(AnacondaBaseStandalone *win) {
+    return GTK_BUTTON(win->priv->continue_button);
+}
diff --git a/widgets/src/BaseStandalone.h b/widgets/src/BaseStandalone.h
new file mode 100644
index 0000000..5f2e663
--- /dev/null
+++ b/widgets/src/BaseStandalone.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014  Red Hat, Inc.
+ *
+ * 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/>.
+ *
+ * Author: David Shea <dshea at redhat.com>
+ */
+
+#ifndef _BASE_STANDALONE_H
+#define _BASE_STANDALONE_H
+
+#include <gtk/gtk.h>
+
+#include "BaseWindow.h"
+
+G_BEGIN_DECLS
+
+#define ANACONDA_TYPE_BASE_STANDALONE            (anaconda_base_standalone_get_type())
+#define ANACONDA_BASE_STANDALONE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANACONDA_TYPE_BASE_STANDALONE, AnacondaBaseStandalone))
+#define ANACONDA_IS_BASE_STANDALONE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANACONDA_TYPE_BASE_STANDALONE))
+#define ANACONDA_BASE_STANDALONE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), ANACONDA_TYPE_BASE_STANDALONE, AnacondaBaseStandaloneClass))
+#define ANACONDA_IS_BASE_STANDALONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANACONDA_TYPE_BASE_STANDALONE))
+#define ANACONDA_BASE_STANDALONE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), ANACONDA_TYPE_BASE_STANDALONE, AnacondaBaseStandaloneClass))
+
+typedef struct _AnacondaBaseStandalone        AnacondaBaseStandalone;
+typedef struct _AnacondaBaseStandaloneClass   AnacondaBaseStandaloneClass;
+typedef struct _AnacondaBaseStandalonePrivate AnacondaBaseStandalonePrivate;
+
+/**
+ * AnacondaBaseStandalone:
+ *
+ * The AnacondaBaseStandalone class contains only private fields and should not
+ * be directly accessed.
+ */
+struct _AnacondaBaseStandalone {
+    AnacondaBaseWindow             parent;
+
+    /*< private >*/
+    AnacondaBaseStandalonePrivate *priv;
+};
+
+/**
+ * AnacondaBaseStandaloneClass:
+ * @parent_class: The object class structure needs to be the first element in
+ *                the widget class structure in order for the class mechanism
+ *                to work correctly. This allows a AnacondaBaseStandaloneClass
+ *                pointer to be cast to a #AnacondaBaseWindowClass pointer.
+ * @quit_clicked: Function pointer called when the #AnacondaBaseStandalone::quit-clicked
+ *                signal is emitted.
+ * @continue_clicked: Function pointer called when the #AnacondaBaseStandalone::continue-clicked
+ *                    signal is emitted.
+ */
+struct _AnacondaBaseStandaloneClass {
+    AnacondaBaseWindowClass parent_class;
+
+    void (* quit_clicked)     (AnacondaBaseStandalone *window);
+    void (* continue_clicked) (AnacondaBaseStandalone *window);
+};
+
+GType       anaconda_base_standalone_get_type(void);
+
+gboolean    anaconda_base_standalone_get_may_continue(AnacondaBaseStandalone *win);
+void        anaconda_base_standalone_set_may_continue(AnacondaBaseStandalone *win, gboolean may_continue);
+
+GtkButton * anaconda_base_standalone_get_quit_button(AnacondaBaseStandalone *win);
+GtkButton * anaconda_base_standalone_get_continue_button(AnacondaBaseStandalone *win);
+
+G_END_DECLS
+
+#endif
diff --git a/widgets/src/HubWindow.c b/widgets/src/HubWindow.c
index d1b4e56..927dd3d 100644
--- a/widgets/src/HubWindow.c
+++ b/widgets/src/HubWindow.c
@@ -17,7 +17,7 @@
  * Author: Chris Lumens <clumens at redhat.com>
  */
 
-#include "BaseWindow.h"
+#include "BaseStandalone.h"
 #include "HubWindow.h"
 #include "intl.h"
 
@@ -97,91 +97,12 @@ struct _AnacondaHubWindowPrivate {
 
 static void anaconda_hub_window_buildable_init(GtkBuildableIface *iface);
 
-G_DEFINE_TYPE_WITH_CODE(AnacondaHubWindow, anaconda_hub_window, ANACONDA_TYPE_BASE_WINDOW,
+G_DEFINE_TYPE_WITH_CODE(AnacondaHubWindow, anaconda_hub_window, ANACONDA_TYPE_BASE_STANDALONE,
                         G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE, anaconda_hub_window_buildable_init))
 
-static int get_sidebar_width(GtkWidget *window) {
-    GtkAllocation allocation;
-
-    /* change value below to make sidebar bigger / smaller */
-    float sidebar_width_percentage = 0.15;
-
-    gtk_widget_get_allocation(window, &allocation);
-    return allocation.width * sidebar_width_percentage;
-}
-
-static int get_sidebar_height(GtkWidget *window) {
-    GtkAllocation allocation;
-    gtk_widget_get_allocation(window, &allocation);
-    return allocation.height;
-}
-
-/* function to override default drawing to insert sidebar image */
-static gboolean anaconda_hub_window_on_draw(GtkWidget *win, cairo_t *cr) {
-    GtkStyleContext *context;
-    gdouble sidebar_x;
-    gdouble sidebar_width;
-
-    /* calls parent class' draw handler */
-    GTK_WIDGET_CLASS(anaconda_hub_window_parent_class)->draw(win,cr);
-
-    sidebar_width = get_sidebar_width(win);
-
-    /* For RTL languages, move the sidebar to the right edge */
-    if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) {
-        sidebar_x = 0;
-    } else {
-        GtkAllocation allocation;
-        gtk_widget_get_allocation(win, &allocation);
-        sidebar_x = allocation.width - sidebar_width;
-    }
-
-    context = gtk_widget_get_style_context(win);
-    gtk_style_context_save (context);
-
-    gtk_style_context_add_class(context, "logo-sidebar");
-    gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win));
-    gtk_style_context_remove_class(context, "logo-sidebar");
-
-    gtk_style_context_add_class(context, "logo");
-    gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win));
-    gtk_style_context_remove_class(context, "logo");
-
-    gtk_style_context_restore (context);
-
-    return TRUE; /* TRUE to avoid default draw handler */
-}
-
-/* Move base window content appropriate amount of space to make room for sidebar */
-static void anaconda_hub_window_size_allocate (GtkWidget *window, GtkAllocation *allocation) {
-    GtkAllocation child_allocation;
-    GtkWidget *child;
-    int sidebar_width;
-
-    GTK_WIDGET_CLASS(anaconda_hub_window_parent_class)->size_allocate(window, allocation);
-
-    gtk_widget_set_allocation(window, allocation);
-    sidebar_width = get_sidebar_width(window);
-    child_allocation.y = allocation->y;
-    child_allocation.width = allocation->width-sidebar_width;
-    child_allocation.height = allocation->height;
-
-    if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR)
-        child_allocation.x = allocation->x+sidebar_width;
-    else
-        child_allocation.x = allocation->x;
-
-    child = gtk_bin_get_child (GTK_BIN (window));
-    if (child && gtk_widget_get_visible (child))
-        gtk_widget_size_allocate (child, &child_allocation);
-}
-
 static void anaconda_hub_window_class_init(AnacondaHubWindowClass *klass) {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
-    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
 
-    widget_class->draw = anaconda_hub_window_on_draw;
-    widget_class->size_allocate = anaconda_hub_window_size_allocate;
     g_type_class_add_private(object_class, sizeof(AnacondaHubWindowPrivate));
 }
 
diff --git a/widgets/src/HubWindow.h b/widgets/src/HubWindow.h
index 8040eb8..9ff91e0 100644
--- a/widgets/src/HubWindow.h
+++ b/widgets/src/HubWindow.h
@@ -22,7 +22,7 @@
 
 #include <gtk/gtk.h>
 
-#include "BaseWindow.h"
+#include "BaseStandalone.h"
 
 G_BEGIN_DECLS
 
@@ -44,7 +44,7 @@ typedef struct _AnacondaHubWindowPrivate   AnacondaHubWindowPrivate;
  * be directly accessed.
  */
 struct _AnacondaHubWindow {
-    AnacondaBaseWindow           parent;
+    AnacondaBaseStandalone     parent;
 
     /*< private >*/
     AnacondaHubWindowPrivate  *priv;
@@ -58,7 +58,7 @@ struct _AnacondaHubWindow {
  *                pointer to be cast to an #AnacondaBaseWindow pointer.
  */
 struct _AnacondaHubWindowClass {
-    AnacondaBaseWindowClass parent_class;
+    AnacondaBaseStandaloneClass parent_class;
 };
 
 GType       anaconda_hub_window_get_type (void);
diff --git a/widgets/src/Makefile.am b/widgets/src/Makefile.am
index ebeca9a..5862fcd 100644
--- a/widgets/src/Makefile.am
+++ b/widgets/src/Makefile.am
@@ -34,6 +34,7 @@ GISOURCES = BaseWindow.c \
 	 StandaloneWindow.c \
 	 LayoutIndicator.c \
 	 Lightbox.c \
+	 BaseStandalone.c \
 	 widgets-common.c
 
 GIHDRS = BaseWindow.h \
@@ -45,6 +46,7 @@ GIHDRS = BaseWindow.h \
 	 StandaloneWindow.h \
 	 LayoutIndicator.h \
 	 Lightbox.h \
+	 BaseStandalone.h \
 	 widgets-common.h
 
 NONGISOURCES =
diff --git a/widgets/src/StandaloneWindow.c b/widgets/src/StandaloneWindow.c
index 7e20270..4c489c2 100644
--- a/widgets/src/StandaloneWindow.c
+++ b/widgets/src/StandaloneWindow.c
@@ -17,7 +17,7 @@
  * Author: Chris Lumens <clumens at redhat.com>
  */
 
-#include "BaseWindow.h"
+#include "BaseStandalone.h"
 #include "StandaloneWindow.h"
 #include "intl.h"
 
@@ -46,155 +46,79 @@
  *   space.  This is where widgets will be added and the user will do things.
  */
 
-enum {
-    SIGNAL_QUIT_CLICKED,
-    SIGNAL_CONTINUE_CLICKED,
-    LAST_SIGNAL
-};
-
 #define QUIT_TEXT       N_("_Quit")
 #define CONTINUE_TEXT   N_("_Continue")
 
-static guint window_signals[LAST_SIGNAL] = { 0 };
-
 struct _AnacondaStandaloneWindowPrivate {
     GtkWidget  *button_box;
     GtkWidget  *continue_button, *quit_button;
 };
 
-static void anaconda_standalone_window_quit_clicked(GtkButton *button,
-                                                    AnacondaStandaloneWindow *win);
-static void anaconda_standalone_window_continue_clicked(GtkButton *button,
-                                                        AnacondaStandaloneWindow *win);
+enum {
+    PROP_QUIT_BUTTON = 1,
+    PROP_CONTINUE_BUTTON
+};
+
+static void anaconda_standalone_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
 static void anaconda_standalone_window_realize(GtkWidget *widget,
                                                AnacondaStandaloneWindow *win);
 
-G_DEFINE_TYPE(AnacondaStandaloneWindow, anaconda_standalone_window, ANACONDA_TYPE_BASE_WINDOW)
-
-static int get_sidebar_width(GtkWidget *window) {
-    GtkAllocation allocation;
-
-    /* change value below to make sidebar bigger / smaller */
-    float sidebar_width_percentage = 0.15;
-
-    gtk_widget_get_allocation(window, &allocation);
-    return allocation.width * sidebar_width_percentage;
-}
-
-static int get_sidebar_height(GtkWidget *window) {
-    GtkAllocation allocation;
-    gtk_widget_get_allocation(window, &allocation);
-    return allocation.height;
-}
-
-/* function to override default drawing to insert sidebar image */
-static gboolean anaconda_standalone_window_on_draw(GtkWidget *win, cairo_t *cr) {
-    GtkStyleContext *context;
-    gdouble sidebar_x;
-    gdouble sidebar_width;
-
-    /* calls parent class' draw handler */
-    GTK_WIDGET_CLASS(anaconda_standalone_window_parent_class)->draw(win,cr);
-
-    sidebar_width = get_sidebar_width(win);
-
-    /* For RTL languages, move the sidebar to the right edge */
-    if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR) {
-        sidebar_x = 0;
-    } else {
-        GtkAllocation allocation;
-        gtk_widget_get_allocation(win, &allocation);
-        sidebar_x = allocation.width - sidebar_width;
-    }
-
-    context = gtk_widget_get_style_context(win);
-    gtk_style_context_save (context);
-
-    gtk_style_context_add_class(context, "logo-sidebar");
-    gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win));
-    gtk_style_context_remove_class(context, "logo-sidebar");
-
-    gtk_style_context_add_class(context, "logo");
-    gtk_render_background(context, cr, sidebar_x, 0, sidebar_width, get_sidebar_height(win));
-    gtk_style_context_remove_class(context, "logo");
-
-    gtk_style_context_restore (context);
-
-    return TRUE; /* TRUE to avoid default draw handler */
-}
-
-/* Move base window content appropriate amount of space to make room for sidebar */
-static void anaconda_standalone_window_size_allocate (GtkWidget *window, GtkAllocation *allocation) {
-    GtkAllocation child_allocation;
-    GtkWidget *child;
-    int sidebar_width;
-
-    GTK_WIDGET_CLASS(anaconda_standalone_window_parent_class)->size_allocate(window, allocation);
-
-    /*
-     * For RTL languages, the width is reduced by the same amount, but the
-     * start of the window does not need to move.
-     */
-    gtk_widget_set_allocation(window, allocation);
-    sidebar_width = get_sidebar_width(window);
-    child_allocation.y = allocation->y;
-    child_allocation.width = allocation->width-sidebar_width;
-    child_allocation.height = allocation->height;
-
-    if (gtk_get_locale_direction() == GTK_TEXT_DIR_LTR)
-        child_allocation.x = allocation->x+sidebar_width;
-    else
-        child_allocation.x = allocation->x;
-
-    child = gtk_bin_get_child (GTK_BIN (window));
-    if (child && gtk_widget_get_visible (child))
-        gtk_widget_size_allocate (child, &child_allocation);
-}
+G_DEFINE_TYPE(AnacondaStandaloneWindow, anaconda_standalone_window, ANACONDA_TYPE_BASE_STANDALONE)
 
 static void anaconda_standalone_window_class_init(AnacondaStandaloneWindowClass *klass) {
     GObjectClass *object_class = G_OBJECT_CLASS(klass);
-    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
-    widget_class->draw = anaconda_standalone_window_on_draw;
-    widget_class->size_allocate = anaconda_standalone_window_size_allocate;
 
-    klass->quit_clicked = NULL;
-    klass->continue_clicked = NULL;
+    object_class->get_property = anaconda_standalone_window_get_property;
+
+    /*
+     * Override the quit-button and continue-button properties to make them
+     * read only. The buttons will be create during the object's init method.
+     */
 
     /**
-     * AnacondaStandaloneWindow::quit-clicked:
-     * @window: the window that received the signal
+     * AnacondaStandaloneWindow::quit-button:
      *
-     * Emitted when the quit button has been activated (pressed and released).
+     * The button to quit anaconda.
      *
-     * Since: 1.0
+     * This overrides #AnacondaBaseStandalone:quit-button in #AnacondaBaseStandalone to be read-only.
+     *
+     * Since: 3.0
      */
-    window_signals[SIGNAL_QUIT_CLICKED] = g_signal_new("quit-clicked",
-                                                       G_TYPE_FROM_CLASS(object_class),
-                                                       G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
-                                                       G_STRUCT_OFFSET(AnacondaStandaloneWindowClass, quit_clicked),
-                                                       NULL, NULL,
-                                                       g_cclosure_marshal_VOID__VOID,
-                                                       G_TYPE_NONE, 0);
+    g_object_class_install_property(object_class,
+                                    PROP_QUIT_BUTTON,
+                                    g_param_spec_object("quit-button",
+                                                        P_("Quit button"),
+                                                        P_("The button to quit Anaconda"),
+                                                        GTK_TYPE_BUTTON,
+                                                        G_PARAM_READABLE));
 
     /**
-     * AnacondaStandaloneWindow::continue-clicked:
-     * @window: the window that received the signal
+     * AnacondaStandaloneWindow::continue-button:
+     *
+     * The button to continue to the next window.
      *
-     * Emitted when the continue button has been activated (pressed and released).
+     * This overrides #AnacondaBaseStandalone:continue-button in #AnacondaBaseStandalone to be read-only.
      *
-     * Since: 1.0
+     * Since: 3.0
      */
-    window_signals[SIGNAL_CONTINUE_CLICKED] = g_signal_new("continue-clicked",
-                                                           G_TYPE_FROM_CLASS(object_class),
-                                                           G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
-                                                           G_STRUCT_OFFSET(AnacondaStandaloneWindowClass, continue_clicked),
-                                                           NULL, NULL,
-                                                           g_cclosure_marshal_VOID__VOID,
-                                                           G_TYPE_NONE, 0);
+    g_object_class_install_property(object_class,
+                                    PROP_CONTINUE_BUTTON,
+                                    g_param_spec_object("continue-button",
+                                                        P_("Continue button"),
+                                                        P_("The button to continue to the next window"),
+                                                        GTK_TYPE_BUTTON,
+                                                        G_PARAM_READABLE));
 
     g_type_class_add_private(object_class, sizeof(AnacondaStandaloneWindowPrivate));
 }
 
+static void anaconda_standalone_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) {
+    /* Just chain up to the parent class get_property */
+    gchar *parent_property = g_strdup_printf("%s::%s", G_OBJECT_CLASS_NAME(anaconda_standalone_window_parent_class), pspec->name);
+    g_object_get_property(object, parent_property, value);
+    g_free(parent_property);
+}
+
 /**
  * anaconda_standalone_window_new:
  *
@@ -225,13 +149,9 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) {
     atk = gtk_widget_get_accessible(win->priv->continue_button);
     atk_object_set_name(atk, _(CONTINUE_TEXT));
 
-    /* Hook up some signals for those buttons.  The signal handlers here will
-     * just raise our own custom signals for the whole window.
-     */
-    g_signal_connect(win->priv->quit_button, "clicked",
-                     G_CALLBACK(anaconda_standalone_window_quit_clicked), win);
-    g_signal_connect(win->priv->continue_button, "clicked",
-                     G_CALLBACK(anaconda_standalone_window_continue_clicked), win);
+    /* Set the properties on AnacondaBaseStandalone */
+    g_object_set(G_OBJECT(win), "AnacondaBaseStandalone::quit-button", win->priv->quit_button, NULL);
+    g_object_set(G_OBJECT(win), "AnacondaBaseStandalone::continue-button", win->priv->continue_button, NULL);
 
     /* Create the button box and pack the buttons into it. */
     win->priv->button_box = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
@@ -240,6 +160,7 @@ static void anaconda_standalone_window_init(AnacondaStandaloneWindow *win) {
     gtk_widget_set_margin_bottom(win->priv->button_box, 6);
     gtk_button_box_set_layout(GTK_BUTTON_BOX(win->priv->button_box), GTK_BUTTONBOX_END);
     gtk_box_set_spacing(GTK_BOX(win->priv->button_box), 12);
+
     gtk_container_add(GTK_CONTAINER(win->priv->button_box), win->priv->quit_button);
     gtk_container_add(GTK_CONTAINER(win->priv->button_box), win->priv->continue_button);
 
@@ -263,49 +184,6 @@ static void anaconda_standalone_window_realize(GtkWidget *widget,
                                0);
 }
 
-static void anaconda_standalone_window_quit_clicked(GtkButton *button,
-                                                    AnacondaStandaloneWindow *win) {
-    g_signal_emit(win, window_signals[SIGNAL_QUIT_CLICKED], 0);
-}
-
-static void anaconda_standalone_window_continue_clicked(GtkButton *button,
-                                                        AnacondaStandaloneWindow *win) {
-    g_signal_emit(win, window_signals[SIGNAL_CONTINUE_CLICKED], 0);
-}
-
-/**
- * anaconda_standalone_window_get_may_continue:
- * @win: a #AnacondaStandaloneWindow
- *
- * Returns whether or not the continue button is sensitive (thus, whether the
- * user may continue forward from this window).
- *
- * Returns: Whether the continue button on @win is sensitive.
- *
- * Since: 1.0
- */
-gboolean anaconda_standalone_window_get_may_continue(AnacondaStandaloneWindow *win) {
-    return gtk_widget_get_sensitive(win->priv->continue_button);
-}
-
-/**
- * anaconda_standalone_window_set_may_continue:
- * @win: a #AnacondaStandaloneWindow
- * @may_continue: %TRUE if this window's continue button should be sensitive.
- *
- * Specifies whether the user may continue forward from this window.  If so,
- * the continue button will be made sensitive.  Windows default to continuable
- * so you must set it as false if you want.  The reason the user may not be
- * able to continue is if there is required information the user must enter
- * when no reasonable default may be given.
- *
- * Since: 1.0
- */
-void anaconda_standalone_window_set_may_continue(AnacondaStandaloneWindow *win,
-                                                 gboolean may_continue) {
-    gtk_widget_set_sensitive(win->priv->continue_button, may_continue);
-}
-
 /**
  * anaconda_standalone_window_retranslate:
  * @win: a #AnacondaStandaloneWindow
@@ -320,6 +198,7 @@ void anaconda_standalone_window_set_may_continue(AnacondaStandaloneWindow *win,
  */
 void anaconda_standalone_window_retranslate(AnacondaStandaloneWindow *win, const char *lang) {
     anaconda_base_window_retranslate(ANACONDA_BASE_WINDOW(win), lang);
+
     gtk_button_set_label(GTK_BUTTON(win->priv->quit_button), _(QUIT_TEXT));
     gtk_button_set_label(GTK_BUTTON(win->priv->continue_button), _(CONTINUE_TEXT));
 }
diff --git a/widgets/src/StandaloneWindow.h b/widgets/src/StandaloneWindow.h
index d156205..63a9e9e 100644
--- a/widgets/src/StandaloneWindow.h
+++ b/widgets/src/StandaloneWindow.h
@@ -22,7 +22,7 @@
 
 #include <gtk/gtk.h>
 
-#include "BaseWindow.h"
+#include "BaseStandalone.h"
 
 G_BEGIN_DECLS
 
@@ -44,7 +44,7 @@ typedef struct _AnacondaStandaloneWindowPrivate   AnacondaStandaloneWindowPrivat
  * be directly accessed.
  */
 struct _AnacondaStandaloneWindow {
-    AnacondaBaseWindow           parent;
+    AnacondaBaseStandalone       parent;
 
     /*< private >*/
     AnacondaStandaloneWindowPrivate  *priv;
@@ -56,22 +56,13 @@ struct _AnacondaStandaloneWindow {
  *                the widget class structure in order for the class mechanism
  *                to work correctly.  This allows an AnacondaStandaloneWindowClass
  *                pointer to be cast to an #AnacondaBaseWindow pointer.
- * @quit_clicked: Function pointer called when the #AnacondaStandaloneWindow::quit-clicked
- *                signal is emitted.
- * @continue_clicked: Function pointer called when the #AnacondaStandaloneWindow::continue-clicked
- *                    signal is emitted.
  */
 struct _AnacondaStandaloneWindowClass {
-    AnacondaBaseWindowClass parent_class;
-
-    void (* quit_clicked)     (AnacondaStandaloneWindow *window);
-    void (* continue_clicked) (AnacondaStandaloneWindow *window);
+    AnacondaBaseStandaloneClass parent_class;
 };
 
 GType       anaconda_standalone_window_get_type (void);
 GtkWidget  *anaconda_standalone_window_new      ();
-gboolean    anaconda_standalone_window_get_may_continue  (AnacondaStandaloneWindow *win);
-void        anaconda_standalone_window_set_may_continue  (AnacondaStandaloneWindow *win, gboolean may_continue);
 void        anaconda_standalone_window_retranslate       (AnacondaStandaloneWindow *win, const char *lang);
 
 G_END_DECLS
-- 
2.0.0



More information about the anaconda-patches mailing list