//------------------------------------------------------------------------
//
//  Seeker.cc  --  Incremental search custom control
//
//  
//  dBASE Samples Group, and Ken Chan
//
//  $Revision:   1.3  $
//
//  Copyright (c) 1997-1999, Borland International, Inc. and
//  1999, dBASE, Inc.
//
//  All rights reserved.
//
// Usage Notes:
//
// By default, this control should be in the Component Palette
// when you create a form, on the "Custom" tab. If it is not,
// then before creating a form or modifying a form, issue
// the command (in the Command Window):
//    set procedure to ":FormControls:\SEEKER.CC" additive
// 
// In the form designer, make sure that the rowset you wish
// to use this control with has the indexName property set to
// an index tag that is character, and created with the UPPER()
// function, so that it is indexed on the upper-case of any
// characters in the field(s) index on.
//
// Drag the SEEKER control to the design surface. When the form
// is run, entering text in the entryfield will cause an
// incremental search to occur, using the index tag mentioned
// above.
//
// NOTE: do not attempt to datalink this custom entryfield, 
// first, it is "protected" so that you cannot access this
// property in the designer, and second, the code will not function
// properly if datalinked to a field in the table.
//
//------------------------------------------------------------------------
//

CLASS Seeker( oCont ) of Entryfield( oCont ) CUSTOM

  protect lNear, lExact, lFound, lRefresh, nMaxPos
  protect seekRowset
  // This control uses the active index to seek; DO NOT datalink!
  protect dataLink

  // Set stock properties
  this.selectAll := true
  this.value     := ""
  
  // Custom properties
  this.rowset    = null    // Set to override form.rowset
  this.tabAfter  = 0       // Automatically tab after typing N chars
  this.seek      = this.xSeek

  // Internal properties
  this.lNear     = ( SET( "NEAR"  ) == "ON" )
  this.lExact    = ( SET( "EXACT" ) == "ON" )
  this.lRefresh  = this.form.RefreshAlways
  this.lFound    = true
  this.nMaxPos   = 0       // Max position of cursor in entryfield

  FUNCTION onGotFocus
    if form.rowset == null       // Xbase settings
      this.lNear    = ( SET( "NEAR"  ) == "ON" )
      this.lExact   = ( SET( "EXACT" ) == "ON" )
      this.lRefresh = form.RefreshAlways
      set near on                // To do incremental matching
      set exact off              // To return FOUND() true on substring match
      form.RefreshAlways = true
      this.seek     = this.xSeek // Use method with Xbase SEEK()
    else
      // For OODML, use findKeyNearest(), which always does a
      // partial key match (like EXACT OFF)
      this.seek     = this.oSeek
      // Assign protected object reference to either form's rowset
      // or control's rowset
      this.seekRowset = iif( this.rowset == null, form.rowset, this.rowset )
    endif
    this.lFound  = true
    this.nMaxPos = 0

  FUNCTION onKey( nChar, nPosition, bShift, bControl )
    if nextkey() == 0
      // Wait until there are no keystrokes pending
      if not empty( this.value ) and ;
          ( this.lFound or nPosition <= this.nMaxPos )
        this.lFound = this.seek()
      endif
      if this.tabAfter > 0 and nPosition > this.tabAfter
        // Stuff tab if position is greater than tabAfter
        this.keyboard( chr(9) )
      endif
      this.nMaxPos = max( nPosition, ;
          min( this.nMaxPos, len( trim( this.value ))) )
    endif

  FUNCTION xSeek
    local lRet
    lRet = seek( this.normalizedValue() )
    if eof()               // Always move record pointer to real record
      goto bottom
    endif
  RETURN lRet

  FUNCTION oSeek
    local lRet
    lRet = this.seekRowset.findKeyNearest( this.normalizedValue() )
    if ( this.seekRowset.endOfSet )
      this.seekRowset.next( -1 )
    endif
  RETURN lRet

  FUNCTION normalizedValue
  RETURN this.value.rightTrim().toUpperCase()

  FUNCTION onLostFocus
    if form.rowset == null
      if not this.lNear
        set near off
      endif
      if this.lExact
        set exact on
      endif
      form.RefreshAlways := this.lRefresh
    endif

ENDCLASS
