 *----------------------------------------------------------------------
 *-- SEQVALUE.CC.: A SEQUENTIAL value Custom Class Entryfield.
 *-- Programmer..: Romain Strieff (CIS: 71333,2147)
 *-- Date........: 02/05/1996 Original
 *-- ............: 03/08/1997 removed FIELDS bug
 *-- Notes.......: Custom entryfield that will fill automatically
 *-- ............: with sequential values when issuing Form.Saverecord()
 *-- ............: in the form it is placed. Used for InVoice/Order N#
 *-- ............: etc where the sequence of the numbers must be
 *-- ............: guaranteed.
 *-- Written for.: Visual dBASE 5.5a for Windows
 *-- Calls.......: None
 *-- Based on....: AUTOINC.CC from Ken Mayer (CIS: 71333,1030)
 *-- Remarks.....: Your form must use Form.BeginAppend() and
 *-- ............: Form.SaveRecord() for the CC to work.
 *-- Comment.....: This CC overrides the Forms SAVERECORD() method.
 *----------------------------------------------------------------------

 CLASS SeqValue(F,N) Of Entryfield(F,N) Custom
       Protect Onopen,When,Speedtip,enabled
       This.Speedtip  = "SeqValue Custom Control -- You Can't Edit Me!"
       This.When      = {;Return .F.}
       This.Onopen    = Class::SeqValueOnopen
       this.enabled   =.f.

       Procedure Incrementit

       *this control MUST have a datalink
       If Empty(This.Datalink)
          ?? Chr(7)
          Msgbox("There is no datalink for this control!","ERROR",16)
          Return .F.
       Endif && Empty(This.Datalink)

       *check if it already has a value
       If Type("this.value")="N" .And. This.Value > 0
          Return .F.  && Don't do anything!
       Endif && Type("this.value")="N" .And. This.Value
       If Type("this.value")="C" .And. .Not. Empty(This.Value)
          Return .F.  && Don't do anything!
       Endif && Type("this.value")="C" .And. .Not. Empty


       *-- if table doesn't exist, create it:
       *-- check if you don't have any _old_ dbf with the same name!!!
       cAlias = Alias()
       If .Not. File("SeqValue.DBF")
          Select Select()  && Next Work Area
          *structure has been changed so don't use any old version dbf!
          Create Table "SeqValue.DBF" (Incfield Char (50),Incvalue;
            Numeric(15,0))
          Use SeqValue Excl &&Use Exclusive To Create Index Tag
          sFields=Set("FIELDS")
          set fields off

          *create index tag, so that we can use one record for each
          *field that needs an incrementing value
          Index On Upper(Incfield) Tag Incfield
          *close newly created dbf and return to the saved workarea
          Use
          set fields &sFields.
          Select (Calias)
       Endif && .Not. File("SeqValue.DBF")

       *-- open SeqValue.dbf, so we can increment it, then
       *-- close it as soon as possible:
       *create exact search expression so that we don't have to
       *bother with SET EXACT
       cSeek=Left(Upper(This.Datalink)+Space(50),50)

       Select Select()  && Next Work Area
       Use SeqValue Order Incfield    && Open The Table
       *wait loop for multiple users the same time
       Do While .Not. Flock() && Try To Flock It, If Impossible
       Enddo                  &&Another User Was Faster
       *We lock the _file_ instead of the record (RLOCK()) to avoid
       *problems when 2 users would try to add a new record to SEQVALUE
       *the same time for the same table field when used the first time.
       sFields=Set("FIELDS")
       ? sFields
       set fields off

       *search record with this datalink
       If Seek(cSeek)  &&Ok Record Exist
          *increment value
          Replace SeqValue->Incvalue With SeqValue->Incvalue+1  && Increment
       Else
          *it does not exist, so add a new record
          Append Blank
          *save datalink to this record and reset the counter
          *Here's where the problem could happen if we used record lock
          *instead on file lock, 2 users might add a record the the same
          *table-field
          Replace SeqValue->Incvalue With 1,Incfield With This.Datalink
       Endif && Seek(Cseek)  &&Ok Record Exists

       Do Case
       Case Type("this.value")="N" &&It's a numeric field
          *assign numeric value unchanged
          This.Value = SeqValue->Incvalue
       Case Type("this.value")="C" &&It's a char field
          *construct the right picture clause for this field
          *@L means leading zeroes
          Cpicture ="@L "+Replicate("9",Len(This.Value))
          *transform to chr type and lenght
          This.Value= Transform(SeqValue->Incvalue,Cpicture)
       Otherwise
          *the user set the datalink to an illegal type field
          ?? Chr(7)
          Msgbox("SeqValue.CC only handles N and C datatypes!")
       Endcase
       set fields &sFields.
       Use
       Select (cAlias)
       *so we just updated the value, now save it with the rest of
       *the changes that were done.
       form.SaveRecord()

       Return .F.

       Procedure SeqValueonopen
       *set the focus to every SeqValuerement control automatically each
       *time a navigating occurs in the form

       If Type("form.SeqValue_already_installed")="U"
          *do this only once for the first SeqValuerement
          *control on the form.
          Form.SeqValue_already_installed=.T.

          *If the form does not have an overriden Saverecord

          If(Empty(Form.SaveRecord))

             Form.SaveRecord=Class::Checkauto
          Else
             *otherwise execute both codes
             Form.AutoSaveRecord=Form.SaveRecord
             *save reference to new property and
             Form.SaveRecord={;class::checkauto();form.AutoSaveRecord()}

          Endif && (Empty(Form.Onnavigate))
       Endif && Type("form.SeqValue_already_installed")="

       Procedure Checkauto
       *set focus to each SeqValue object on the form to check
       *if it has a value

       *save current control to a variable
       Oscontrol=Form.Activecontrol
       *loop through all controls and set focus to all the
       *SeqValue controls
       Ocontrol=Form.First
       Do

          If "SEQVALUE" = upper(Ocontrol.ClassName)
              if oControl.pageno=form.pageno
                 Ocontrol.incrementit()
              endif
          Endif && "SeqValue" $ Ocontrol.Name
          Ocontrol=Ocontrol.Before
       Until  Ocontrol.Name==Form.First.Name
       *set focus to the control that had it before this loop
       oScontrol.Setfocus()

 ENDCLASS
