commit-gnue
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

gnue gnue-common/setup.py gnue-common/doc/DataO...


From: James Thompson
Subject: gnue gnue-common/setup.py gnue-common/doc/DataO...
Date: Sun, 12 Aug 2001 12:55:28 -0700

CVSROOT:        /cvs
Module name:    gnue
Changes by:     James Thompson <address@hidden> 01/08/12 12:55:27

Modified files:
        gnue-common    : setup.py 
        gnue-common/doc: DataObjects.txt 
        gnue-common/etc: sample.gnue.conf 
        gnue-common/src: GClientApp.py GConditions.py GConnections.py 
                         GDataObjects.py GDataSource.py GLoginHandler.py 
                         GObjects.py GParser.py 
        gnuef          : TODO 
        gnuef/client   : gfclient 
        gnuef/samples/location/forms: zipcode_maint.gfd 
        gnuef/samples/trigger: trigger.gfd 
        gnuef/samples/zipcode: states.gfd zipcode.gfd 
        gnuef/src      : GFClient.py GFForm.py GFInstance.py GFParser.py 
                         GFTrigger.py UIbase.py UIwxpython.py 
        gnuef/src/GFObjects: GFBlock.py GFDataSource.py GFEntry.py 
                             GFLabel.py GFObj.py 
        reports/src    : GRDataMapper.py GRLayout.py GRParser.py 
                         GRQueryBuilder.py 
Added files:
        gnue-common/doc: CustomizedLoginHandlers.txt 
Removed files:
        gnuef/src      : GFController.py GFDebug.py GFGetOpt.py 
                         GFOptions.py 

Log message:
        Merged new datasource branch w/ HEAD
        *****THIS WILL BREAK THINGS*****
        The previous cvs HEAD was tagged prior to doing this.  Use that if you 
need
        features not yet present in this code.

CVSWeb URLs:
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/setup.py.diff?cvsroot=OldCVS&tr1=1.15&tr2=1.16&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/doc/CustomizedLoginHandlers.txt.diff?cvsroot=OldCVS&tr1=1.1&tr2=1.2&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/doc/DataObjects.txt.diff?cvsroot=OldCVS&tr1=1.3&tr2=1.4&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/etc/sample.gnue.conf.diff?cvsroot=OldCVS&tr1=1.5&tr2=1.6&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GClientApp.py.diff?cvsroot=OldCVS&tr1=1.9&tr2=1.10&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GConditions.py.diff?cvsroot=OldCVS&tr1=1.1&tr2=1.2&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GConnections.py.diff?cvsroot=OldCVS&tr1=1.9&tr2=1.10&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GDataObjects.py.diff?cvsroot=OldCVS&tr1=1.7&tr2=1.8&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GDataSource.py.diff?cvsroot=OldCVS&tr1=1.11&tr2=1.12&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GLoginHandler.py.diff?cvsroot=OldCVS&tr1=1.2&tr2=1.3&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GObjects.py.diff?cvsroot=OldCVS&tr1=1.16&tr2=1.17&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnue-common/src/GParser.py.diff?cvsroot=OldCVS&tr1=1.18&tr2=1.19&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/TODO.diff?cvsroot=OldCVS&tr1=1.57&tr2=1.58&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/client/gfclient.diff?cvsroot=OldCVS&tr1=1.44&tr2=1.45&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/samples/location/forms/zipcode_maint.gfd.diff?cvsroot=OldCVS&tr1=1.3&tr2=1.4&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/samples/trigger/trigger.gfd.diff?cvsroot=OldCVS&tr1=1.8&tr2=1.9&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/samples/zipcode/states.gfd.diff?cvsroot=OldCVS&tr1=1.14&tr2=1.15&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/samples/zipcode/zipcode.gfd.diff?cvsroot=OldCVS&tr1=1.10&tr2=1.11&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFClient.py.diff?cvsroot=OldCVS&tr1=1.24&tr2=1.25&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFForm.py.diff?cvsroot=OldCVS&tr1=1.114&tr2=1.115&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFInstance.py.diff?cvsroot=OldCVS&tr1=1.20&tr2=1.21&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFParser.py.diff?cvsroot=OldCVS&tr1=1.51&tr2=1.52&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFTrigger.py.diff?cvsroot=OldCVS&tr1=1.44&tr2=1.45&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/UIbase.py.diff?cvsroot=OldCVS&tr1=1.42&tr2=1.43&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/UIwxpython.py.diff?cvsroot=OldCVS&tr1=1.106&tr2=1.107&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFObjects/GFBlock.py.diff?cvsroot=OldCVS&tr1=1.8&tr2=1.9&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFObjects/GFDataSource.py.diff?cvsroot=OldCVS&tr1=1.4&tr2=1.5&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFObjects/GFEntry.py.diff?cvsroot=OldCVS&tr1=1.7&tr2=1.8&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFObjects/GFLabel.py.diff?cvsroot=OldCVS&tr1=1.4&tr2=1.5&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/gnuef/src/GFObjects/GFObj.py.diff?cvsroot=OldCVS&tr1=1.4&tr2=1.5&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/reports/src/GRDataMapper.py.diff?cvsroot=OldCVS&tr1=1.3&tr2=1.4&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/reports/src/GRLayout.py.diff?cvsroot=OldCVS&tr1=1.6&tr2=1.7&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/reports/src/GRParser.py.diff?cvsroot=OldCVS&tr1=1.10&tr2=1.11&r1=text&r2=text
http://savannah.gnu.org/cgi-bin/viewcvs/gnue/reports/src/GRQueryBuilder.py.diff?cvsroot=OldCVS&tr1=1.2&tr2=1.3&r1=text&r2=text

Patches:
Index: gnue/gnue-common/doc/DataObjects.txt
diff -u gnue/gnue-common/doc/DataObjects.txt:1.3 
gnue/gnue-common/doc/DataObjects.txt:1.4
--- gnue/gnue-common/doc/DataObjects.txt:1.3    Tue Jul 17 12:10:54 2001
+++ gnue/gnue-common/doc/DataObjects.txt        Sun Aug 12 12:55:26 2001
@@ -74,9 +74,11 @@
 ***********
 ResultSet: 
   
-  isFirstRecord() : Returns 1=At first record, 0=
-  getRecord()   : returns 0=No records in memory, #=Current record #
-  setRecord()   : move to record #, returns 1=New record loaded, 0=invalid #
+  isFirstRecord() : Returns 1=At first record, 0=not at first record or no 
records loaded
+  getRecordNumber()   : returns -1=No records in memory, #=Current record # 
(0-based)
+  getRecord()   : return a specified record w/o changing 
+                  current record (returns None if invalid #) (0-based)
+  setRecord()   : move to record #, returns 1=New record loaded, 0=invalid # 
(0-based)
   nextRecord() : returns 1=New record loaded, 0=No more records
   prevRecord()  : returns 1=New record loaded, 0=At first record
   firstRecord() : returns 1=at first record, 0=No records loaded
Index: gnue/gnue-common/etc/sample.gnue.conf
diff -u gnue/gnue-common/etc/sample.gnue.conf:1.5 
gnue/gnue-common/etc/sample.gnue.conf:1.6
--- gnue/gnue-common/etc/sample.gnue.conf:1.5   Thu Aug  2 20:02:28 2001
+++ gnue/gnue-common/etc/sample.gnue.conf       Sun Aug 12 12:55:26 2001
@@ -27,6 +27,10 @@
 # Create new records in blocks automagically when you hit the bottom
 autoCreate = 1
 
+# Remember last query entered. (if enabled, then the last values entered
+# for a query can be retrieved by doing an Enter-Query twice) 
+RememberLastQuery = 1
+
 # Hack for db encoding
 Encoding=DEFAULT
                           
Index: gnue/gnue-common/setup.py
diff -u gnue/gnue-common/setup.py:1.15 gnue/gnue-common/setup.py:1.16
--- gnue/gnue-common/setup.py:1.15      Wed Jul 18 21:02:29 2001
+++ gnue/gnue-common/setup.py   Sun Aug 12 12:55:26 2001
@@ -92,6 +92,7 @@
                    "gnue.common.dbdrivers.geas",
                    "gnue.common.dbdrivers.mysql",
                    "gnue.common.dbdrivers._dbsig",
+                   "gnue.common.dbdrivers.empty",
                    "gnue.common.dbdrivers.odbc",
                    "gnue.common.dbdrivers.oracle",
                    "gnue.common.dbdrivers.postgresql"
Index: gnue/gnue-common/src/GClientApp.py
diff -u gnue/gnue-common/src/GClientApp.py:1.9 
gnue/gnue-common/src/GClientApp.py:1.10
--- gnue/gnue-common/src/GClientApp.py:1.9      Mon Jun  4 20:23:39 2001
+++ gnue/gnue-common/src/GClientApp.py  Sun Aug 12 12:55:26 2001
@@ -149,7 +149,7 @@
        
     # Get the connection definitions
     if connections != None: 
-      self.connections = Connections
+      self.connections = connections
     else: 
       if self.OPTIONS['connections']: 
         self.connections_file = self.OPTIONS['connections']
Index: gnue/gnue-common/src/GConditions.py
diff -u gnue/gnue-common/src/GConditions.py:1.1 
gnue/gnue-common/src/GConditions.py:1.2
--- gnue/gnue-common/src/GConditions.py:1.1     Wed Jun  6 19:09:45 2001
+++ gnue/gnue-common/src/GConditions.py Sun Aug 12 12:55:26 2001
@@ -31,12 +31,15 @@
 
 from GObjects import GObj
 
+class ConditionError (StandardError): 
+  pass
+
 #
 # Build a condition tree using a dict
 # as a source.  Assumes keys are field 
 # names and values are constants.
 #
-def buildConditionFromDict (dict): 
+def buildConditionFromDict (dict, comparison=None): 
   cond = GCondition()
   lastParent = cond
 
@@ -44,7 +47,7 @@
     lastParent = GCand(lastParent)
 
   for key in dict.keys(): 
-    eq = GCeq(lastParent)
+    eq = (comparison or GCeq)(lastParent)
     GCField(eq, key)
     GCConst(eq, dict[key])
 
Index: gnue/gnue-common/src/GConnections.py
diff -u gnue/gnue-common/src/GConnections.py:1.9 
gnue/gnue-common/src/GConnections.py:1.10
--- gnue/gnue-common/src/GConnections.py:1.9    Mon Jun 25 12:29:11 2001
+++ gnue/gnue-common/src/GConnections.py        Sun Aug 12 12:55:26 2001
@@ -144,6 +144,7 @@
   def setDataConnection(self, dataSource): 
     connection_name = dataSource.database
     dataObject = dataSource.getDataObject()
+    print "*** GConnections.setDataConnection.dataObject=%s" % dataObject
     if self._openConnections.has_key(string.lower(connection_name)): 
 
       # If a database connetion has already been established, use it
Index: gnue/gnue-common/src/GDataObjects.py
diff -u gnue/gnue-common/src/GDataObjects.py:1.7 
gnue/gnue-common/src/GDataObjects.py:1.8
--- gnue/gnue-common/src/GDataObjects.py:1.7    Tue Jun  5 08:57:45 2001
+++ gnue/gnue-common/src/GDataObjects.py        Sun Aug 12 12:55:26 2001
@@ -62,8 +62,14 @@
   # Raised when connection raises an exception.
   pass 
 
+class MasterDetailFieldMismatch(Exception): 
+  # Raised when a the number of master fields doesn't match the 
+  # number of detail fields. (e.g., masterlink="id,subid" 
+  # and detaillink="id" would be a problem; must be 1:1)
+  pass
 
 
+
 ###########################################################
 #
 # 
@@ -78,24 +84,57 @@
 
     self._masterfields = []
     self._detailfields = []
+
+    self._masterObject = None
+
+    # TODO: This is SO not true.
+    # _detailObjects really isn't used anywhere and shoud be removed
+    
     self._detailObjects = []
     self._dataConnection = None
     self._resultSetClass = ResultSet
     self._fieldReferences = {}  # Set by GDataSource; lists all fields
                                # a client explicits references
 
+    self._unboundFieldReferences = {}  # Contains names of all unbound
+                                       # field references
+
+    self.triggerExtensions = None
+
+
+  # Do we have a master datasource?
+  def hasMaster(self): 
+    return self._masterObject != None
+
   def createResultSet(self, conditions={}, readOnly=0): 
     pass
 
+  def createEmptyResultSet(self, readOnly=0): 
+    return self.createResultSet(conditions={'1':2}, readOnly=readOnly)
 
+
   # Add a detail data object.  This dataobject will create a new resultset 
-  # everything this dataobject changes (new record, etc).  The optional 
+  # everytime this dataobject changes (new record, etc).  The optional 
   # handler will be called after the detail dataobject is notified.  The 
-  # client application may with to add a handler to know when the detail
+  # client application may wish to add a handler to know when the detail
   # has been requeried.  handler is a method that takes two arguments: 
   # the master ResultSet and the detail ResultSet 
   def addDetailDataObject(self, dataObject, handler=None): 
     GDebug.printMesg (1,"Adding a master/detail relationship to DataObject")
+    dataObject._masterObject = self
+    dataObject._masterfields = string.split(hasattr(dataObject,'masterlink') 
and \
+                                      dataObject.masterlink or "", ',')
+    dataObject._detailfields = string.split(hasattr(dataObject,'detaillink') 
and \
+                                      dataObject.detaillink or "", ',')
+
+    if len(dataObject._masterfields) != len(dataObject._detailfields): 
+      raise MasterDetailFieldMismatch, "master=%s; detail=%s" % \
+          (dataObject._masterfields, dataObject._detailfields)
+
+    # Make sure "master" fields will be in our future query
+    for field in dataObject._masterfields:
+      self._fieldReferences[string.strip(string.lower(field))] = 1
+
     self._detailObjects.append ([dataObject, handler])
 
 
@@ -104,15 +143,47 @@
   def getLoginFields(self): 
     return []
 
+
+  #
+  # Connect to database. Design to be replaced by vendor-specific code.
+  #
+  # NOTE: This will only be called for the FIRST dataobject
+  #   using a particular connection.  Any subsequent dataobjects
+  #   will only get setDataConnection() called. Therefore, any
+  #   routines that must be run for EACH dataobject after a connection
+  #   has been establisted should be placed in _postConnect(), not connect().
+  #
+  #   As such, if the vendor-specific drivers replace this method, the last
+  #   line of the new method should be self._postConnect()!
+  # 
   def connect(self, connectData={}):
+    self._postConnect()
+
+
+  #
+  # Post connection routines. Design to be replaced by vendor-specific code.
+  #
+  # NOTE: See note for connect()
+  #
+  def _postConnect(self): 
     pass
 
+
+  #
+  # Set the associated data connection. Used by GConnections.
+  # THERE IS NO NEED FOR VENDOR-CLASSES TO REPLACE THIS METHOD!
+  #
+  # NOTE: See note for connect()
+  #
   def setDataConnection(self, connection): 
     self._dataConnection = connection
+    self._postConnect()
 
+
   def getDataConnection(self): 
     return self._dataConnection
 
+
   def commit(self):
     pass
 
@@ -121,8 +192,8 @@
 
   # Called when new record master in master/detail is queried
   def _masterRecordChanged(self, master):
-    self._masterfields = string.split(self.masterlink, ',')
-    self._detailfields = string.split(self.detaillink, ',')
+#    self._masterfields = string.split(self.masterlink, ',')
+#    self._detailfields = string.split(self.detaillink, ',')
     GDebug.printMesg (5, 'Master Record Changed')
     criteria = {}
     for i in range(0, len(self._masterfields)): 
@@ -130,7 +201,7 @@
       criteria[string.strip(self._detailfields[i])] = \
           master.current.getField(string.strip(self._masterfields[i]))
       GDebug.printMesg(10,master.current.getField(self._masterfields[i]))
-    return self.createResultSet(criteria)
+    return self.createResultSet(conditions=criteria)
 
 
 ###########################################################
@@ -146,10 +217,14 @@
      self._cursor = cursor
      self._cachedRecords = []
      self._currentRecord = -1
-     self._defaultValues = defaultValues
      self._masterRecordSet = masterRecordSet
      self._readonly = 0
      self._boundFields = {}
+     self._unboundFields = {}
+    
+     self._defaultValues = {} 
+     for key in defaultValues.keys(): 
+       self._defaultValues[key] = defaultValues[key]
 
      self.current = None
 
@@ -164,21 +239,44 @@
 
   # Returns 1=At first record, 0=Not first record
   def isFirstRecord(self):
-    return (_currentRecord == 1)
+    return (self._currentRecord == 1)
+
+
+  # returns -1=No records in memory, #=Current record #
+  def getRecordNumber(self):
+    return self._currentRecord
 
 
-  # returns 0=No records in memory, #=Current record #
-  def getRecord(self):
-    return _currentRecord + 1
+  # returns # of records currently loaded
+  def getCacheCount(self):
+    return len(self._cachedRecords)
 
 
+  # Get a specific record (0=based)
+  def getRecord(self, record):
+    while (record + 1 > len(self._cachedRecords)) and self._loadNextRecord(): 
+      pass
+
+    if record + 1 > len(self._cachedRecords): 
+      return None
+    else:
+      return self._cachedRecords[record]
+
+
   # move to record #, returns 1=New record loaded, 0=invalid #
   def setRecord(self, record):
-    if record - 1 > len(self._cachedRecords): 
-      # load records
-      pass
 
+    while (record - 1 > len(self._cachedRecords)) and self._loadNextRecord(): 
+      pass
 
+    if record - 1 > len(self._cachedRecords): 
+      return 0
+    else:
+      self._currentRecord = record
+      self.current = self._cachedRecords[self._currentRecord]
+      self.notifyDetailObjects()
+      return 1
+      
   # returns 1=New record loaded, 0=No more records
   def nextRecord(self):
     if self._currentRecord + 1 == len(self._cachedRecords): 
@@ -205,12 +303,14 @@
   # returns 1=at first record, 0=No records loaded
   def firstRecord(self):
     if self._currentRecord < 0: 
-      return 0
-    else: 
-      self._currentRecord = 0
-      self.current = self._cachedRecords[0]
-      self.notifyDetailObjects()
-      return 1
+      if not self._loadNextRecord(): 
+        return 0
+
+    self._currentRecord = 0
+    self.current = self._cachedRecords[0]
+    self.notifyDetailObjects()
+    return 1
+       
 
 
   # returns 1=at last record, 0=No records loaded
@@ -218,10 +318,10 @@
     if self._currentRecord == -1: 
       return 0
     else:
-      while self._loadNextRecord: 
+      while self._loadNextRecord(): 
         pass
-      self._currentRecord = len(self._cachedRecords) 
-      self.current = self._cachedRecords(self._currentRecord)
+      self._currentRecord = len(self._cachedRecords) - 1
+      self.current = self._cachedRecords[self._currentRecord]
       self.notifyDetailObjects()
       return 1
 
@@ -232,9 +332,10 @@
       # Provide better feedback?? 
       raise ReadOnlyError, "Attempted to insert into a read only datasource"
     else: 
+      GDebug.printMesg(7,'Inserting a blank record')
       self._currentRecord = self._currentRecord+1
-      self._cachedRecords.insert(_currentRecord, self._createEmptyRecord())
-      current = self._cachedRecords[self._currentRecord]
+      self._cachedRecords.insert(self._currentRecord, 
self._createEmptyRecord())
+      self.current = self._cachedRecords[self._currentRecord]
       self.notifyDetailObjects()
       return 1
 
@@ -248,6 +349,9 @@
         break
     return isPending
 
+  # Returns 1=DataObject has uncommitted changes
+  def isRecordPending(self):
+    return self.current.isPending()
 
   # Post changes to the database
   def post(self):
@@ -255,10 +359,15 @@
     for record in (self._cachedRecords): 
       record.post()
 
-  # 
+  # jst
   def notifyDetailObjects(self):
     GDebug.printMesg(5,'Master record changed; Notifying Detail Objects')
+    #if self.current: 
+    #  for detail in self.current._detailObjects:
+    #    print "GDataObjects ",detail
+
     for detail in self._dataObject._detailObjects:
+       print detail
        rs = detail[0]._masterRecordChanged(self)
        if detail[1]: 
          detail[1].masterResultSetChanged(self, rs)
@@ -276,12 +385,12 @@
 
   # Load cacheCount number of new records
   def _loadNextRecord(self): 
-    return 1
+    return 0
 
 
   # Load cacheCount number of new records
   def _createEmptyRecord(self): 
-    return self._recordSetClass()
+    return self._recordSetClass(self)
 
 
 ###########################################################
@@ -299,18 +408,30 @@
     self._parent = parent
     self._modifiedFlags = {}      # If field name is present as a key, 
                                   # then field has been modified
+
+    self._initialData = initialData
+
+    self._unboundFields = {}
+
     if len(initialData): 
       self._insertFlag = 0
       self._emptyFlag = 0
-      self._fields = initialData
+      self._fields = {} 
+      for key in initialData.keys(): 
+       self._fields[key] = initialData[key]
     else: 
       self._insertFlag = 1
       self._emptyFlag = 1
-      self._fields = defaultData
+      self._fields = {} 
+      for key in defaultData.keys(): 
+       self._fields[key] = defaultData[key]
 
 
   # Returns 1=Record has uncommitted changes
   def isPending(self):
+
+    # The _insertFlag and _deleteFlag takes care of records that
+    # were inserted, but then deleted before a save (i.e., nothing to do)
     if self._emptyFlag or self._insertFlag and self._deleteFlag: 
       return 0
     else: 
@@ -364,14 +485,14 @@
     else: 
       self._emptyFlag = 0
       fn = string.lower(field)
-      self._fields[fn] = value
+      self._fields[fn] = "%s" % value
       if self._parent.isFieldBound(field): 
         self._updateFlag = 1
-      if self._modifiedFlags.has_key(fn): 
-        flag = self._modifiedFlags[fn] + 1
-      else: 
-        flag = 1
-      self._modifiedFlags[fn] = flag
+        if self._modifiedFlags.has_key(fn): 
+          flag = self._modifiedFlags[fn] + 1
+        else: 
+          flag = 1
+        self._modifiedFlags[fn] = flag
 
 
   # Returns 1=Field has been modified
@@ -379,7 +500,7 @@
     return self._modifiedFlags.has_key (string.lower(fieldName))   
 
 
-  # Posts changes to database
+  # Mark the current record as deleted
   def delete(self): 
     if self._parent.isReadOnly(): 
       # Provide better feedback?? 
@@ -414,5 +535,6 @@
   # Post any changes to database
   def _postChanges(self): 
     return 1
+
 
 
Index: gnue/gnue-common/src/GDataSource.py
diff -u gnue/gnue-common/src/GDataSource.py:1.11 
gnue/gnue-common/src/GDataSource.py:1.12
--- gnue/gnue-common/src/GDataSource.py:1.11    Wed Jul  4 17:24:11 2001
+++ gnue/gnue-common/src/GDataSource.py Sun Aug 12 12:55:26 2001
@@ -55,6 +55,7 @@
     self._dataObject = None
     self._connectionComment = "" 
     self._fieldReferences = {}
+    self._unboundFieldReferences = {}
 
   #
   # This method should be called after the object is created 
@@ -64,18 +65,28 @@
     self._connectionManager = connectionManager
 
   def initialize(self): 
-    if self.database and self._connectionManager: 
+
+    if not self.database: 
+      # We are a connectionless datasource (virtual?)
+      # We have to bind to something, so bind to empty driver
+      from gnue.common.dbdrivers.empty import DBdriver
+      GDebug.printMesg (7, 'Using empty data driver')
+      self._dataObject = DBdriver.supportedDataObjects['object']() 
+
+    elif self._connectionManager: 
       # This will throw a GConnections.NotFoundError if an unknown 
       # connection name is specified.  The calling method should 
       # catch this exception and handle it properly (exit w/message)
       self._dataObject = \
          self._connectionManager.getDataObject(self.database, self.type)
-      self._dataObject._fieldReferences = self._fieldReferences
 
-      # Copy all attributes from XML to the dataObject 
-      for attribute in tagAttributes.keys(): 
-        if hasattr(self, attribute): 
-          self._dataObject.__dict__[attribute] = self.__dict__[attribute]
+    self._dataObject._fieldReferences = self._fieldReferences
+    self._dataObject._unboundFieldReferences = self._unboundFieldReferences
+
+    # Copy all attributes from XML to the dataObject 
+    for attribute in tagAttributes.keys(): 
+      if hasattr(self, attribute): 
+        self._dataObject.__dict__[attribute] = self.__dict__[attribute]
           
 
   def connect(self): 
@@ -91,7 +102,37 @@
     if self._dataObject != None: 
       self._dataObject._fieldReferences[field] = 1
 
+  def referenceUnboundField(self, field): 
+    GDebug.printMesg(7,'Field %s implicitly referenced' % field)
+    self._unboundFieldReferences[field] = 1
+    if self._dataObject != None: 
+      self._dataObject._unboundFieldReferences[field] = 1
+
 
+  #
+  # The following are a simple wrapper arround the datasource's dataobject
+  # to hide the dataobject from the app programmer
+  # 
+  def hasMaster(self): 
+    return self._dataObject != None and self._dataObject.hasMaster()
+
+  def createResultSet(self, conditions={}, readOnly=0):
+#    if self._dataObject: 
+      return self._dataObject.createResultSet(conditions,readOnly)
+
+  def addDetailDataObject(self, dataObject, handler=None):
+#    if self._dataObject:
+      self._dataObject.addDetailDataObject(dataObject, handler)
+
+  def createEmptyResultSet(self, readOnly=0):
+#    if self._dataObject:
+      return self._dataObject.createEmptyResultSet(readOnly)
+
+######
+# 
+# 
+#
+######
 class GSql(GObjects.GObj): 
   def __init__(self, parent=None): 
      GObjects.GObj(self, parent, type="GDSql") 
Index: gnue/gnue-common/src/GLoginHandler.py
diff -u gnue/gnue-common/src/GLoginHandler.py:1.2 
gnue/gnue-common/src/GLoginHandler.py:1.3
--- gnue/gnue-common/src/GLoginHandler.py:1.2   Tue May 15 08:16:38 2001
+++ gnue/gnue-common/src/GLoginHandler.py       Sun Aug 12 12:55:26 2001
@@ -40,6 +40,10 @@
 
 
 class LoginHandler: 
+
+  # The client app can set any default values for the needed parameters.
+  defaults = {}
+
   #
   # getLogin is passed an list consisting of: 
   #   Connection Name
Index: gnue/gnue-common/src/GObjects.py
diff -u gnue/gnue-common/src/GObjects.py:1.16 
gnue/gnue-common/src/GObjects.py:1.17
--- gnue/gnue-common/src/GObjects.py:1.16       Sat Jul 28 20:21:41 2001
+++ gnue/gnue-common/src/GObjects.py    Sun Aug 12 12:55:26 2001
@@ -57,6 +57,7 @@
     self._parent = parent
     self._children = []
     self._attributes = {}
+    self._inits = []
     if parent :
       parent.addChild(self)
 
@@ -72,6 +73,27 @@
       for child in self._children:
         if isinstance(child, GObj):
           child.initialize()
+
+  #
+  # phaseInit
+  #
+  # TODO: Phase init should really get the size of the largest _inits
+  # TODO: array and use it instead of hardcoding 5 iterations
+  def phaseInit(self): 
+    for phase in range(5):
+      self._phaseInit(phase)
+      phase = phase + 1
+
+  def _phaseInit(self,phase):
+    if (len(self._inits) > phase) and (self._inits[phase] != None):
+      GDebug.printMesg(6,"%s: Init Phase %s" % (self.getObjectType(), phase+1))
+      self._inits[phase]()
+
+    if self._children:
+      for child in self._children:
+        if isinstance(child, GObj):
+          child._phaseInit(phase)
+    
 
   #
   # getObjectType
Index: gnue/gnue-common/src/GParser.py
diff -u gnue/gnue-common/src/GParser.py:1.18 
gnue/gnue-common/src/GParser.py:1.19
--- gnue/gnue-common/src/GParser.py:1.18        Wed Jul  4 17:24:11 2001
+++ gnue/gnue-common/src/GParser.py     Sun Aug 12 12:55:27 2001
@@ -103,7 +103,14 @@
     object.__dict__[att] = attributes[att]
 
   if initialize:
-    object.initializeTree()
+    GDebug.printMesg(10,"Initializing the object tree starting at %s" 
%(object))
+
+    # TODO: initializeTree is depreciated but left in here for 
+    # TODO: backwards compatibility.
+    if hasattr(object,'initializeTree'):
+      object.initializeTree()
+    else:
+      object.phaseInit()
 
   return object
 
Index: gnue/gnuef/TODO
diff -u gnue/gnuef/TODO:1.57 gnue/gnuef/TODO:1.58
--- gnue/gnuef/TODO:1.57        Mon Jul 16 20:09:41 2001
+++ gnue/gnuef/TODO     Sun Aug 12 12:55:27 2001
@@ -1,7 +1,6 @@
 A name inside of [] means that person has taken that task
 
    Bugs 
-    Fix symlink support 
     I've broken msgboxs in triggers
     
     triggers should not fire during query entry
@@ -16,15 +15,6 @@
       provide better seperation of Objects in the Data system
       restore proper encapsulation in system
 
-    Move things like database init in GFForm out of __init__ and 
-      into a initialize routine that is called via some type of 
-      uiINITIALIZED event so that a UI windows can prompt for things
-      like name/password.  
-
-      move initialize function into the base object and have them
-      call the initializes of their children so we can get rid of 
-      more of the self.walk nonsense
- 
     Rewrite the event system to allow greater efficency and flexibility
 
   Data source related
Index: gnue/gnuef/samples/location/forms/zipcode_maint.gfd
diff -u gnue/gnuef/samples/location/forms/zipcode_maint.gfd:1.3 
gnue/gnuef/samples/location/forms/zipcode_maint.gfd:1.4
--- gnue/gnuef/samples/location/forms/zipcode_maint.gfd:1.3     Sun Jul 29 
12:36:32 2001
+++ gnue/gnuef/samples/location/forms/zipcode_maint.gfd Sun Aug 12 12:55:27 2001
@@ -14,15 +14,14 @@
   <datasource name="validator" database="gnue" table="state" prequery="" />
 
   <page>
+    <label text="City" x="1" y="1"/>
+    <label text="State" x="31" y="1"/>
+    <label text="Zip" x="39" y="1"/>
     <block name="zip" datasource="zips" rows="10">
-      <label text="City" x="1" y="1"/>
-      <entry name="city" field="city" x="1" y="2" width="30"  uppercase="">
-      </entry>
+      <entry name="city" field="city" x="1" y="2" width="30"  uppercase=""/>
 
-      <label text="State" x="31" y="1"/>
       <entry name="state" field="state" x="32" y="2" width="10"  
style="dropdown" foreign_key="validator.state" 
foreign_key_description="description"/>
 
-      <label text="Zip" x="39" y="1"/>
       <entry name="zip" field="zipcode" x="42" y="2" width="7"  numeric="" 
max_length="5"/>
     </block>
   </page>
Index: gnue/gnuef/samples/trigger/trigger.gfd
diff -u gnue/gnuef/samples/trigger/trigger.gfd:1.8 
gnue/gnuef/samples/trigger/trigger.gfd:1.9
--- gnue/gnuef/samples/trigger/trigger.gfd:1.8  Thu Jul 12 20:59:01 2001
+++ gnue/gnuef/samples/trigger/trigger.gfd      Sun Aug 12 12:55:27 2001
@@ -29,7 +29,7 @@
            <tip>Type Field One's Replacement</tip>
          </options>
          <trigger type ="Pre-FOCUSOUT">
-block1.fields.one = self.getValue()
+block1.fields.one.setValue(self.getValue())
 #
 #User name trigger
 #
Index: gnue/gnuef/samples/zipcode/states.gfd
diff -u gnue/gnuef/samples/zipcode/states.gfd:1.14 
gnue/gnuef/samples/zipcode/states.gfd:1.15
--- gnue/gnuef/samples/zipcode/states.gfd:1.14  Sat Jul 21 16:33:49 2001
+++ gnue/gnuef/samples/zipcode/states.gfd       Sun Aug 12 12:55:27 2001
@@ -8,29 +8,29 @@
     <width>39</width>
   </options>
 
-  <database name="gnue" provider="postgresql" dbname="gnue" host="gnue"/>
-  <datasource name="dsstate" database="gnue" table="state" cache="5" 
order_by="state"/>
-  <datasource name="dscities" database="gnue" table="zipcode" cache="5" 
order_by="city"/> 
+  <datasource name="dsstate" database="devel" table="state" cache="5" 
order_by="state"/>
+  <datasource name="dscities" database="devel" table="zipcode" cache="5" 
order_by="city" 
+              master="dsstate" detaillink="state_code" masterlink="state"/> 
 
   <page>
-    <block name="blkstate" datasource="dsstate">
+    <block name="blkstate" datasource="dsstate" rows="5">
       <label text="Code" x="2" y="1"/>
-      <entry name="entstate" field="state" x="2" y="2" width="4" height="1" 
rows="5"/>
+      <entry name="entstate" field="state" x="2" y="2" width="4" height="1"/>
       <label text="Description" x="7" y="1"/>
-      <entry name="entdesc" field="description" x="7" y="2" width="30" 
height="1" rows="5"/>
+      <entry name="entdesc" field="description" x="7" y="2" width="30" 
height="1"/>
     </block>
 
 
     <!-- Block of cities -->
-    <block name="cities"  datasource="dscities" master="blkstate.state" 
detail="state_code">
+    <block name="cities" datasource="dscities" rows="5">
       <label text="City" x="2" y="9"/>
-      <entry name="city" field="city" x="2" y="10" width="20" rows="5" />
+      <entry name="city" field="city" x="2" y="10" width="20" />
 
       <label text="ST" x="23" y="9"/>
-      <entry name="state" field="state_code" x="23" y="10" width="3" rows="5" 
/>
+      <entry name="state" field="state_code" x="23" y="10" width="3" />
 
       <label text="Zip" x="27" y="9"/>
-      <entry name="zip" field="zip" x="27" y="10" width="10" rows="5" />
+      <entry name="zip" field="zip" x="27" y="10" width="10" />
 
       <box x="1" y="8" width="37" height="8" label="State Cities"/>
 
Index: gnue/gnuef/samples/zipcode/zipcode.gfd
diff -u gnue/gnuef/samples/zipcode/zipcode.gfd:1.10 
gnue/gnuef/samples/zipcode/zipcode.gfd:1.11
--- gnue/gnuef/samples/zipcode/zipcode.gfd:1.10 Tue Mar 20 15:25:50 2001
+++ gnue/gnuef/samples/zipcode/zipcode.gfd      Sun Aug 12 12:55:27 2001
@@ -8,30 +8,27 @@
     <width>42</width>
   </options>
 
-<!--  <database name="gnue" provider="mysql" dbname="gnue" host="localhost"/> 
-->
-  <database name="gnue" provider="postgresql" dbname="gnue" host="gnue"/> 
+  <datasource name="zips" database="devel" table="zipcode" cache="5" 
order_by="state_code,city" prequery=""/> 
+  <datasource name="validator" database="devel" table="state" prequery="" 
order_by="description"/>
 
-  <datasource name="zips" database="gnue" table="zipcode" cache="5" 
order_by="state_code,city" prequery=""/> 
-  <datasource name="validator" database="gnue" table="state" prequery="" 
order_by="description"/>
-
   <page>
-    <block name="zip" datasource="zips">
+    <block name="zip" datasource="zips" rows="15">
       <label text="City" x="1" y="1"/>
-      <entry name="city" field="city" x="1" y="2" width="15" visibleCount="15" 
uppercase="">
+      <entry name="city" field="city" x="1" y="2" width="15"  uppercase="">
        <options>
          <option name="tip">Full name of city</option>
        </options>
       </entry>
 
       <label text="State" x="17" y="1"/>
-      <entry name="state" field="state_code" x="17" y="2" width="15" 
visibleCount="15" uppercase="" foreign_key="validator.state" 
foreign_key_description="description" style="dropdown">
+      <entry name="state" field="state_code" x="17" y="2" width="15"  
uppercase="" foreign_key="validator.state" 
foreign_key_description="description" style="dropdown">
        <options>
          <tip>State</tip>
        </options>
       </entry>
 
       <label text="Zip" x="33" y="1"/>
-      <entry name="zip" field="zip" x="33" y="2" width="5" visibleCount="15" 
numeric="" max_length="5" >
+      <entry name="zip" field="zip" x="33" y="2" width="5"  numeric="" 
max_length="5" >
        <options>
          <tip>US Postal Zip Code</tip>
        </options>
Index: gnue/gnuef/src/GFClient.py
diff -u gnue/gnuef/src/GFClient.py:1.24 gnue/gnuef/src/GFClient.py:1.25
--- gnue/gnuef/src/GFClient.py:1.24     Mon Jul  2 10:54:19 2001
+++ gnue/gnuef/src/GFClient.py  Sun Aug 12 12:55:27 2001
@@ -34,7 +34,7 @@
 #
 # Copyright (c) 2000 Free Software Foundation
 #
-# $Id: GFClient.py,v 1.24 2001/07/02 17:54:19 jcater Exp $
+# $Id: GFClient.py,v 1.25 2001/08/12 19:55:27 jamest Exp $
 #
 
 import pstats
@@ -42,12 +42,9 @@
 import os.path
 import sys
 import urllib
-import gnue.forms.GFOptions
 
-from gnue.forms.GFGetOpt import *
 from gnue.forms.GFInstance import *
 from gnue.forms.GFForm import *
-from gnue.forms.GFController import *
 from gnue.forms.GFParser import loadForm 
 from gnue.common import GDebug
 from gnue.common import GConfig
@@ -106,17 +103,17 @@
     if self.ui_type == 'gui'  :
       if os.environ.has_key('DISPLAY') or os.name != 'posix':
         from gnue.forms import UIwxpython
-        self._ui=UIwxpython.GFUserInterface
+        self._ui=UIwxpython
       else:
         self.ui_type='text'
 
     if self.ui_type == 'pytext':
       from gnue.forms import UIpyncurses
-      self._ui=UIpyncurses.GFUserInterface
+      self._ui=UIpyncurses
 
     if self.ui_type == 'text' : 
       from gnue.forms import UIcurses
-      self._ui=UIcurses.GFUserInterface
+      self._ui=UIcurses
 
     self.runForm(formfile, self.disableSplash)
 
@@ -125,11 +122,19 @@
     self.runForm(formFile, disableSplash=1)
 
   def runForm(self, formFile, disableSplash=0):    
-    
+    #
+    # Create the instance
+    #
     instance = GFInstance(self, self.getNextSerialNumber(), 
         connections=self.connections, ui=self._ui, disableSplash=disableSplash)
+    self._formInstances[instance.getSerialNumber()] = instance 
 
     #
+    # Assign the proper login handler based upon the user interface choice
+    #
+    self.getConnectionManager().setLoginHandler(self._ui.UILoginHandler())
+
+    #
     # Build the form tree
     #
     drive = os.path.splitdrive(formFile) 
@@ -142,13 +147,10 @@
     fileHandle.close()
 
     #
-    # Create and start the instance
+    # Start the instance
     #
-    self._formInstances[instance.getSerialNumber()] = instance 
-
     instance.setForm(form)
     instance.activate()
-
 
   def getNextSerialNumber (self): 
      self._lastSerialNumber = self._lastSerialNumber + 1
Index: gnue/gnuef/src/GFForm.py
diff -u gnue/gnuef/src/GFForm.py:1.114 gnue/gnuef/src/GFForm.py:1.115
--- gnue/gnuef/src/GFForm.py:1.114      Tue Aug  7 20:17:00 2001
+++ gnue/gnuef/src/GFForm.py    Sun Aug 12 12:55:27 2001
@@ -1,3 +1,4 @@
+#
 # This file is part of GNU Enterprise.
 #
 # GNU Enterprise is free software; you can redistribute it 
@@ -15,7 +16,7 @@
 # write to the Free Software Foundation, Inc., 59 Temple Place 
 # - Suite 330, Boston, MA 02111-1307, USA.
 #
-# Copyright 2000 Free Software Foundation
+# Copyright 2000-2001 Free Software Foundation
 #
 # FILE:
 # GFForm.py
@@ -28,14 +29,12 @@
 #
 
 import sys
-import os
 import string
-from gnue.common import GDebug, openResource
-from gnue.common import GConfig            
 import traceback
 
+from gnue.common import GDebug, openResource
+from gnue.common import GConfig            
 from GFObjects import *
-#import GFObjects
 from GFEvent import *
 from GFError import DBError
 from GFTriggerError import *
@@ -68,10 +67,15 @@
     self._blockList = []
     self._pageList = []
 
-    #self.borderPercentage = 10
-    #self._textPercentage   = 7.5
     self._app = app
 
+    self._triggerns={}
+    self._triggerns['TRUE'] = 1
+    self._triggerns['FALSE'] = 0
+    self._triggerns['GFMsgBox'] = GFMsgBox
+
+    self._inits = [self.primaryInit, self.secondaryInit]
+
   def buildObject(self):
 
     # Convert some deprecated options to new style
@@ -93,73 +97,76 @@
           i = i + 1
       else: 
         i = i + 1
-
-          
         
-
-  def initializeTree(self):
-      
-    # Import all needed objects
+  #
+  # primaryInit
+  #
+  # Called during phaseInit startup by GParser.
+  #
+  def primaryInit(self):
+    # TODO: Look at moving this into GParser
+    # Import all needed objects 
     self.walk(self.initImportedObjects)  
 
-    # set the initial focus
-    self.walk(self.setInitialFocus)
+    # analyze the tree
+    self.walk(self.analyzeTree)
+  
+  def secondaryInit(self):
+    # create the first records
+    for key in self._datasourceDictionary.keys(): 
+      if not self._datasourceDictionary[key].hasMaster():
+        self._datasourceDictionary[key].createEmptyResultSet()
 
-    # Connect to the defined databases
-    self.walk(self.initDatabaseObjects)
-    self.walk(self.initDataSources)
-
-    # build the block list
-    self.walk(self.initBlocks)
-    self.walk(self.initPages)
-    self.walk(self.initEntries)
-    self.walk(self.initLabels)
-
-    GDebug.printMesg(3,  "Database Dictionary")
-    GDebug.printMesg(3,  "%s"%self._databaseDictionary)
-    GDebug.printMesg(3,  "Datasource Dictionary")
-    GDebug.printMesg(3,  "%s"%self._datasourceDictionary)
-
-    #Set trigger namespace, we'll need something more
-    #detailed than this including widgets etc. Also attaches
-    #named triggers to thier placeholders
-    self._triggerns={}
-    self._triggerns['TRUE'] = 1
-    self._triggerns['FALSE'] = 0
+  #
+  # begin routines made for walking
+  #
+  # routines that are used by the walk() method
+  # suggesting a better way probably exists :)
+  #
 
-    for block in self._blockList:
+  #
+  # analyzeTree
+  #
+  # Scans the tree form tree and performs the following
+  # 
+  # builds the various entity lists
+  # it also sets the initial focus of the form
+  # constructs the trigger namespace
+  #
+  def analyzeTree(self,object):
+    if object.getObjectType() == 'GFBlock':
+      self._blockList.append(object)
+      if self._currentBlock == None:
+        self._currentBlock = object
+
       try:
-        self._triggerns[block.name] = GFTrigger.GFTriggerHelper( block )
+        self._triggerns[object.name] = GFTrigger.GFTriggerHelper( object )
       except AttributeError: pass
-
-    for key in self._datasourceDictionary.keys():
-      self._triggerns[key] = self._datasourceDictionary[key]
-
-
-    self._triggerns['GFMsgBox'] = GFMsgBox
 
+    elif object.getObjectType() == 'GFPage':
+      self._pageList.append(object)
+      if self._currentPage == None:
+        self._currentPage = object
 
-  # Initialization steps that must occur after connected to database
-  def postLoginInitialization(self):
+    elif object.getObjectType() == 'GFEntry' and self._currentEntry == None 
and not object.hidden:
+      self._currentEntry = object
+            
+    elif object.getObjectType() =='GFDataSource':
+      self._datasourceDictionary[object.name]=object
 
-    for db in self._databaseDictionary.keys(): 
-      self._triggerns[db] = self._databaseDictionary[db].triggerNamespace
-      self._databaseDictionary[db].properties = self._databaseDictionary[db]
-
-    for key in self._datasourceDictionary.keys():
-      object = self._datasourceDictionary[key]
-      object.initialize()    
+  def setFocus(self,object):
+    if object.getObjectType() == 'GFBlock' and self._currentBlock == None:
+        self._currentBlock = object
 
-    self.walk(self.initTriggers)      
+    elif object.getObjectType() == 'GFPage' and self._currentPage == None:
+        self._currentPage = object
 
+    elif object.getObjectType() == 'GFEntry' and \
+         self._currentEntry == None and \
+         not object.readonly and \
+         not object.hidden:
+      self._currentEntry = object
 
-    
-  #
-  # begin routines made for walking
-  #
-  # routines that are used by the walk() method
-  # suggesting a better way probably exists :)
-  #
   def initImportedObjects(self, object): 
     if isinstance(object, GFLibrary.GFImport): 
       importAllDataSources = object.datasources == "*"
@@ -220,8 +227,7 @@
             rv.__dict__[key] = object._loadedxmlattrs[key]
             GDebug.printMesg (5, ">>> Moving %s" % key)
         rv.buildObject()
-        
-      
+              
   def __findImportItem(self, find, object, id): 
     if isinstance(object, find._importclass) and \
        hasattr(object, id) and \
@@ -236,70 +242,10 @@
       return rv
     else: 
       return None 
-        
-
-  def setInitialFocus(self, object):
-    if object.getObjectType() == 'GFBlock' and self._currentBlock == None:
-      self._currentBlock = object
-    if object.getObjectType() == 'GFPage' and self._currentPage == None:
-      self._currentPage = object
-    if object.getObjectType() == 'GFEntry' and self._currentEntry == None and 
not object.hidden:
-      self._currentEntry = object
-
-  def initBlocks(self, object):
-    if object.getObjectType() == 'GFBlock':
-      self._blockList.append(object)
-      object.initialize()
-
-  def initPages(self,object):
-    if object.getObjectType() == 'GFPage':
-      self._pageList.append(object)
-            
-  def initEntries(self, object):
-    if object.getObjectType() == 'GFEntry':
-      object.initialize()
-      value = object._block._dataSourceLink.getField(0,object.field)
-      if value=='':
-        if hasattr(object, 'style') and object.style=='checkbox':
-          value = 0
-      object._value = value
-
-  def initLabels(self, object):
-    if object.getObjectType() == 'GFLabel':
-      object.initialize()
-
-  def initDataSources(self, object):
-    if object.getObjectType() =='GFDataSource':
-      self._datasourceDictionary[object.name]=object
-
-      # Should we load a connection definition?
-      # This is a temporary hack :(
-      if hasattr(object,'database') and not \
-        self._databaseDictionary.has_key(object.database) and \
-        self._connections.hasConnectionParameters(object.database): 
-        conn = self._connections.getConnectionParameters(object.database)
-        self._databaseDictionary[object.database] = GFDatabase()
-        self._databaseDictionary[object.database].name = object.database
-        for attr in conn.keys():  
-          self._databaseDictionary[object.database].__dict__[attr] = conn[attr]
-
-      
-  def initDatabaseObjects(self, object):
-    if object.getObjectType() =='GFDatabase':
-      self._databaseDictionary[object.name]=object
-
-  def initTriggers(self, object):
-    if object.getObjectType() == "GFTrigger":
-      object._triggerns.update( self._triggerns )
-      object.initialize()
-      if object.src != None:
-        object.setFunctionFrom(self._triggerDictionary[object.src])
 
   #
   # end of routines made for walking
   #
-  def getDatabaseList(self):
-    return self._databaseDictionary
 
 
   def updateUIEntry(self,entry):
@@ -349,30 +295,41 @@
          entry.setValue(entry.queryDefault)      
     self._currentBlock.initQuery()
     return message
-
     
   def executeQuery(self):
+    self._app.dispatchEvent(GFEvent('beginWAIT',None));
+
     message = None
     try:
       self._currentBlock.processQuery()
-#      self.processTrigger('Post-Query')
 
     except DBError:
-      for key in self._databaseDictionary.keys():
-        self._databaseDictionary[key].rollback()
+      self.processRollback()
       message = "Database query error:\n%s\n%s " % (sys.exc_info()[0], 
sys.exc_info()[1])
 
+    self._app.dispatchEvent(GFEvent('endWAIT',None));
+
   def commit(self):
+    self._app.dispatchEvent(GFEvent('beginWAIT',None));
+
     message = None
-    for key in self._databaseDictionary.keys():
-      self._databaseDictionary[key].beginTransaction()
 
+    # TODO: Disabling transactions for now to remove need for 
_databaseDictionary
+    #
+    # TODO: Addendum by jcater: the new database drivers are assumed to be in 
a 
+    # TODO: transactional state if the underlying database supports 
transactions.
+    # TODO: Therefore, beginTransaction is no longer necessary.
+    #
+    #for key in self._databaseDictionary.keys():
+    #  self._databaseDictionary[key].beginTransaction()
+
     try:
       if not self.readonly:
         try:
           self.processTrigger('Pre-Commit')
         except TriggerError:
           GDebug.printMesg(1, "Trigger form Pre-Commit threw a TriggerError!")
+          self._app.dispatchEvent(GFEvent('endWAIT',None));
           return "Form trigger returned error"
 
         for block in self._blockList:
@@ -382,6 +339,7 @@
             block.processCommit()
           except TriggerError:
             GDebug.printMesg(1, "Trigger block Pre-Commit threw a 
TriggerError!")
+            self._app.dispatchEvent(GFEvent('endWAIT',None));
             return "Block trigger returned error"
           block.processTrigger('Post-Commit')
 
@@ -390,15 +348,16 @@
 
       else:
         message = 'Form is readonly'
+        self._app.dispatchEvent(GFEvent('endWAIT',None));
         return message
       
       self.processTrigger('Post-Commit')
-      for key in self._databaseDictionary.keys():
-        self._databaseDictionary[key].commit()
+    # TODO: Disabling transactions for now to remove need for 
_databaseDictionary
+    #  for key in self._databaseDictionary.keys():
+    #    self._databaseDictionary[key].commit()
 
     except DBError:
-      for key in self._databaseDictionary.keys():
-        self._databaseDictionary[key].rollback()
+      self.processRollback()
       message = "Database commit error:\n%s\n%s " % (sys.exc_info()[0], 
sys.exc_info()[1])
     except:
       print "\n\nGFForm: Unexpected Exception:" 
@@ -406,6 +365,8 @@
       traceback.print_exc(file=sys.stdout) 
       print '-'*60 
 
+    self._app.dispatchEvent(GFEvent('endWAIT',None));
+
     return message
 
   def executeAbout(self,event):
@@ -420,21 +381,8 @@
   def rollback(self):
     for block in self._blockList:
       block.processRollback();
-    self._currentBlock.nextRecord()
-    self._currentBlock.prevRecord()
+    self._currentBlock.jumpRecord(self._currentBlock._currentRecord)
     
-  #
-  # updateDetailBlocks
-  #
-  # This should be redone so that master blocks have detailBlocks register w/ 
them
-  # then the blocks updateDetail() could be simpler as only blocks that need 
the
-  # update get it
-  def updateDetailBlocks(self, originatingBlock):
-    if originatingBlock.mode == 'normal':
-      for block in self._blockList:
-        block.updateDetail(originatingBlock)
-        
-      # Need to force UI update here for other blocks
         
 # i question if this fuction is needed as one could just use t.msg
 # however i gave it a return and converted print to debugas some of the 
@@ -510,7 +458,6 @@
     
     if (self._currentEntry != None):
         currentvalue = self._currentEntry.getValue()
-
         if ord(value) == 13:
           if self._currentEntry.height > 1: value = '\n';
           else: return modified 
@@ -548,9 +495,6 @@
        self._currentEntry.processTrigger('pre-change')
         self._currentEntry.setValue(currentvalue)
        self._currentEntry.processTrigger('post-change')
-        # Hack alert
-        self.updateDetailBlocks(self._currentBlock)
-        # end hack alert
                                                
         modified = 1
       
@@ -578,9 +522,6 @@
       self._currentEntry.setValue(currentvalue)
       self._currentEntry.processTrigger('post-change')
       modified = 1
-      # hack alert
-      self.updateDetailBlocks(self._currentBlock)
-      # end hack alert
       return modified
 
 
@@ -588,6 +529,7 @@
   # fireTrigger
   #
   def fireTrigger(self, triggerName):
+    print self._triggerDictionary.keys()
     self._triggerDictionary[triggerName](self)
 
 #
@@ -603,7 +545,9 @@
   def nextEntry(self):
     nextEntry = None
     keepNext = 0
-    if not self._currentEntry.verifyValue(): return
+    if not self._currentEntry.verifyValue(): 
+      return "Invalid field value"
+
     for object in self._currentEntry._parent._children:
       # Put the first field as the next to rollover
       if (object.getObjectType()=='GFEntry' and
@@ -707,7 +651,7 @@
       
     # reset current entry 
     self._currentEntry = None
-    self._currentBlock.walk(self.setInitialFocus)    
+    self._currentBlock.walk(self.setFocus)    
 
     # set current page
     pageWidget = self._currentBlock
@@ -740,7 +684,7 @@
       
     # reset current entry 
     self._currentEntry = None
-    self._currentBlock.walk(self.setInitialFocus)    
+    self._currentBlock.walk(self.setFocus)    
 
     # set current page
     pageWidget = self._currentBlock
@@ -748,7 +692,6 @@
       pageWidget = pageWidget._parent
     self._currentPage = pageWidget
 
-
   def gotoPage(self, index):
     for object in self._blockList:
       pageWidget = object
@@ -762,46 +705,29 @@
 
     # reset current entry
     self._currentEntry = None
-    self._currentBlock.walk(self.setInitialFocus)
+    self._currentBlock.walk(self.setFocus)
              
   def prevRecord(self):
-    for block in self._blockList:
-      if hasattr(block,'master'):
-        masterBlock,masterField = string.split(block.master,'.')
-        if masterBlock == self._currentBlock.name:
-          if not block.isSaved():
-            message = GConfig.get('_msgNOTSAVED')
-            return message
-            #message = GFMsgBox(self, GConfig.get('_msgNOTSAVED'))
-            #message.show()
-            #return    
+    if self._currentBlock.mode == 'query': 
+      GDebug.printMesg(5,'Cannot go to previous record: in query mode!')
+      return 
     self._currentBlock.prevRecord()
 
   def nextRecord(self):
-    for block in self._blockList:
-      if hasattr(block,'master'):
-        masterBlock,masterField = string.split(block.master,'.')
-        if masterBlock == self._currentBlock.name:
-          if not block.isSaved():
-            message = GConfig.get('_msgNOTSAVED')
-            return message
+    if self._currentBlock.mode == 'query': 
+      GDebug.printMesg(5,'Cannot go to next record: in query mode!')
+      return 
     self._currentBlock.nextRecord()
 
   def jumpRecord(self,count):
-    for block in self._blockList:
-      if hasattr(block,'master'):
-        masterBlock,masterField = string.split(block.master,'.')
-        if masterBlock == self._currentBlock.name:
-          if not block.isSaved():
-            message = GConfig.get('_msgNOTSAVED')
-            return message
+    if self._currentBlock.mode == 'query': 
+      GDebug.printMesg(5,'Cannot jump to record: in query mode!')
+      return 
     self._currentBlock.jumpRecord(count)
 
-
   def toggleInsertMode(self):
     self._insertMode = not self._insertMode
 
-
   def dumpXML(self, treeDump=None, gap="  "):
-    return GObj.dumpXML(self, GFParser.xmlFormsHandler().xmlElements, 
treeDump, gap)
+    return GObj.dumpXML(self, GFParser.getXMLelements(), treeDump, gap)
 
Index: gnue/gnuef/src/GFInstance.py
diff -u gnue/gnuef/src/GFInstance.py:1.20 gnue/gnuef/src/GFInstance.py:1.21
--- gnue/gnuef/src/GFInstance.py:1.20   Thu Aug  2 21:57:35 2001
+++ gnue/gnuef/src/GFInstance.py        Sun Aug 12 12:55:27 2001
@@ -34,7 +34,7 @@
 #
 # Copyright (c) 2000 Free Software Foundation
 #
-# $Id: GFInstance.py,v 1.20 2001/08/03 04:57:35 jamest Exp $
+# $Id: GFInstance.py,v 1.21 2001/08/12 19:55:27 jamest Exp $
 #
 
 import pstats
@@ -44,8 +44,6 @@
 import urllib
 import gnue.common.GConfig
 from gnue.forms.GFForm import *
-from gnue.forms.GFGetOpt import *
-from gnue.forms.GFController import *
 from gnue.forms.GFParser import loadForm 
 from gnue.forms import VERSION
 from gnue.common import GDebug, GDataObjects
@@ -53,18 +51,19 @@
 from gnue.common.GClientApp import * 
 
 
-class GFInstance(GFController):
+class GFInstance(GFEventAware):
   # 
   # Initialize the class
   #
   def __init__(self, manager, serial, connections, ui, disableSplash):
-    GFController.__init__(self)
+    GFEventAware.__init__(self)
 
     self.connections = connections
     self.manager = manager
     self._serial = serial 
-    self._uiclass = ui
+    self._uiclass = ui.GFUserInterface
     self._disableSplash = disableSplash
+    self._uiinstance =  self._uiclass(self._disableSplash)       
     # Incomming events
     self.incommingEvent = {'requestNEXTENTRY'    : self.nextEntry,
                            'requestPREVENTRY'    : self.previousEntry,
@@ -101,10 +100,6 @@
                            'requestTOGGLECHKBOX'  : self.toggleCheckBox,       
                    
                            }
     
-    # Init database list
-    self._databaseDictionary = {}
-
-
   #
   # Associate a form object with this instance
   #
@@ -355,7 +350,7 @@
     
self.dispatchEvent(GFEvent('uiUPDATESTATUS',[None,None,self._form._insertMode,None,None,None,None]))
     
   def updateRecordCounter(self):
-    
self.dispatchEvent(GFEvent('uiUPDATESTATUS',[None,None,None,self._form._currentBlock._currentRecord+1,self._form._currentBlock._recordCount+1,None,None]))
+    
self.dispatchEvent(GFEvent('uiUPDATESTATUS',[None,None,None,self._form._currentBlock._currentRecord+1,self._form._currentBlock._recordCount,None,None]))
     
   def updatePageCounter(self):
     maxPages = len(self._form._pageList)
@@ -378,8 +373,13 @@
   def updateRecordStatus(self):
     if self._form._currentBlock.mode == 'query':
       status = 'query'
-    else:
-      status = 
self._form._currentBlock._dataSourceLink.resultSetStatus[self._form._currentBlock._currentRecord]
+    elif self._form._currentBlock._resultSet.current.isDeleted():
+      status = 'deleted'
+    elif self._form._currentBlock._resultSet.current.isPending():
+      status = 'modified' 
+    else: 
+      status = 'saved' 
+ 
     
self.dispatchEvent(GFEvent('uiUPDATESTATUS',[None,status,None,None,None,None,None]))
       
   def updateStatus(self):
@@ -499,82 +499,21 @@
     value = event.data[1]
     object.setValue(value)
     
-
-#
-# old stuff still in use
-#
-
-
   #
   #  Activate this puppy...
   #
   def activate(self): 
-    ui = self._uiclass(self._disableSplash)
+    ui = self._uiinstance
     ui.registerEventListener(self.processEvent)
     self.registerEventListener(ui.processEvent)
 
-    #
-    # extract DB info and init datasources
-    #
-    databaseList = self._form.getDatabaseList()
-    for key in databaseList.keys():
-      object = databaseList[key]
-      attempts = 4
-      while 1:
-        try:
-          #Login
-          loginData = [key, object.comment, 
-             [["user", "Username", 0], 
-              ["passwd", "Password", 1]]]
-
-          # Returned data is in format of [ Completed?, {dict of values} ]
-          self.dispatchEvent( GFEvent('getLogin', loginData) )
-          results = loginData[3]  # Set by the login manager
-          if not results[0]: 
-            print "\n\nUser cancelled login process. Exiting...\n"
-            sys.exit()
-
-          user = results[1]['user']
-          passwd = results[1]['passwd']
-          
-          object.initialize(user,passwd)
-
-        except GDataObjects.ConnectionError: 
-          attempts = attempts - 1
-          if not attempts: 
-            print "\n\nToo many unsuccessful login attempts.  Exiting...\n"
-            sys.exit()
-          message = GFMsgBox(self,"\nInvalid Login.\nPlease Try Again.  ")  
#\n\nActual Error: \n%s" % sys.exc_info()[1])
-          message.show()    
-        except DBError:
-          attempts = attempts - 1
-          if not attempts: 
-            print "\n\nToo many unsuccessful login attempts.  Exiting...\n"
-            sys.exit()
-          message = GFMsgBox(self,"Database initialization error:\n%s\n%s " % 
(sys.exc_info()[0], sys.exc_info()[1]))
-          message.show()
-        except TypeError: 
-          # For some bizarre reason, the Oracle driver 
-          # returns a TypeError for an invalid login????
-          attempts = attempts - 1
-          if not attempts: 
-            print "\n\nToo many unsuccessful login attempts.  Exiting...\n"
-            sys.exit()
-          print "Invalid Login"
-        except:
-          # All other errors: make tty sane and re-raise error
-          if sys.platform != 'win32':
-            os.system("stty sane")
-          raise
-        else:
-          break
-
-    self._form.postLoginInitialization()
-
-    # pass control to UI
     ui.buildForm(self._form)
     ui.activateForm(self._form)
+    
+    
self.dispatchEvent(GFEvent('gotoENTRY',{'object':self._form._currentEntry}))
     self.updateStatus()
     self._ui = ui
+
+    # pass control to UI
     ui.mainLoop()
 
Index: gnue/gnuef/src/GFObjects/GFBlock.py
diff -u gnue/gnuef/src/GFObjects/GFBlock.py:1.8 
gnue/gnuef/src/GFObjects/GFBlock.py:1.9
--- gnue/gnuef/src/GFObjects/GFBlock.py:1.8     Tue Aug  7 20:17:00 2001
+++ gnue/gnuef/src/GFObjects/GFBlock.py Sun Aug 12 12:55:27 2001
@@ -16,7 +16,7 @@
 # write to the Free Software Foundation, Inc., 59 Temple Place 
 # - Suite 330, Boston, MA 02111-1307, USA.
 #
-# Copyright 2000. 2001 Free Software Foundation
+# Copyright 2000, 2001 Free Software Foundation
 #
 #
 # FILE:
@@ -28,15 +28,13 @@
 # NOTES:
 #
 # HISTORY:
-# Copyright (c) 2000 Free Software Foundation
 #
 
-#from gnue.common.GObjects import * 
 from gnue.forms.GFEvent import *
 from gnue.forms.GFObjects.GFDataSource import GFDataSource
 
 from gnue.common import GDebug
-from gnue.common import GConfig
+from gnue.common import GConfig, GConditions
 from GFObj import GFObj
 
 import string
@@ -55,25 +53,40 @@
     GFObj.__init__(self, parent)
     GFEventAware.__init__(self)
     self._type = "GFBlock"
-    self.buildObject()
-    
+
+    # TODO: For some reason, self.initialize is already getting called.
+    # TODO: If we rename to something else and list here, something else
+    # TODO: breaks.  If we list it here as self.initialize, then entryList 
+    # TODO: is doubly populated, causing lots o' problems.
+    self._inits = []
+    #self._inits = [self.initialize]  
+
+
+    self._datasource = None
+    self._resultSet = None    
     self._dataSourceLink = None
     self.mode = 'normal'
     self._entryList = []
     
     self._currentRecord = 0
     self._recordCount = 0
+    self._queryValues = {}
+    self._lastQueryValues = {}
 
   def initialize(self):
     self._form = self.findParentOfType('GFForm')
     self.walk(self.buildEntryList)
 
-    if hasattr(self,'datasource'):
-      self._dataSourceLink = self._form._datasourceDictionary[self.datasource]
-    else:
-      self._dataSourceLink = GFDataSource(self)
-      self._dataSourceLink.initialize()
-    self._recordCount = self._dataSourceLink.getLastRecordNumber()
+    if not hasattr(self,'datasource') or not self.datasource: 
+      ds = GFDataSource(self)
+      self.datasource = ds.name = "dts_%s" % self
+      self._form._datasourceDictionary[ds.name] = ds
+      ds.buildObject()
+      ds.initialize()
+
+    self._dataSourceLink = self._form._datasourceDictionary[self.datasource]
+    self._dataSourceLink.registerResultSetListener(self._loadResultSet)
+
     
   def buildEntryList(self, object):
     if object.getObjectType() == 'GFEntry':
@@ -86,7 +99,7 @@
   # with is saved
   #
   def isSaved(self):
-    return self._dataSourceLink.isSaved()
+    return self._resultSet == None or not self._resultSet.isPending()
   
   #
   # deleteRecord
@@ -95,75 +108,74 @@
   # deletion during next commit
   #
   def deleteRecord(self):
-    self._dataSourceLink.markForRemoval(self._currentRecord)
+    self._resultSet.current.delete()
 
-  #
-  # newRecord
-  #
-  # Adds a record to the current records in memory
+  # 
+  # Load and process a resultset
   #
-  def newRecord(self):
-    self._dataSourceLink.new()
-    self._recordCount = self._dataSourceLink.getLastRecordNumber()
+  def _loadResultSet(self, resultSet):
+    self._resultSet = resultSet
+    self._currentRecord = -1
+    self._recordCount = 0 
 
-    if hasattr(self,"master"):
-      masterBlock,masterField = string.split(self.master,'.')
-      fieldValue = str(self._form.findValue(masterBlock, masterField))
-      if len(fieldValue) > 0:
-        
self._dataSourceLink.setField(self._recordCount,self.detail,fieldValue,TRUE)
-      
-    self._currentRecord = self._recordCount
-    adjustment = self._recordCount - self._currentRecord
-    self.switchRecord(adjustment)
-    self.processTrigger('on-newrecord')
-    
+    if self._resultSet.firstRecord(): 
+      self.switchRecord(0)
+      self._recordCount = self._resultSet.getCacheCount()
+    else:
+      # TODO: This means no results were returned from a query. 
+      # TODO: It probably shouldn't create a new record :)  Display a message?
+      print "WARNING: GFBlock.py self.newRecord - ", self.newRecord()
+
   #
   # Moves the proper record into editing position
   #
   def switchRecord(self, adjustment):
-    self._recordCount = self._dataSourceLink.getLastRecordNumber()
+    self._currentRecord = self._resultSet.getRecordNumber()
+    print "Current record is %s" % self._currentRecord
     for entry in self._entryList:
-      entry.switchRecord(self._currentRecord)
+      entry.switchRecord()
       entry.recalculateVisible( adjustment, self._currentRecord, 
self._recordCount)
       self._form.updateUIEntry(entry)
       
-#qwik hack ask jamest how to handle long term for adj 0
+    # TODO: qwik hack ask jamest how to handle long term for adj 0
     if adjustment <> 0:
       self.processTrigger('on-switch')
-    self._form.updateDetailBlocks(self)
 
+  #
+  # newRecord
+  #
+  # Adds a record to the current records in memory
+  #
+  def newRecord(self):
+    if self._resultSet.insertRecord():
+      self._recordCount = self._resultSet.getCacheCount()
+      self.switchRecord(1)
+      self.processTrigger('on-newrecord')
+    
   def nextRecord(self):
-    self._recordCount = self._dataSourceLink.getLastRecordNumber()
-    self._currentRecord = self._currentRecord + 1
-    if self._currentRecord > self._recordCount :
-      if int(GConfig.get('autocreate')) and 
(self._dataSourceLink.resultSetStatus[self._recordCount] != 'saved' or
-                (hasattr(self._dataSourceLink,'uniqueKey') and 
self._dataSourceLink.resultSet[self._recordCount].has_key(self._dataSourceLink.uniqueKey))
-                ): 
-        self.newRecord()
-      else:
-        self._currentRecord = 0
-    self.switchRecord(1)
+    if self._resultSet.nextRecord(): 
+      self._recordCount = self._resultSet.getCacheCount()
+      self.switchRecord(1)
+                                         #TODO: pending system is broken?
+    elif int(GConfig.get('autocreate')) and not 
self._resultSet.current.isEmpty(): 
+      self.newRecord()
+
+# TODO: This statement only allowed new entries 
+#   if int(GConfig.get('autocreate')) and 
(self._dataSourceLink.resultSetStatus[self._recordCount] != 'saved' or
+#    (hasattr(self._dataSourceLink,'uniqueKey') and 
self._dataSourceLink.resultSet[self._recordCount].has_key(self._dataSourceLink.uniqueKey))
                ): 
+
 
   def prevRecord(self):
-    self._recordCount = self._dataSourceLink.getLastRecordNumber()
-    self._currentRecord = self._currentRecord - 1
-    if self._currentRecord < 0 :
-      if int(GConfig.get('autocreate')) and 
(self._dataSourceLink.resultSetStatus[self._recordCount] != 'saved' or
-                (hasattr(self._dataSourceLink,'uniqueKey') and 
self._dataSourceLink.resultSet[self._recordCount].has_key(self._dataSourceLink.uniqueKey))
-                ): 
-        self.newRecord()
-      else:
-        self._currentRecord = self._recordCount    
-    self.switchRecord(-1)
+    if self._resultSet.prevRecord():
+      self.switchRecord(-1)
 
   def jumpRecord(self, recordNumber):
-    if recordNumber > self._recordCount:
-      recordNumber = self._recordCount
-    jump = recordNumber - self._currentRecord
-    self._currentRecord = recordNumber
+    if not self._resultSet.setRecord(recordNumber): 
+      self._resultSet.lastRecord()
+    jump = self._resultSet.getRecordNumber() - self._currentRecord
+    self._currentRecord = self._resultSet.getRecordNumber()
     self.switchRecord(jump)
-    
-      
+          
   #
   # processCommit
   #
@@ -171,21 +183,21 @@
     GDebug.printMesg(1, "processing commit on block %s"%self.name)
     self.mode='commit'
 
-    # if this is a detail and the key isn't filled from master block then fill 
it
-    if hasattr(self,"master"):
-      masterBlock,masterField = string.split(self.master,'.')
-      fieldValue = self._form.findValue(masterBlock, masterField)
-      
-      GDebug.printMesg(2, "Master field value is :%s:"%fieldValue)
-      if len(fieldValue) > 0:
-        for count in range(self._recordCount+1):
-          self._dataSourceLink.setField(count,self.detail,fieldValue, TRUE)
-
-    #self._currentRecord = 0
-    self._dataSourceLink.commit()
-    self._recordCount = self._dataSourceLink.getLastRecordNumber()
+    if not self._dataSourceLink.hasMaster(): 
+      self._resultSet.post()
+      self._dataSourceLink._dataObject.commit()
+
+      # TODO: What should happen after a commit? 
+      # TODO: If we stay at the current record, 
+      # TODO: how do we know the "current record" 
+      # TODO: or one of the earlier records wasn't 
+      # TODO: deleted. Or worse yet, what if our 
+      # TODO: "current" record is higher than the 
+      # TODO: actual number of records left in cache?
+      if self._currentRecord >= self._resultSet.getCacheCount():
+        self.switchRecord(-self._currentRecord)
+
     self.mode='normal'
-    #self.switchRecord(0)
 
   #
   # processRollback
@@ -193,17 +205,31 @@
   def processRollback(self):
     self._currentRecord = 0
     self._recordCount = 0
-    self._dataSourceLink.clear()
+    self._dataSourceLink.createEmptyResultSet()
     self.switchRecord(0)
 
   #
   # initQuery and processQuery
   #
   def initQuery(self):
+
+    # If Enter-Query is hit once, enter query mode
+    # If Enter-Query is hit twice, bring back conditions from last query.
+    # If Enter-Query is hit thrice, cancel the query and go into normal mode.
+
     if self.mode != 'query':
       self.mode = 'query'
-    else:
+      self._query2 = int(GConfig.get("RememberLastQuery","1"))
+      self._queryValues = {}
+    elif self._query2:
+      self._query2 = 0
+      self._queryValues = {}
+      for key in self._lastQueryValues.keys(): 
+        self._queryValues[key] = self._lastQueryValues[key]
+      self.switchRecord(0)
+    else: 
       self.mode = 'normal'
+      self.switchRecord(0)
       
     # Removed to allow the GFForm object to deside when to clear the block
     # self.processRollback() 
@@ -222,36 +248,39 @@
           
       self.mode = 'normal'
 
-      self._dataSourceLink.query()
-      self.processTrigger('Post-Query')
-      self._recordCount = self._dataSourceLink.getLastRecordNumber()
-      
-      self._currentRecord = 0
-      self.switchRecord(0)
+      self._lastQueryValues = {}
+      for key in self._queryValues.keys(): 
+        self._lastQueryValues[key] = self._queryValues[key]
+
+      conditionLike = {}
+      conditionEq = {}
+
+      # Get all the user-supplied parameters from the entry widgets
+      for entry in self._queryValues.keys():
+        if entry._bound and entry.isQueryable(): 
+          if entry.typecast == 'text': 
+            conditionLike[entry.field] = self._queryValues[entry]
+          else: 
+            conditionEq[entry.field] = self._queryValues[entry]
+
+      if len(conditionLike.keys()) and len(conditionEq.keys()): 
+        conditions = GConditions.combineConditions( \
+            GConditions.buildConditionFromDict(conditionLike, 
GConditions.GClike), \
+            GConditions.buildConditionFromDict(conditionEq, GConditions.GCeq) )
+
+      elif len(conditionLike.keys()): 
+        conditions = GConditions.buildConditionFromDict(conditionLike, 
GConditions.GClike)
 
-  #
-  # updateDetail - makes block check to see if it needs to update itself
-  # if so it forces the update automagically
-  # 
-  def updateDetail(self,originatingBlock):
-    # Block can't be master of itself
-    if self != originatingBlock and hasattr(self,'master'):
-      masterBlock,masterField = string.split(self.master,'.')
-      if masterBlock == originatingBlock.name:
-        fieldValue = str(self._form.findValue(masterBlock, masterField))
-
-        ## clear the link
-        self._dataSourceLink.clear()
-        self._recordCount = 0
-        self._currentRecord = 0
-        
-        if len(fieldValue) > 0:
-          self._dataSourceLink.setField(0,self.detail,fieldValue,TRUE)
-          self._dataSourceLink.query()
-          self._recordCount = self._dataSourceLink.getLastRecordNumber()
-          
-        self.switchRecord(0)
+      elif len(conditionEq.keys()): 
+        conditions = GConditions.buildConditionFromDict(conditionEq, 
GConditions.GCeq)
+
+      else: 
+        conditions = {}
 
+      self._dataSourceLink.createResultSet(conditions)
+      self._recordCount = self._resultSet.getCacheCount()
+
+      self.processTrigger('Post-Query')
 
 
 
Index: gnue/gnuef/src/GFObjects/GFDataSource.py
diff -u gnue/gnuef/src/GFObjects/GFDataSource.py:1.4 
gnue/gnuef/src/GFObjects/GFDataSource.py:1.5
--- gnue/gnuef/src/GFObjects/GFDataSource.py:1.4        Tue Jul 17 06:43:09 2001
+++ gnue/gnuef/src/GFObjects/GFDataSource.py    Sun Aug 12 12:55:27 2001
@@ -16,7 +16,7 @@
 # write to the Free Software Foundation, Inc., 59 Temple Place 
 # - Suite 330, Boston, MA 02111-1307, USA.
 #
-# Copyright 2000. 2001 Free Software Foundation
+# Copyright 2000, 2001 Free Software Foundation
 #
 #
 # FILE:
@@ -28,294 +28,73 @@
 # NOTES:
 #
 # HISTORY:
-# Copyright (c) 2000 Free Software Foundation
 #
 
 from gnue.common.GObjects import * 
 from gnue.common.dbdrivers.factory import factory
 
 from gnue.common import GDebug
-from gnue.common import GConfig
-from GFObj import GFObj
+from gnue.common.GDataSource import GDataSource
 
-import string
-import types
-
-
-# These should really go somewhere else
-TRUE = 1
-FALSE = 0
-
 ############################################################
 # GFDataSource
 #
-# In memory store of data manipulated by forms
-# sometimes attached to a database, sometimes not
-#
-class GFDataSource(GFObj):
-  def __init__(self, parent=None):
-    GFObj.__init__(self, parent)
-    self._type = "GFDataSource"
-
-    self._form = None
-    self.dataConnection = None
-    self.fieldList = []
-
-    self.emptyRecord = {}
-    self.resultSet = []
-    self.resultSetStatus = []
-    self.prequery = None
-
-    self.sql = None
-    
-    
-  def initialize(self):
-    self._form = self.findParentOfType('GFForm')
-
-    #self.emptyRecord = {}
-    
-    if hasattr(self,'database'):
-      GDebug.printMesg(1, "  linking to database %s" % (self.database))
-
-      self.dataConnection = self._form._databaseDictionary[self.database]
-      self.fieldList = self.dataConnection.getFieldList(self.table).keys()
-      self.uniqueKey = self.dataConnection.getUniqueKey(self.table)
-
-      # Build an empty record for new records and query mask seed
-      for field in self.fieldList:
-        self.emptyRecord[string.lower(field)] = ""
-        
-    self.clear()
-
-    if self.prequery:
-      self.query()
-
-  #
-  # new
-  #
-  # create a new, empty record
-  #
-  def new(self):
-    newentry = {}
-    for key in self.emptyRecord.keys():
-      newentry[key] = self.emptyRecord[key]
-    self.resultSet.append(newentry)
-    self.resultSetStatus.append('saved')
-
-  #
-  # clear
-  #
-  # remove all records from memory except for an initial
-  # empty record
-  #
-  def clear(self):
-    self.resultSet = []
-    self.resultSetStatus = []
-      
-    # Copy initial empty record into the resultSet
-    newentry = {}
-    for key in self.emptyRecord.keys():
-      newentry[key] = self.emptyRecord[key]
-      
-    self.resultSet.append(newentry)
-    self.resultSetStatus.append('saved')
-    
-  def getLastRecordNumber(self):
-    return len(self.resultSet)-1
-
-  #
-  # isSaved
-  #
-  # returns a positive value if all records in the datasource are saved
-  #
-  def isSaved(self):
-    value = 1
-    for count in range(len(self.resultSetStatus)):
-      if self.resultSetStatus[count] != 'saved':
-        value = 0
-        break
-    return value
-
-  #
-  # query
-  #
-  # Takes the first record in the datasource and uses it as a query mask
-  # against the database backend
-  #
-  def query(self):
-    GDebug.printMesg(2, "Query with this in resultSet\n%s"%self.resultSet[0])
-    if hasattr(self,'database'):
-      if self.sql == None:
-        mask = {}
-        for key in self.fieldList:
-          if key in self.dataConnection.getFieldList(self.table).keys():
-            mask[key]=self.resultSet[0][key]
-
-      self.clear()
-      if not hasattr(self,'order_by'):
-        self.order_by = None
-        
-      self.resultSet = self.dataConnection.query(self.table, len(mask) and 
mask or None, self.order_by)
-
-      if self.resultSet:
-        # Set each record's status
-        for count in range(len(self.resultSet)):
-          self.resultSetStatus.append('saved')
-
-        # Check first record to see if field present - if not then add one to 
all records
-        for fieldName in self.emptyRecord.keys():
-          if not self.resultSet[0].has_key(string.lower(fieldName)):
-            for result in self.resultSet:
-              result [fieldName] = ""
-
-            
-        # Reset the internal type to string so things like len() work properly
-        for count in range(len(self.resultSet)):
-          for key in self.resultSet[count].keys():
-            if self.resultSet[count][key] != None:
-              self.resultSet[count][key] = str(self.resultSet[count][key])
 
+class GFDataSource (GDataSource):
+  def __init__(self, parent):
+    GDataSource.__init__(self, parent, 'GFDataSource')
+#    self._form = None
+    self._inits =[self.primaryInit]
+    self._currentResultSet = None
+    self._resultSetListeners = []
+
+  # Called by dbdrivers whenever this datasource's master has changed
+  def masterResultSetChanged(self, masterResultSet, detailResultSet):
+    self._masterResultSet = masterResultSet
+    self.__setResultSet( detailResultSet )
 
+  def primaryInit(self):
+    self._form = self.findParentOfType('GFForm')
+    GDebug.printMesg(10,"Setting %s to connect mgr %s" 
%(self.name,self._form._connections))
+    self.setConnectionManager(self._form._connections)
+    self.initialize()
+    self.connect()
+    self._form._triggerns[self.name] = self._dataObject.triggerExtensions
+
+    if hasattr(self, 'master') and self.master:
+      GDebug.printMesg(3,"Linking detail '%s' to master '%s'" \
+                         % (self.name, self.master) )
+
+      if self._form._datasourceDictionary.has_key(self.master):
+        self._form._datasourceDictionary[self.master] \
+            .getDataObject().addDetailDataObject(self.getDataObject(),
+                                                 self)
       else:
-        self.clear()
-
-  #
-  # commit
-  #
-  # goes thru all records in the datasource and if they are new, have been 
altered, or marked
-  # for delete then it calls the appropriate GFDatabase methods to accomplish 
inserts,updates,or deletes
-  #
-  def commit(self):
-    if hasattr(self,'database'):
-      for count in range(len(self.resultSet)):
-        if self.resultSetStatus[count] in ('modified', 'deleted'):
-          GDebug.printMesg(2, "record %s is %s \n %s" % (count, 
self.resultSetStatus[count],self.resultSet[count]))
-          mask = {}
-          for key in self.resultSet[count].keys():
-            if key in self.dataConnection.getFieldList(self.table).keys() or \
-               key == self.uniqueKey:
-              mask[key]=self.resultSet[count][key]
-
-          GDebug.printMesg(10, "will be passing\n%s"% mask)
-                                                  
-          if self.resultSet[count].has_key(self.uniqueKey):
-            if self.resultSetStatus[count]  == 'modified':
-              GDebug.printMesg(1, "updating the record")
-              self.dataConnection.update(self.table,mask)
-            else:
-              GDebug.printMesg(1,"deleting the record")
-              self.dataConnection.delete(self.table,mask)
-              
-          else:
-            GDebug.printMesg(1, "Inserting the record")
-            result = self.dataConnection.insert(self.table,mask)
-
-            for newkey in result.keys():
-              self.resultSet[count][newkey] = result[newkey]
-
-      # Clean up record list
-      count = 0
-      #self.resultSetStatus = []
-      while (count < len(self.resultSet)):
-        if self.resultSet[count].has_key(self.uniqueKey) and 
self.resultSetStatus[count]=='deleted':
-          del self.resultSet[count]
-          del self.resultSetStatus[count]
-        else:
-          self.resultSetStatus[count] = 'saved'
-          
-        count = count + 1
-#          self.resultSetStatus.append('saved')
-
-      if len(self.resultSet) == 0:
-        self.clear()
-
-      # Reset the internal type to string so things like len() work properly
-      for count in range(len(self.resultSet)):
-        for key in self.resultSet[count].keys():
-          if self.resultSet[count][key] != None:
-            self.resultSet[count][key] = str(self.resultSet[count][key])
-
-  #
-  # markForRemoval
-  #
-  # Flips a specifics record number's unique key value from back and forth
-  # from positive to negative.  A negative unique key means that the record
-  # is marked for deletion on next commit
-  #
-  def markForRemoval(self,recordNumber):
-    if self.resultSetStatus[recordNumber] == 'deleted':
-      self.resultSetStatus[recordNumber] = 'modified'
-    else:
-      self.resultSetStatus[recordNumber] = 'deleted'
-    
-    #if not self.resultSet[recordNumber].has_key(self.uniqueKey):
-    #  self.resultSet[recordNumber][self.uniqueKey] = 'delete'
-    #else:
-    #  if self.resultSet[recordNumber][self.uniqueKey] == 'delete':
-    #    del self.resultSet[recordNumber][self.uniqueKey]
-    #  else:
-    #    self.resultSet[recordNumber][self.uniqueKey] = \
-    #    int(self.resultSet[recordNumber][self.uniqueKey]) * -1
-
-    #self.resultSetStatus[recordNumber] = 'deleted'
-    #GDebug.printMesg(1, "removing object: %s" % 
(self.resultSet[recordNumber][self.uniqueKey]))
-    
-  def getField(self,recordNumber,fieldName):
-    try:
-      return self.resultSet[recordNumber][string.lower(fieldName)] or ''
-    except:
-      return ''
-
-  #
-  # setField
-  #
-  # The internal call argument allows other objects to
-  # adjust field values w/o altering the entries state.
-  # mainly usefull to set default values or master/detail
-  # values
-  #
-  def setField(self,recordNumber,fieldName,value,internalCall=FALSE):
-    if len(self.resultSet) > recordNumber:
-
-      #CheckBox does not work if uncomment these (ra3vat)
-      #if len(value) == 0:
-      #  value = None
-
-
-      # Believed fixed but leaving comment in for now just in case - jamest
-      # Hack warning - the value != None is catching an error in the code
-      # where none is being stored for some reason in widgets.  Need to fix in 
proper
-      # place.  Without this hack the rollback doesn't work properly and the 
current
-      # block is never in a state of Saved after a rollback
-      #if self.resultSet[recordNumber][fieldName] != value and value != None:
-
-      # Commenting out for now - not sure why we're doing this
-      #if fieldName in self.dataConnection.getFieldList(self.table).keys() or \
-      #   fieldName == self.uniqueKey:
-      if self.resultSet[recordNumber][string.lower(fieldName)] != value:
-        self.resultSet[recordNumber][string.lower(fieldName)] = value
-        if not internalCall: 
-          self.resultSetStatus[recordNumber] = 'modified'
-        #
-        # Eventually we need to lock that record in the datasource here
-        #
-        
-  #
-  # addField
-  #
-  # Adds a named field to a datasource
-  # 
-  def addField(self,fieldName):
-    if not fieldName in self.fieldList:
-      for count in range(len(self.resultSet)):
-        self.resultSet[count][string.lower(fieldName)] = ""
-      self.emptyRecord[string.lower(fieldName)]=""
-      self.fieldList.append(fieldName)
-
-
-
-
-
+        raise StandardError, \
+           "Detail source '%s' references non-existant master '%s'" \
+                         % (self.name, self.master)
+
+
+    print "GFDataSource.py - dataobject name ",self.getDataObject().name
+
+  def createResultSet(self, conditions={}): 
+    resultSet = GDataSource.createResultSet(self, conditions=conditions)
+    self.__setResultSet( resultSet )
+    return resultSet
+
+  def createEmptyResultSet(self): 
+    resultSet = GDataSource.createEmptyResultSet(self)
+    self.__setResultSet( resultSet )
+    return resultSet
+
+  def __setResultSet(self, resultSet): 
+    self._currentResultSet = resultSet
+    # Notify all the listeners (i.e., blocks) that the result set changed
+    for listener in self._resultSetListeners: 
+      listener(resultSet)
+  
 
+  def registerResultSetListener(self, listener): 
+    self._resultSetListeners.append(listener)
 
 
Index: gnue/gnuef/src/GFObjects/GFEntry.py
diff -u gnue/gnuef/src/GFObjects/GFEntry.py:1.7 
gnue/gnuef/src/GFObjects/GFEntry.py:1.8
--- gnue/gnuef/src/GFObjects/GFEntry.py:1.7     Tue Jul 31 16:22:30 2001
+++ gnue/gnuef/src/GFObjects/GFEntry.py Sun Aug 12 12:55:27 2001
@@ -16,7 +16,7 @@
 # write to the Free Software Foundation, Inc., 59 Temple Place 
 # - Suite 330, Boston, MA 02111-1307, USA.
 #
-# Copyright 2000. 2001 Free Software Foundation
+# Copyright 2000, 2001 Free Software Foundation
 #
 #
 # FILE:
@@ -49,17 +49,17 @@
     self._type = "GFEntry"
     self.height = float(GConfig.get('widgetHeight'))
     self.width = float(GConfig.get('widgetWidth'))
-    self._value=""
+    self._oldval=""
     self._uppercase = 0
     self._lowercase = 0
     self._numeric = 0
-    self._rows = 1
+    self._queryable = 1
     self.typecast="text"
     self.case="mixed"
+    self._inits = [self.initialize, self.loadStartingValues]
 
 
   def buildObject(self): 
-
     # Convert deprecated attributes
     if hasattr(self,'numeric') and self.numeric: 
       del self.numeric
@@ -72,14 +72,7 @@
       del self.lowercase
       self.case = 'lower' 
 
-    if not hasattr(self, 'rows') and hasattr(self,'visibleCount'): 
-      self.rows = self.visibleCount
-      del self.visibleCount
-
-
-
   def initialize(self):
-
     if self.typecast == 'number': 
       self._numeric = 1
     if self.case == 'upper': 
@@ -87,60 +80,90 @@
     elif self.case == 'lower': 
       self._lowercase = 1
 
-    self._cursorPosition = len(self._value)
+    self._cursorPosition = len(self._oldval)
     self._block = self.findParentOfType('GFBlock')
     if not hasattr(self,'field'):
       GDebug.printMesg(1,"%s" % self.getObjectType())
       GDebug.printMesg(1,"%s" % self.__dict__)
-      self.field = self.name
+      self.field = "__GNUe__%s" % self.name
+      self._block._dataSourceLink.referenceUnboundField(self.field)
+      self._bound = 0
+    else: 
+      self._block._dataSourceLink.referenceField(self.field)
+      self._bound = 1
 
-    self._block._dataSourceLink.addField(self.field)
     if hasattr(self,'foreign_key'):
       datasourceName,self.fieldName = string.split(self.foreign_key,'.')
       self.datasource = self._block._form._datasourceDictionary[datasourceName]
 
-    if not hasattr(self,'rows'):
-      if hasattr(self._block,'rows'):
-        self._rows = self._block.rows
+    if not hasattr(self,'rows') and hasattr(self._block,'rows'):
+        self.rows = self._block.rows
     else:
-      self._rows = self.rows
+      self.rows = 1
       
     if not hasattr(self,'rowSpacer'):
       if hasattr(self._block,'rowSpacer'):
         self.gap = self._block.rowSpacer
     else:
       self.gap = self.rowSpacer
-      
-    #following check does not work if move it to GFEntry.__init__ (ra3vat) 
-    if hasattr(self, 'style'):
-      if self.style=='checkbox':
-        self._value = 0
 
+  def isQueryable(self): 
+    if self._queryable and len(self._oldval): 
+      return 1
+
+  #
+  # Once the phase 1 init is complete for the tree
+  # then attempt to load the proper values
+  #
+  def loadStartingValues(self):
+    pass
+#    value = self.getValue()
+#    if value=='' and hasattr(self, 'style') and self.style=='checkbox':
+#       value = 0
+#    self.setValue(value)
+
+
+  #
+  # Pulls the proper value from the resultSet
+  #
   def getValue(self):
+    if self._block.mode == 'query': 
+      if self._block._queryValues.has_key(self): 
+        value = self._block._queryValues[self]
+      else: 
+        value = None
+    else: 
+      value = self._block._resultSet.current.getField(self.field)
+
+    if value == None:       value = ''
+
     #CheckBox does not work if comment following "if" block (ra3vat)
-    if self._value=='':
-      if hasattr(self, 'style') and self.style=='checkbox':
-        self._value = 0
-    GDebug.printMesg(10, "Value = %s"% self._value)
-    return self._value
+    if value == '' and self.style == 'checkbox':      value = 0
+
+    self._oldval = value
+    return value
 
 
   def setValue(self, value):
-    if value=='':
-      if hasattr(self, 'style') and self.style=='checkbox':
-        value = 0
-    self._value = value
-    self._block._dataSourceLink.setField(self._block._currentRecord,
-                                       self.field,self._value)
+    if value =='' and hasattr(self, 'style') and self.style=='checkbox':
+      value = 0
+    self._oldval = value
+    if self._block.mode == 'query': 
+      self._block._queryValues[self] = value
+    else: 
+      self._block._resultSet.current.setField(self.field,value)
     self._block._form.updateUIEntry(self)
 
   #
   # switchRecord
   #
-  def switchRecord(self, newRecord):
-    GDebug.printMesg(2, "Switching to record %s" % (newRecord))
-    self._value = self._block._dataSourceLink.getField(newRecord,self.field)
-    self._cursorPosition = len(self._value)
+  def switchRecord(self):
+    value = self._block._resultSet.current.getField(self.field) 
+    if value == None: 
+      value = ''
+    else: 
+      value = "%s" % value
+    self._cursorPosition = len(value)
 
   #
   # verifyValue
@@ -149,15 +172,15 @@
     value = 1
 
     # If empty skip test
-    if len(self._value) != 0:
+    if len(self._oldval) != 0:
 
       if hasattr(self,'foreign_key'):
         value = 0
         datasourceName,fieldName = string.split(self.foreign_key,'.')
         datasource = self._block._form._datasourceDictionary[datasourceName]
 
-        for count in range(datasource.getLastRecordNumber()+1):
-          if datasource.getField(count,fieldName) == self._value:
+        for count in range(self._block._form._currentBlock._recordCount+1):
+          if datasource.getField(count,fieldName) == self._oldval:
             value = 1
             break        
     return value
@@ -168,28 +191,40 @@
   def findClosestValue(self, value):
     newvalue=None
     if hasattr(self,'foreign_key'):
+
       datasourceName,fieldName = string.split(self.foreign_key,'.')
-      datasource = self._block._form._datasourceDictionary[datasourceName]
-      for count in range(datasource.getLastRecordNumber()+1):
-        if 
string.lower(datasource.getField(count,self.foreign_key_description)[:len(value)])
 == string.lower(value):
-          newvalue = datasource.getField(count,fieldName)
-          if newvalue == self._value: continue
+      rs = 
self._block._form._datasourceDictionary[datasourceName]._currentResultSet
+ 
+      more = rs.firstRecord()
+      while more: 
+        if 
string.lower(rs.current.getField(self.foreign_key_description)[:len(value)]) == 
string.lower(value):
+          newvalue = rs.current.getField(fieldName)
+          if newvalue == self._oldval: continue
           break
+        more = rs.nextRecord()
     return newvalue
                                                     
   #
   # allowedValues
   #
   def allowedValues(self):
-    values = {}
-    values[""]=""
-    for count in range(self.datasource.getLastRecordNumber()+1):
-      try:
-        values[self.datasource.getField(count,self.fieldName)] = \
-         self.datasource.getField(count,self.foreign_key_description)
-      except AttributeError:
-        values[self.datasource.getField(count,self.fieldName)] = None
-    return values
+
+    if not hasattr(self.datasource,'_allowedValues'): 
+      rs = self.datasource.createResultSet()
+      self.datasource._allowedValues = {"":""}
+
+      more = rs.firstRecord()
+
+      while more: 
+        try:
+          self.datasource._allowedValues["%s" % 
rs.current.getField(self.fieldName)] = "%s" % \
+             rs.current.getField(self.foreign_key_description)
+        except AttributeError:
+          self.datasource._allowedValues[rs.current.getField(self.fieldName)] 
= None
+        more = rs.nextRecord()
+    GDebug.printMesg (5,'Created for DropDown: %s' % 
self.datasource._allowedValues)
+
+    return self.datasource._allowedValues
 
 
 
Index: gnue/gnuef/src/GFObjects/GFLabel.py
diff -u gnue/gnuef/src/GFObjects/GFLabel.py:1.4 
gnue/gnuef/src/GFObjects/GFLabel.py:1.5
--- gnue/gnuef/src/GFObjects/GFLabel.py:1.4     Tue Jul 31 16:22:30 2001
+++ gnue/gnuef/src/GFObjects/GFLabel.py Sun Aug 12 12:55:27 2001
@@ -41,6 +41,8 @@
   def __init__(self, parent=None, value=None):
     GFValue.__init__(self, parent, value)
     self._type = "GFLabel"
+    self._inits = [self.initialize]
+
     self.alignment = "left"
     self.height = float(GConfig.get('widgetHeight'))
     self._rows = 1
@@ -54,6 +56,9 @@
     if not self._value:
       GFValue.setValue(self,value)
 
+  #
+  # Routines called during a phaseInit
+  #
   def initialize(self):
     self._block = self.findParentOfType('GFBlock')
     if not hasattr(self,'rows'):
Index: gnue/gnuef/src/GFObjects/GFObj.py
diff -u gnue/gnuef/src/GFObjects/GFObj.py:1.4 
gnue/gnuef/src/GFObjects/GFObj.py:1.5
--- gnue/gnuef/src/GFObjects/GFObj.py:1.4       Tue Jul 31 16:22:30 2001
+++ gnue/gnuef/src/GFObjects/GFObj.py   Sun Aug 12 12:55:27 2001
@@ -16,7 +16,7 @@
 # write to the Free Software Foundation, Inc., 59 Temple Place 
 # - Suite 330, Boston, MA 02111-1307, USA.
 #
-# Copyright 2000. 2001 Free Software Foundation
+# Copyright 2000, 2001 Free Software Foundation
 #
 #
 # FILE:
@@ -28,7 +28,6 @@
 # NOTES:
 #
 # HISTORY:
-# Copyright (c) 2000 Free Software Foundation
 #
 
 from gnue.common.GObjects import GObj
@@ -48,6 +47,7 @@
     self.readonly = 0
     self._visibleIndex = 0
     self.name = "__%s__" % self
+    self._inits = [self.initialize]
 
   #
   # get an Option 
@@ -72,8 +72,8 @@
       if index < 0 : index = 0
 
       # Don't let index pass the number of widgets on screen
-      if index >= int(self._rows):
-        index = int(self._rows)-1
+      if index >= int(self.rows):
+        index = int(self.rows)-1
 
       # Don't let the index past the number of records in the
       # system
@@ -82,25 +82,25 @@
       if lowestVisible + index > recordCount:
         index = index -1
 
+
       # If the current record has rolled around
       # from the top to the bottom then reset
       # the counter
       if currentRecord == 0:
         index = 0
 
+
       # if the current record has rolled from
       # bottom to top then flip to bottom keeping
       # in mind the number of records in memory
       if currentRecord == recordCount:
-        if recordCount > int(self._rows)-1:
-          index = int(self._rows)-1
+        if recordCount > int(self.rows)-1:
+          index = int(self.rows)-1
         else:
           index = recordCount
-        
+
       self._visibleIndex = index
       self.lowestVisible = lowestVisible
-
-
 
 
 
Index: gnue/gnuef/src/GFParser.py
diff -u gnue/gnuef/src/GFParser.py:1.51 gnue/gnuef/src/GFParser.py:1.52
--- gnue/gnuef/src/GFParser.py:1.51     Thu Aug  2 20:02:28 2001
+++ gnue/gnuef/src/GFParser.py  Sun Aug 12 12:55:27 2001
@@ -151,7 +151,6 @@
          'BaseClass': GFObjects.GFDataSource,  
          'Attributes': GDataSource.tagAttributes, 
          'ParentTags': ('form',) },
-
       'page': { 
          'BaseClass': GFObjects.GFPage,
          'Required': 1,
Index: gnue/gnuef/src/GFTrigger.py
diff -u gnue/gnuef/src/GFTrigger.py:1.44 gnue/gnuef/src/GFTrigger.py:1.45
--- gnue/gnuef/src/GFTrigger.py:1.44    Thu Aug  2 20:02:28 2001
+++ gnue/gnuef/src/GFTrigger.py Sun Aug 12 12:55:27 2001
@@ -62,7 +62,6 @@
 class GFTriggerAware:
   def __init__(self):
     self._trigger = {}
-
     self._validTriggers = VALIDTRIGGERS.keys()
 
   # addTrigger
@@ -103,6 +102,7 @@
     self.src = src
     self.type = type  
     self.name = name
+    self._inits   = [None,self.initialize]
     if text != None: 
       GContent(self, text)
     if self.type != None: 
@@ -117,10 +117,18 @@
         self.name = self.id
       del self.__dict__['id']  
 
+  #
+  # Must be at least a phase 2 init
+  #
   def initialize(self): 
+    print "GFTrigger.py - initialize"
+    self._form = self.findParentOfType('GFForm')
+    self._triggerns.update( self._form._triggerns )
     self.__call__ = self.dummyFunction
 
-    if self.type!="NAMED":
+    print "GFTrigger.py ",self.type
+    
+    if self.type != "NAMED":
       if self._parent:
         self._parent.addTrigger( self.type, self )
     else:
@@ -128,6 +136,9 @@
 
     if self.src == None: 
       self.setFunction( self.getChildrenAsContent(), self.language )
+    else:
+      self.setFunctionFrom(self._form._triggerDictionary[self.src])
+
 
   def setFunctionFrom(self, object):
     self.__call__=object.__call__
Index: gnue/gnuef/src/UIbase.py
diff -u gnue/gnuef/src/UIbase.py:1.42 gnue/gnuef/src/UIbase.py:1.43
--- gnue/gnuef/src/UIbase.py:1.42       Tue Jul 31 16:22:30 2001
+++ gnue/gnuef/src/UIbase.py    Sun Aug 12 12:55:27 2001
@@ -50,14 +50,14 @@
                            'updateENTRY'     : self.updateEntry,
                            'gotoPAGE'        : self.gotoPage,
                            'formALERT'       : self.formAlert,
-                           'requireLOGIN'    : self.getLogin,
+                           'beginWAIT'       : self.beginWait, 
+                           'endWAIT'         : self.endWait, 
                            'uiNEXTRECORD'    : self.adjustMultiView,
                            'uiPREVRECORD'    : self.adjustMultiView,
                            'uiUPDATESTATUS'  : self.updateStatusBar,
                            'uiABOUT'         : self.aboutBox,
                            'exitApplication' : self.exitApplication,
-                           'msgBoxUI'        : self.msgBox,
-                           'getLogin'        : self.getLogin
+                           'msgBoxUI'        : self.msgBox
                            }
 
     #
@@ -276,7 +276,7 @@
     index = event.data._visibleIndex
 
     block = event.data._block
-    currentRecord = block._currentRecord
+    currentRecord = block._resultSet.getRecordNumber()
 
     if hasattr(event.data, 'style'):
       style = event.data.style
@@ -284,22 +284,24 @@
       style = 'text'
 
     # Fill the prior spots
-    for count in range(index):
-      value = 
block._dataSourceLink.getField(currentRecord-(index-count),event.data.field)
-      value = str(value) # Done to remove unicode types which wxpython doesn't 
like
-      if style == 'dropdown':
-        value = event.data.allowedValues()[value]
-      elif style == 'checkbox':
-        if value in (1,'1','T','t','TRUE','true'):
-          value = 1
-        else:
-          value = 0          
+    if block.mode != 'query': 
+      for count in range(index):
+        value = 
block._resultSet.getRecord(currentRecord-(index-count)).getField(event.data.field)
+        if value == None: value = ""
+        value = str(value) # Done to remove unicode types which wxpython 
doesn't like
+        if style == 'dropdown':
+          value = event.data.allowedValues()[value]
+        elif style == 'checkbox':
+          if value in (1,'1','T','t','TRUE','true'):
+            value = 1
+          else:
+            value = 0          
 
-      self._formToUIWidget[event.data].setValue(value,count)
+        self._formToUIWidget[event.data].setValue(value,count)
 
     # fill currently indexed spot
     if style == 'dropdown':
-      value = event.data.allowedValues()[event.data.getValue()]
+      value = event.data.allowedValues()["%s" % event.data.getValue()]
     else:
       value = event.data.getValue()
     value = str(value) # Done to remove unicode types which wxpython doesn't 
like
@@ -321,26 +323,40 @@
     # you do not want to just add 1 to count
     # as the formulas would then be off
     count = index
-    while count < int(event.data._rows):
+    while count < int(event.data.rows):
       if count != index:
-        if currentRecord+(count-index) > block._recordCount:
+        rec = block._resultSet.getRecord(currentRecord+(count-index))
+        if rec == None:
           if style == 'checkbox':
             value = int(0)
           else:  
             value = ""
         else:
-          value = 
block._dataSourceLink.getField(currentRecord+(count-index),event.data.field)
+          value = rec.getField(event.data.field)
+          if value == None: value = ""
           value = str(value) # Done to remove unicode types which wxpython 
doesn't like
           if style == 'dropdown':
             value = event.data.allowedValues()[value]
           elif style == 'checkbox':
-           if value in (1,'1','T','t','TRUE','true'):
-             value = 1
+            if value in (1,'1','T','t','TRUE','true'):
+              value = 1
             else:
               value = 0          
         self._formToUIWidget[event.data].setValue(value, count)
       count = count +1
-                                                                               
                                                                   
+
+
+  # Called whenever forms goes into a "wait" state in which user cannot 
+  # interact with interface (e.g., while waiting for a query or a commit)
+  def beginWait (self, event): 
+    pass
+
+
+  # Called whenever forms leaves a "wait" state 
+  def endWait (self, event): 
+    pass
+
+                                               
 #############################################################################
 #
 # UIWidget class
@@ -352,10 +368,10 @@
 class UIWidget:
   def __init__(self, object, container, textWidth, textHeight, widgetWidth, 
widgetHeight, interface, initialize=1):
     self.widgets = []
-    if not hasattr(object,'_rows'):
-      object._rows = 1
+    if not hasattr(object,'rows'):
+      object.rows = 1
     else:
-      object._rows = int(object._rows)
+      object.rows = int(object.rows)
 
     if hasattr(object,'gap'):
       object.gap = int(object.gap)
@@ -370,7 +386,7 @@
       self.itemHeight = -1
       
     if not object.hidden:
-      for spacer in range(int(object._rows)):
+      for spacer in range(int(object.rows)):
         newWidget = self.createWidget(object, container, textWidth, 
textHeight, widgetWidth, widgetHeight, interface, spacer, initialize)
         self.widgets.append(newWidget)
         
Index: gnue/gnuef/src/UIwxpython.py
diff -u gnue/gnuef/src/UIwxpython.py:1.106 gnue/gnuef/src/UIwxpython.py:1.107
--- gnue/gnuef/src/UIwxpython.py:1.106  Thu Aug  2 20:02:28 2001
+++ gnue/gnuef/src/UIwxpython.py        Sun Aug 12 12:55:27 2001
@@ -42,6 +42,32 @@
 _NOTEBOOK = None
 _LOOPTRAP = 0
 
+
+
+##
+## Only create one instance of wxApp
+## Use getWxApp() to get or create the
+## single instance
+
+class GFwxApp(wxApp): 
+  def OnInit(self): 
+    GDebug.printMesg(7,"WxApp initializing")
+    wxInitAllImageHandlers()
+    return true
+
+__wxApp = None
+
+def getWxApp(): 
+  global __wxApp
+
+  if __wxApp == None: 
+    __wxApp = GFwxApp(0)
+    __wxApp.MainLoop()
+
+  return __wxApp
+
+
+
 #
 # Little global helper routine to set font according to options
 #
@@ -79,13 +105,12 @@
 # The public interface to the User Interface
 # All UIs must provide this class
 #
-class GFUserInterface(wxApp,GFUserInterfaceBase):
+class GFUserInterface(GFUserInterfaceBase):
   def __init__(self, disableSplash = None):
+    self._wxapp = getWxApp()
     self._disableSplash = disableSplash
-    wxApp.__init__(self,0)
     GFUserInterfaceBase.__init__(self)
 
-    self._loginHandler = wxLoginHandler()
     self.keyEvents = {WXK_PRIOR:  GFEvent('requestPREVBLOCK'),
                       WXK_NEXT:   GFEvent('requestNEXTBLOCK'),
                       WXK_TAB:    GFEvent('requestNEXTENTRY'),                 
       
@@ -109,6 +134,10 @@
                       WXK_F12:    GFEvent('requestNEWRECORD'),
                      }
     self.forms = {}
+
+    self.init()
+
+    #self._loginHandler = wxLoginHandler()
   #
   # mainLoop
   #
@@ -116,7 +145,7 @@
   # fully activated
   #
   def mainLoop(self):
-    self.MainLoop() # simply call the wxApp's MainLoop method
+    self._wxapp.MainLoop() # simply call the wxApp's MainLoop method
 
   #
   # getModule
@@ -125,21 +154,21 @@
     return GFUserInterface.__module__
 
   #
-  # OnInit
+  # init
   #
   # Routine called by wxWindows when wxApp instance is created
   # It is used the build the basic UI
   #
-  def OnInit(self):
-    wxInitAllImageHandlers()
+  def init(self):
     
     #
     # SplashScreen
     # 
     if not self._disableSplash:
+      print GConfig.get('splashScreenPNG')
       self.splash = 
SplashScreen(None,bitmapfile=GConfig.get('splashScreenPNG'),
                                  # duration=65535, callback=self.onMain,
-                                 duration = 2000, callback=self.onCloseSplash, 
+                                 duration = 4000, callback=self.onCloseSplash, 
                                  style=wxSIMPLE_BORDER|wxCENTRE_ON_SCREEN )
       swidth,sheight = self.splash.GetSizeTuple()
 
@@ -212,41 +241,41 @@
 
     fileMenu = wxMenu()
     fileMenu.Append( 100, "Commit     (F6)",GConfig.get('msg_commit'))
-    EVT_MENU(self, 100, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestCOMMIT')))
+    EVT_MENU(self._wxapp, 100, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestCOMMIT')))
     fileMenu.Append( 102, "Rollback  (F11)",GConfig.get('msg_rollback'))
-    EVT_MENU(self, 102, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestROLLBACK')))
+    EVT_MENU(self._wxapp, 102, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestROLLBACK')))
     fileMenu.AppendSeparator()
     fileMenu.Append( 104, "&Print",GConfig.get('msg_print'))
-    EVT_MENU(self, 104, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestPRINTOUT')))
+    EVT_MENU(self._wxapp, 104, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestPRINTOUT')))
     fileMenu.AppendSeparator()
     fileMenu.Append( 101, "E&xit",GConfig.get('msg_exit'))
-    EVT_MENU(self, 101,  self.menuExitEvent)
+    EVT_MENU(self._wxapp, 101,  self.menuExitEvent)
      
     dataMenu = wxMenu()
     dataMenu.Append( 201, "Next Record             
(Up)",GConfig.get('msg_next'))
-    EVT_MENU(self, 201, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestNEXTRECORD')))
+    EVT_MENU(self._wxapp, 201, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestNEXTRECORD')))
     dataMenu.Append( 202, "Previous Record   
(Down)",GConfig.get('msg_previous'))
-    EVT_MENU(self, 202, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestPREVRECORD')))
+    EVT_MENU(self._wxapp, 202, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestPREVRECORD')))
     dataMenu.Append( 209, "Jump to Record...     (F2)",GConfig.get('msg_jump'))
-    EVT_MENU(self, 209, self.promptForRecordNumber)
+    EVT_MENU(self._wxapp, 209, self.promptForRecordNumber)
     dataMenu.Append( 203, "New Record           
(F12)",GConfig.get('msg_insert'))
-    EVT_MENU(self, 203, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestNEWRECORD')))
+    EVT_MENU(self._wxapp, 203, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestNEWRECORD')))
     dataMenu.Append( 204, "Delete Record          
(F5)",GConfig.get('msg_delete'))
-    EVT_MENU(self, 204, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestMARKFORDELETE')))
+    EVT_MENU(self._wxapp, 204, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestMARKFORDELETE')))
     dataMenu.AppendSeparator()
     dataMenu.Append( 205, "Next Block           
(PgDn)",GConfig.get('msg_next_block'))
-    EVT_MENU(self, 205, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestNEXTBLOCK')))
+    EVT_MENU(self._wxapp, 205, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestNEXTBLOCK')))
     dataMenu.Append( 206, "Previous Block     
(PgUp)",GConfig.get('msg_previous_block'))
-    EVT_MENU(self, 206, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestPREVBLOCK')))
+    EVT_MENU(self._wxapp, 206, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestPREVBLOCK')))
     dataMenu.AppendSeparator()
     dataMenu.Append( 207, "Enter Query            
(F8)",GConfig.get('msg_query_prep'))
-    EVT_MENU(self, 207, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestQUERY')))
+    EVT_MENU(self._wxapp, 207, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestQUERY')))
     dataMenu.Append( 208, "Execute Query        (F9)", 
GConfig.get('msg_query'))
-    EVT_MENU(self, 208, lambda event, l=self: 
l.dispatchEvent(GFEvent('executeQUERY')))
+    EVT_MENU(self._wxapp, 208, lambda event, l=self: 
l.dispatchEvent(GFEvent('executeQUERY')))
     
     helpMenu = wxMenu()
     helpMenu.Append( 300, "&About...", GConfig.get('msg_help'))
-    EVT_MENU(self, 300, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestABOUT')))
+    EVT_MENU(self._wxapp, 300, lambda event, l=self: 
l.dispatchEvent(GFEvent('requestABOUT')))
 
     menuBar = wxMenuBar()
     menuBar.Append( fileMenu, "&File" )
@@ -279,12 +308,11 @@
     mainToolBar.AddSimpleTool(102, wxImage(GConfig.get('tb_rollback'), 
wxBITMAP_TYPE_PNG).ConvertToBitmap(), "Rollback Changes", 
GConfig.get('msg_rollback'))
     mainToolBar.Realize()
 
-
     self.mainWindow.panel = wxPanel(self.mainWindow,-1, 
wxDefaultPosition,self.mainWindow.GetSize())
     EVT_CHAR(self.mainWindow.panel, self.uiEventTrap)
 
     self.currentWidget = [self.mainWindow.panel]
-    self.SetTopWindow(self.mainWindow)
+    self._wxapp.SetTopWindow(self.mainWindow)
 
     EVT_CLOSE(self.mainWindow,self.closeTrap)
 
@@ -311,8 +339,6 @@
     self.mainWindow.panel.SetSize(wxSize(width*int(self.widgetWidth),
                               
int(height+self.menu_sb_space)*int(self.widgetHeight)))
     self._pageList[0].Show(TRUE)
-    self.dispatchEvent(GFEvent('requestPREVRECORD'))
-    self.dispatchEvent(GFEvent('requestNEXTRECORD'))
 
     # Only one page at a time can be visible
     self.visiblePage = self._pageList[0]
@@ -326,6 +352,9 @@
         child.Fit()
         child = child.GetParent()    
 
+    self.mainWindow.CenterOnScreen()
+
+
   #############################################################################
   #
   # Private UIBase support functions
@@ -611,164 +640,19 @@
       
     if action != None:
       self.dispatchEvent(action)
- 
-  #############################################################################
-  #
-  # Login support
-  #
-  # All methods below this point are only used to present the initial login box
-  #
-
-  #
-  # getLogin
-  #
-  # creates the login dialog and displays it modal
-  #
-  def getLogin(self, event):
-    self.loginData = event.data
-
-
-    if len(self.loginData[1]): 
-#      loginMesg = 'Login required for "%s" (%s)' % (self.loginData[1], 
self.loginData[0])
-      loginMesg = 'Login required for "%s"' % (self.loginData[1])
-    else: 
-      loginMesg = 'Login required for %s' % (self.loginData[0])
-
-    self.dlg = wxDialog(NULL, -1, "GNU Enterprise: Login")
-#    self.dlg.SetBackgroundColour(wxWHITE)
-    self.dlg.SetAutoLayout(true)
-    
-    bmp = wxImage(GConfig.get('smallPNG'), wxBITMAP_TYPE_PNG).ConvertToBitmap()
-
-
-    self.textctrlList = []
-    messageField = wxStaticText(self.dlg, 1010, str(loginMesg))
-    labelList = []
-
-    dlgWidth = getLargest(bmp.GetWidth(), messageField.GetSize().GetWidth()+20)
-
-    dlgHeight = bmp.GetHeight() + messageField.GetSize().GetHeight() + 80
-
-    xSpacing = 0
-    ySpacing = 0
-    fieldLabelWidth = 0
-
-    for prompt in self.loginData[2]: 
-      s = wxStaticText(self.dlg, 1010, '%s:' % prompt[1])
-      labelList.append(s)
-      if prompt[2]: 
-        t = wxTextCtrl(self.dlg, -1,"",wxPoint(1, 1), wxSize(150, 20), 
style=wxTE_PASSWORD|wxTE_PROCESS_ENTER)
-      else:
-        t = wxTextCtrl(self.dlg, -1,"",wxPoint(1, 1), wxSize(150, 20), 
style=wxTE_PROCESS_ENTER)
-      myID = len(self.textctrlList)
-      self.textctrlList.append(t)
-      EVT_CHAR(t, LoginFieldHandler(self, myID).loginFieldEventTrap)
-
-      fieldLabelWidth = getLargest(fieldLabelWidth, \
-           s.GetSize().GetWidth() + t.GetSize().GetWidth() + 10)   
-
-      dlgWidth = getLargest(dlgWidth, \
-           s.GetSize().GetWidth() + t.GetSize().GetWidth() + 10) 
-
-      xSpacing = getLargest(xSpacing, s.GetSize().GetWidth())
-      ySpacing = getLargest(ySpacing, s.GetSize().GetHeight())
-      ySpacing = getLargest(ySpacing, t.GetSize().GetHeight())
-
-
-    loginButton = wxButton(self.dlg,19998,'Login')
-    cancelButton = wxButton(self.dlg,19999,'Cancel')
 
-    EVT_BUTTON(self.dlg, 19998, self.loginButtonEventTrap)                
-    EVT_BUTTON(self.dlg, 19999, self.loginCancelEventTrap)                
-    dlgWidth = getLargest(dlgWidth, loginButton.GetSize().GetWidth() + 
-                          cancelButton.GetSize().GetWidth() + 6) + 20
-
-    dlgHeight = dlgHeight + \
-               getLargest(loginButton.GetSize().GetHeight(), 
-                          cancelButton.GetSize().GetHeight()) - 6
-
-    firstY = bmp.GetHeight() + messageField.GetSize().GetHeight() + 50
-    lastY = firstY
-    xSpacing = xSpacing + 10   # Add whitespace between widgets
-    ySpacing = ySpacing + 6    # Add whitespace between widgets
-    xPos = dlgWidth/2 - fieldLabelWidth/2
-
-
-    # Move the fields and labels into position
-    for i in range(0, len(self.textctrlList)):
-      dlgHeight = dlgHeight + ySpacing
-      labelList[i].SetPosition(wxPoint(xPos, lastY))
-      self.textctrlList[i].SetPosition(wxPoint(xPos + xSpacing, lastY))
-      lastY = lastY + ySpacing
-
-
-    # Create and position the logo
-    wxStaticBitmap(self.dlg,-1, bmp, 
-                   wxPoint((dlgWidth-bmp.GetWidth())/2, 12),
-                   wxSize(bmp.GetWidth(), bmp.GetHeight()))
 
+  # Called whenever forms goes into a "wait" state in which user cannot 
+  # interact with interface (e.g., while waiting for a query or a commit)
+  def beginWait (self, event): 
+    wxBeginBusyCursor()
+
+  # Called whenever forms leaves a "wait" state 
+  def endWait (self, event): 
+    wxEndBusyCursor()
 
-    # Move the various widgets into position
-    messageField.SetPosition(
-        wxPoint(dlgWidth/2 - messageField.GetSize().GetWidth()/2, 
-                30 + bmp.GetHeight()))
 
 
-    cancelButton.SetPosition(
-        wxPoint(dlgWidth - 10 - cancelButton.GetSize().GetWidth(), 
-                dlgHeight - 10 - getLargest(loginButton.GetSize().GetHeight(),
-                                          cancelButton.GetSize().GetHeight())))
-
-    loginButton.SetPosition(
-        wxPoint(dlgWidth - 16 - cancelButton.GetSize().GetWidth() - \
-                loginButton.GetSize().GetWidth(),
-                dlgHeight - 10 - getLargest(loginButton.GetSize().GetHeight(),
-                                          cancelButton.GetSize().GetHeight())))
-
-
-    self.dlg.SetSize(wxSize(dlgWidth, dlgHeight))
-
-    self.dlg.Refresh()
-
-    # If user cancels, this will be set to 0
-    self._completed = 0
-
-    self.dlg.Fit()
-    self.dlg.Raise()
-    self.dlg.CenterOnScreen()
-    self.dlg.ShowModal()       
-    self.dlg.Destroy()  
-
-    rv = {}
-    for i in range(0, len(self.loginData[2])): 
-      rv[self.loginData[2][i][0]] = self.textctrlList[i].GetValue()
-
-    self.loginData.append([self._completed, rv])
-
-#    event.data = [self._completed, rv]
-  
-  
-  #
-  # Login is completed, for whatever reason
-  #
-  def loginCompleted(self, successful): 
-    self._completed = successful
-    self.dlg.EndModal(1)
-        
-  #
-  # Called when user clicks "login"
-  #
-  def loginButtonEventTrap(self, event): 
-    self.loginCompleted(1)
-
-  #
-  # Called when user clicks "login"
-  #
-  def loginCancelEventTrap(self, event): 
-    self.loginCompleted(0)
-
-
-
 #############################################################################
 #
 # UI Objects
@@ -954,7 +838,7 @@
       else: 
         styles = 0
 
-      newWidget = wxTextCtrl(container, -1, object._value,
+      newWidget = wxTextCtrl(container, -1, object.getValue(),
                              wxPoint(int(object.x)*int(widgetWidth),
                                      
(int(object.y)+spacer+(object.gap*spacer))*int(widgetHeight)),
                              wxSize(int(object.width)*int(textWidth),
@@ -977,7 +861,6 @@
     global _NOTEBOOK
 
     if object.tabbed:
-      print object.tabbed
       if object.tabbed == 'left':
         tabstyle = wxNB_LEFT
       elif object.tabbed == 'right':
@@ -1030,32 +913,159 @@
 
 
 
-#
-# LoginHandler
-#
-# The Login Handler for this user interface 
-# Will be used with new db provider interface
+#####################################################################
+#####################################################################
+##
+## Login Support
+##
+#####################################################################
+#####################################################################
+
 #
+# UILoginHandler
+class UILoginHandler(GLoginHandler.LoginHandler):
+  def __init__(self):
+    self._wxapp = getWxApp()
 
-class wxLoginHandler(GLoginHandler.LoginHandler): 
-  # Obviously, this needs to be replaced with a dialog implementation :)
   def getLogin(self, loginData): 
+
+    if len(loginData[1]): 
+#      loginMesg = 'Login required for "%s" (%s)' % (loginData[1], 
loginData[0])
+      loginMesg = 'Login required for "%s"' % (loginData[1])
+    else: 
+      loginMesg = 'Login required for %s' % (loginData[0])
+
+    self.dlg = wxDialog(NULL, -1, "GNU Enterprise: Login")
+#    self.dlg.SetBackgroundColour(wxWHITE)
+    self.dlg.SetAutoLayout(true)
+    
+    bmp = wxImage(GConfig.get('smallPNG'), wxBITMAP_TYPE_PNG).ConvertToBitmap()
+
+
+    self.textctrlList = []
+    messageField = wxStaticText(self.dlg, 1010, str(loginMesg))
+    labelList = []
+
+    dlgWidth = getLargest(bmp.GetWidth(), messageField.GetSize().GetWidth()+20)
 
-    try:
-      print "*"*60
-      print 'Attempting to log into "%s":' % (loginData[1] or loginData[0])
-      print 
-      val = {} 
-      for prompt in loginData[2]: 
-         val[prompt[0]] = raw_input("  %s: " % prompt[1])
-      print 
-      print "*"*60
-      return val
-    except KeyboardInterrupt: 
-      raise GLoginHandler.UserCanceledLogin
+    dlgHeight = bmp.GetHeight() + messageField.GetSize().GetHeight() + 80
 
+    xSpacing = 0
+    ySpacing = 0
+    fieldLabelWidth = 0
+
+    for prompt in loginData[2]: 
+      s = wxStaticText(self.dlg, 1010, '%s:' % prompt[1])
+      labelList.append(s)
+      if prompt[2]: 
+        t = wxTextCtrl(self.dlg, -1,"",wxPoint(1, 1), wxSize(150, 20), 
style=wxTE_PASSWORD|wxTE_PROCESS_ENTER)
+      else:
+        t = wxTextCtrl(self.dlg, -1,"",wxPoint(1, 1), wxSize(150, 20), 
style=wxTE_PROCESS_ENTER)
+      myID = len(self.textctrlList)
+      self.textctrlList.append(t)
+      EVT_CHAR(t, LoginFieldHandler(self, myID).loginFieldEventTrap)
+
+      fieldLabelWidth = getLargest(fieldLabelWidth, \
+           s.GetSize().GetWidth() + t.GetSize().GetWidth() + 10)   
+
+      dlgWidth = getLargest(dlgWidth, \
+           s.GetSize().GetWidth() + t.GetSize().GetWidth() + 10) 
+
+      xSpacing = getLargest(xSpacing, s.GetSize().GetWidth())
+      ySpacing = getLargest(ySpacing, s.GetSize().GetHeight())
+      ySpacing = getLargest(ySpacing, t.GetSize().GetHeight())
+
+
+    loginButton = wxButton(self.dlg,19998,'Login')
+    cancelButton = wxButton(self.dlg,19999,'Cancel')
+
+    EVT_BUTTON(self.dlg, 19998, self.loginButtonEventTrap)                
+    EVT_BUTTON(self.dlg, 19999, self.loginCancelEventTrap)                
+    dlgWidth = getLargest(dlgWidth, loginButton.GetSize().GetWidth() + 
+                          cancelButton.GetSize().GetWidth() + 6) + 20
+
+    dlgHeight = dlgHeight + \
+               getLargest(loginButton.GetSize().GetHeight(), 
+                          cancelButton.GetSize().GetHeight()) - 6
+
+    firstY = bmp.GetHeight() + messageField.GetSize().GetHeight() + 50
+    lastY = firstY
+    xSpacing = xSpacing + 10   # Add whitespace between widgets
+    ySpacing = ySpacing + 6    # Add whitespace between widgets
+    xPos = dlgWidth/2 - fieldLabelWidth/2
 
 
+    # Move the fields and labels into position
+    for i in range(0, len(self.textctrlList)):
+      dlgHeight = dlgHeight + ySpacing
+      labelList[i].SetPosition(wxPoint(xPos, lastY))
+      self.textctrlList[i].SetPosition(wxPoint(xPos + xSpacing, lastY))
+      lastY = lastY + ySpacing
+
+
+    # Create and position the logo
+    wxStaticBitmap(self.dlg,-1, bmp, 
+                   wxPoint((dlgWidth-bmp.GetWidth())/2, 12),
+                   wxSize(bmp.GetWidth(), bmp.GetHeight()))
+
+
+    # Move the various widgets into position
+    messageField.SetPosition(
+        wxPoint(dlgWidth/2 - messageField.GetSize().GetWidth()/2, 
+                30 + bmp.GetHeight()))
+
+
+    cancelButton.SetPosition(
+        wxPoint(dlgWidth - 10 - cancelButton.GetSize().GetWidth(), 
+                dlgHeight - 10 - getLargest(loginButton.GetSize().GetHeight(),
+                                          cancelButton.GetSize().GetHeight())))
+
+    loginButton.SetPosition(
+        wxPoint(dlgWidth - 16 - cancelButton.GetSize().GetWidth() - \
+                loginButton.GetSize().GetWidth(),
+                dlgHeight - 10 - getLargest(loginButton.GetSize().GetHeight(),
+                                          cancelButton.GetSize().GetHeight())))
+
+
+    self.dlg.SetSize(wxSize(dlgWidth, dlgHeight))
+
+    self.dlg.Refresh()
+
+    # If user cancels, this will be set to 0
+    self._completed = 0
+
+    self.dlg.Fit()
+    self.dlg.Raise()
+    self.dlg.CenterOnScreen()
+    self.dlg.ShowModal()
+    self.dlg.Destroy()
+
+    rv = {}
+    for i in range(0, len(loginData[2])): 
+      rv[loginData[2][i][0]] = self.textctrlList[i].GetValue()
+
+    return rv
+
+  #
+  # Login is completed, for whatever reason
+  #
+  def loginCompleted(self, successful): 
+    self._completed = successful
+    self.dlg.EndModal(1)
+        
+  #
+  # Called when user clicks "login"
+  #
+  def loginButtonEventTrap(self, event): 
+    self.loginCompleted(1)
+
+  #
+  # Called when user clicks "login"
+  #
+  def loginCancelEventTrap(self, event): 
+    self.loginCompleted(0)
+
+
 WIDGETS = {'GFLabel'     : UILabel,
            'GFBox'       : UIBox,
            'GFPage'      : UIPage,
@@ -1063,4 +1073,8 @@
            'GFEntry'     : UIEntry,
            'GFButton'    : UIButton,
            'GFScrollBar' : UIScrollBar}
+
+
+
+
 
Index: gnue/reports/src/GRDataMapper.py
diff -u gnue/reports/src/GRDataMapper.py:1.3 
gnue/reports/src/GRDataMapper.py:1.4
--- gnue/reports/src/GRDataMapper.py:1.3        Wed Jun 20 15:33:38 2001
+++ gnue/reports/src/GRDataMapper.py    Sun Aug 12 12:55:27 2001
@@ -216,26 +216,23 @@
       GDebug.printMesg (4, 'Creating ResultSet for source %s' % source)
       controlSection.resultset = self.sources.getDataSource(source)\
              .getDataObject().createResultSet(readOnly=1)
-#      print "controlSection=%s" % controlSection
     else: 
-      GDebug.printMesg(4, 'Getting pre-created ResultSet for source "%s"' \
-             % (source))
+      GDebug.printMesg(4, 'Getting pre-created ResultSet for source "%s"; 
parent=%s' \
+             % (source, controlSection.parent))
 
     return self.getNextRecord(source)
 
 
   # Returns a string containing first section to change. 
-  # If none, then this datasource is all used up.
+  # If None, then this datasource is all used up.
   def getNextRecord(self, source): 
 
+    GDebug.printMesg (6, 'Getting next record for source %s' % source)
     if source == None: 
+      GDebug.printMesg (6, 'No next record to return for source %s' % source)
       return None
 
-    GDebug.printMesg (6, 'Getting next record for source %s' % source)
     controlSection = self.sourceMap[source][0]
-#    print "controlSection=%s" % controlSection
-
-    GDebug.printMesg (10, controlSection.resultset)
 
     if not controlSection.resultset.nextRecord(): 
       return None
Index: gnue/reports/src/GRLayout.py
diff -u gnue/reports/src/GRLayout.py:1.6 gnue/reports/src/GRLayout.py:1.7
--- gnue/reports/src/GRLayout.py:1.6    Wed Jun 20 15:33:38 2001
+++ gnue/reports/src/GRLayout.py        Sun Aug 12 12:55:27 2001
@@ -162,7 +162,7 @@
     # 
     # Pull value for parameters
     #
-    if object.getObjectType == 'GRParam': 
+    if object.getObjectType() == 'GRParam': 
       object._value = self._parameters.getParameter(object.name) 
 
   #
@@ -176,9 +176,9 @@
       structuralComment = _structuralComment
 
     for child in self._children:
-      structuralComment(dest, '<!-- <layout> -->')
-      child.process(dest, self._mapper)
-      structuralComment(dest, '<!-- </layout> -->\n')
+      structuralComment(dest, '<!--[layout]-->')
+      child.processAsController(dest, self._mapper)
+      structuralComment(dest, '<!--[/layout]-->\n')
       
 
 
@@ -205,46 +205,65 @@
 # <section> tag
 #
 class GRSection (GRLayoutElement): 
+
   def __init__(self, parent):
     GRLayoutElement.__init__(self, parent, 'GRSection')
 
-  def process(self, dest, mapper): 
 
-    myTurnAtBat = 1
+  # Retrieve the first row of data, if available. 
+  # Handle default data if no rows retrieved.
+  #
+  # TODO: What if the top section isn't bound to a source?
+  # TODO: Does this work?
+  #
+  def processAsController(self, dest, mapper): 
 
-    while myTurnAtBat: 
-      structuralComment(dest,"<!--[section:%s]-->" % self.name)
-      GDebug.printMesg(10,"Repeating Section %s" % self.name)
-      for child in self._children:
-        if child.getObjectType() == "_content_": 
-          dest.write(child.getContent())
-        else: 
-          if isinstance(child, GRSection) and child._mymapper.toplevel: 
-            GDebug.printMesg(10, "Getting first record for child; 
(child,toplevel)=(%s,%s)" % (child.name, child._mymapper.toplevel))
-            if mapper.getFirstRecord(child.source): 
-              # There was data returned, so process section
-              child.process(dest, mapper)
-            else: 
-              # There was no data returned, so generate default text (if any)
-              for c2 in child._children: 
-                if isinstance (c2, GRDefault): 
-                  c2.processDefault(dest, mapper)
-          else:
-            child.process(dest, mapper)
-      if self._mymapper == None or not self._mymapper.toplevel: 
-        myTurnAtBat = 0
-      else:
+    GDebug.printMesg(10, "Controlling section %s's processAsController() 
called" % self.name)
+
+    if mapper.getFirstRecord(self.source): 
+      # There was data returned, so process section
+
+      section = self 
+      while section: 
+
+        # Process the current data row starting at correct section
+        section.process(dest, mapper)
+
+        # Determine the next section to process our data.
+        # (i.e., handle section grouping)
         goto = mapper.getNextRecord(self.source)
         GDebug.printMesg (10, "Goto is '%s'" % goto)
-        if goto == None: 
-          myTurnAtBat = 0
+
+        if goto == None:
+          section = None
         else: 
-          if goto != self.name: 
-            GDebug.printMesg (10, "Calling process() for section '%s' (mapper 
is %s)" % (goto, mapper.sectionMap[goto]))
-            mapper.sectionMap[goto]._object.process(dest, mapper)
-      structuralComment(dest,"<!--[/section:%s]-->" % self.name)
+          section = mapper.sectionMap[goto]._object
+
+    else: 
+      # There was no data returned, so generate default text (if any)
+      for c2 in self._children: 
+        if isinstance (c2, GRDefault): 
+          c2.processDefault(dest, self._mapper)
+
+
+  # Generic process() method.  Process the 
+  # current record and handle any children.
+  def process(self, dest, mapper): 
+
+    GDebug.printMesg(10,"Repeating Section %s" % self.name)
+    structuralComment(dest,"<!--[section:%s]-->" % self.name)
+
+    for child in self._children:
+      if child.getObjectType() == "_content_": 
+        dest.write(child.getContent())
+      else: 
+        if isinstance(child, GRSection) and child._mymapper.toplevel: 
+          GDebug.printMesg(10, "Calling new controlling section")
+          child.processAsController(dest, mapper)
+        else:
+          child.process(dest, mapper)
+
     GDebug.printMesg(10,"Left section %s" % self.name)
-            
 
 
   #
Index: gnue/reports/src/GRParser.py
diff -u gnue/reports/src/GRParser.py:1.10 gnue/reports/src/GRParser.py:1.11
--- gnue/reports/src/GRParser.py:1.10   Mon Jul  2 10:03:18 2001
+++ gnue/reports/src/GRParser.py        Sun Aug 12 12:55:27 2001
@@ -35,7 +35,7 @@
 from GRParameters import * 
 from GRSortOptions import *
 from GRLayout import *
-from gnue.common import GDataSource
+from gnue.common import GDataSource, GTypecast
 import copy, types
 
 
@@ -83,7 +83,7 @@
          'SingleInstance': 1, 
          'Attributes':  { 
             'title':       {
-               'Typecast': char } } ,
+               'Typecast': GTypecast.name } } ,
          'ParentTags':  None },
 
       'parameters':   {
@@ -97,22 +97,22 @@
             'id':          {
                'Required': 1, 
                'Unique': 1, 
-               'Typecast': char },
+               'Typecast': GTypecast.name },
             'required':    {
-               'Typecast': bool, 
+               'Typecast': GTypecast.boolean, 
                'Default': 0 },
             'limited':     {
-               'Typecast': bool, 
+               'Typecast': GTypecast.boolean, 
                'Default': 0 },
             'default':     {
-               'Typecast': char },
+               'Typecast': GTypecast.name },
             'description': {
                'Required': 1, 
-               'Typecast': char },
+               'Typecast': GTypecast.name },
             'source':      {
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'type':        {
-               'Typecast': char, 
+               'Typecast': GTypecast.name, 
                'Default': "char" } }, 
          'ParentTags':  ('parameters',) }, 
 
@@ -127,12 +127,12 @@
             'id':          {
                'Required': 1, 
                'Unique': 1, 
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'description': {
                'Required': 1, 
-               'Typecast': char },
+               'Typecast': GTypecast.name },
             'default':     {
-               'Typecast': bool, 
+               'Typecast': GTypecast.boolean, 
                'Default': 0 } }, 
          'ParentTags':  ('sortoptions',) },
 
@@ -141,7 +141,7 @@
          'Attributes': { 
             'name':        {
                'Required': 1, 
-               'Typecast': char } }, 
+               'Typecast': GTypecast.name } }, 
          'ParentTags':  ('sortoption',) }, 
 
       'layout':       {
@@ -150,7 +150,7 @@
          'SingleInstance': 1, 
          'Attributes': { 
             'format':      {
-               'Typecast': char } }, 
+               'Typecast': GTypecast.name } }, 
          'ParentTags':  ('form',) }, 
 
       'section':      {
@@ -160,9 +160,9 @@
          'KeepWhitespace': 1, 
          'Attributes': { 
             'name':        {
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'source':      {
-               'Typecast': char } }, 
+               'Typecast': GTypecast.name } }, 
          'ParentTags':  ('layout',) }, 
 
       'default':      {
@@ -176,11 +176,11 @@
          'Attributes': { 
             'name':        {
                'Required': 1, 
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'source':      {
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'format':      {
-               'Typecast': char } }, 
+               'Typecast': GTypecast.name } }, 
          'ParentTags':  ('layout',) }, 
 
       'summ':         {
@@ -188,17 +188,17 @@
          'Attributes': { 
             'name':        {
                'Required': 1, 
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'function':    {
-               'Typecast': char, 
+               'Typecast': GTypecast.name, 
                'Default': "count"}, 
             'source':      {
-               'Typecast': char, 
+               'Typecast': GTypecast.name, 
                'Default': None}, 
             'section':     {
-               'Typecast': char }, 
+               'Typecast': GTypecast.name }, 
             'format':      {
-               'Typecast': char } }, 
+               'Typecast': GTypecast.name } }, 
          'ParentTags':  ('layout',) }, 
 
       'sources':      {
Index: gnue/reports/src/GRQueryBuilder.py
diff -u gnue/reports/src/GRQueryBuilder.py:1.2 
gnue/reports/src/GRQueryBuilder.py:1.3
--- gnue/reports/src/GRQueryBuilder.py:1.2      Mon Apr 23 09:28:06 2001
+++ gnue/reports/src/GRQueryBuilder.py  Sun Aug 12 12:55:27 2001
@@ -28,6 +28,9 @@
 # Database-specific QueryBuilders should extend either QueryBuilder or 
 # AnsiSQLQueryBuilder (normally the latter if vendor uses SQL)
 #
+# *** THIS FILE IS NOT CURRENTLY USED BY THE REPORT ENGINE ***
+# *** IT IS BASICALLY A TEST PROJECT FOR LATER INCLUSION ***
+#
 
 from GRQuery import GRQueryElement
 import string 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]