From ba505cf681ccb51f5500eaab1d720aef9f81103b Mon Sep 17 00:00:00 2001
From: David Soulayrol <david.soulayrol@gmail.com>
Date: Wed, 17 Nov 2010 13:25:48 +0100
Subject: [PATCH] Added a ready status to the panes.

---
 wizpym.py | 55 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 42 insertions(+), 13 deletions(-)

diff --git a/wizpym.py b/wizpym.py
index 1af7ce9..31342d1 100644
--- a/wizpym.py
+++ b/wizpym.py
@@ -60,15 +60,19 @@ class Pane(gtk.VBox):
     object that forwards data from one pane to the other and can be a
     simple string as well as a complex structure. It is initially set
     by the wizard Frame when the first pane is created.
+
+    A pane can also state that it is not ready to fulfill the next
+    pane requirements. The frame will grey the Next button as long as
+    its ready attribute is set to False.
     """
 
     def __init__(self, frame):
         """Constructor.
 
-        A fresh pane has no successors nor predecessor. The
-        constructor doesn't either set the subject: the wizard frame
-        takes care of setting it, and passing it from one pane to the
-        other.
+        A fresh pane is in a ready state and has no successors nor
+        predecessor. The constructor doesn't either set the subject:
+        the wizard frame takes care of setting it, and passing it from
+        one pane to the other.
 
         Note that all wizard panes constructors should take no
         parameter since they are called automatically by the wizard
@@ -81,6 +85,7 @@ class Pane(gtk.VBox):
         self._subject = None
         self._switch = None
         self._successors = {}
+        self._ready = True
 
     def link_to(self, klass):
         """Add a successor to this pane.
@@ -143,7 +148,15 @@ class Pane(gtk.VBox):
     def _set_subject(self, s):
         self._subject = s
 
+    def _is_ready(self):
+        return self._ready
+
+    def _set_ready(self, v):
+        self._ready = v
+        self._frame.update()
+
     subject = property(_get_subject, _set_subject, None, 'This pane subject')
+    ready = property(_is_ready, _set_ready, None, 'Whether the next pane can be called')
 
 
 class ActivePane(Pane):
@@ -211,9 +224,11 @@ class Frame(gtk.Dialog):
         """
         gtk.Dialog.__init__(self, title)
 
-        start_pane = start_klass(self)
-        start_pane.subject = subject
-        self._track = [start_pane]
+        # Members are all defined before calling the First pane so as
+        # to be ready if the start pane constructor invokes the frame
+        # (such as the ready attribute setting)
+        self._locked = False
+        self._track = []
 
         self._prev_button = gtk.Button('Previous')
         self._prev_button.set_sensitive(False)
@@ -230,6 +245,10 @@ class Frame(gtk.Dialog):
         self.connect('destroy', gtk.main_quit)
         self.connect("response", self._on_button)
 
+        start_pane = start_klass(self)
+        start_pane.subject = subject
+        self._track = [start_pane]
+
         self._enter_pane(start_pane, True)
 
     def lock(self):
@@ -238,8 +257,8 @@ class Frame(gtk.Dialog):
         This method is called by ActivePane instances when their
         background task is started.
         """
-        self._prev_button.set_sensitive(False)
-        self._next_button.set_sensitive(False)
+        self._lock = True
+        self.update()
 
     def unlock(self):
         """Unfreeze the previous and next buttons.
@@ -247,8 +266,19 @@ class Frame(gtk.Dialog):
         This method is called by ActivePane instances when their
         background task is terminated.
         """
-        self._prev_button.set_sensitive(len(self._track) > 1)
-        self._next_button.set_sensitive(not self._track[-1].is_terminal())
+        self._lock = False
+        self.update()
+
+    def update(self):
+        """Update the frame buttons display."""
+        # Beware that this method can be code early, before the track
+        # contains the first pane
+        if len(self._track):
+            pane = self._track[-1]
+            self._prev_button.set_sensitive(
+                not self._locked and len(self._track) > 1)
+            self._next_button.set_sensitive(
+                not self._locked and pane.ready and not pane.is_terminal())
 
     def _on_button(self, w, response_id):
         """The frame buttons handler."""
@@ -270,8 +300,7 @@ class Frame(gtk.Dialog):
         """Handle a new pane display."""
         if forward:
             pane.enter()
-        self._prev_button.set_sensitive(len(self._track) > 1)
-        self._next_button.set_sensitive(not pane.is_terminal())
+        self.update()
         self.vbox.pack_start(self._track[-1], True, True)
         self.vbox.show_all()