AutoFill in Text Box

HMG Samples and Enhancements

Moderator: Rathinagiri

User avatar
esgici
Posts: 4543
Joined: Wed Jul 30, 2008 9:17 pm
DBs Used: DBF
Location: iskenderun / Turkiye
Contact:

AutoFill in Text Box

Post by esgici »

Hi all,

I'm experimenting AutoFill feature in text box.

Some friends told about it and some commercial products support it with some high promotions.

After seeing Rathinagiri's wonderful work Combined Search box, the AutoFill concept seems not very complicated, at least it will be easier than CSBox, I thought.

So, first implementation is attached.

It is very primitive stage and needs some fine tunes. fe the two sub functions has repetitive lines and may be shortened.

Also navigation by UP and DOWN keys may be optimized.

Please test and give your comments.

Regards

--

Esgici
Attachments
AutoFill.zip
AutoFill in Text Box experimenting
(2.18 KiB) Downloaded 672 times
Viva INTERNATIONAL HMG :D
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: AutoFill in Text Box

Post by Rathinagiri »

Just another nice utility Esgici.

I am very much interested to use it immediately in my applications.

Some suggestions:

1. When we use backspace, the selection doesn't change. For example, if we press i, Iceland comes. When we press backspace, it becomes 'celand'

2. As you have rightly said, up/down keys should be optimized. We can use Esc key also to cancel.

Eagerly waiting for the next version.

I had modified a little, to call only with the array parameter. ie., on change autofill(acountries)

Code: Select all

/*
  

  AutoFill in Text Box try


*/

#include "minigui.ch"

PROC Main()

   aCountries := HB_ATOKENS( MEMOREAD( "Countries.lst" ),   CRLF )
   
   ASORT( aCountries )                    // This Array MUST be sorted
       
   DEFINE WINDOW frmAFTest ;
      AT 0,0 ;
      WIDTH 550 ;
      HEIGHT 300 ;
      TITLE 'AutoFill in Text Box try' ;
      MAIN 
      
      ON KEY UP     ACTION AFNavigate( 1, "frmAFTest", "txbCountry", aCountries )
      ON KEY DOWN   ACTION AFNavigate( 2, "frmAFTest", "txbCountry", aCountries )
      
      ON KEY ESCAPE ACTION frmAFTest.Release
      
      DEFINE LABEL lblCountry
         ROW        50
         COL        50
         VALUE      "Country :"
         RIGHTALIGN .T.
         AUTOSIZE   .T.
      END LABEL // lblCountry
            
      DEFINE TEXTBOX txbCountry 
         ROW      48
         COL      110
         ONCHANGE AutoFill(aCountries )
      END TEXTBOX // txbCountry   
   
   END WINDOW // frmAFTest
   
   frmAFTest.Center
   
   frmAFTest.Activate

      
RETU // Main()

*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.


PROC AutoFill( ;                       // Setting CSBox values
                aList )      // Items list 
   
   LOCAL cCurval  := '' ,;
         n1Result := 0,;
         cFrmName := thiswindow.name,;
         cTxBName := this.name,;
         nCarePos := this.caretpos
   cCurval := LEFT( GetProperty( cFrmName, cTxBName, "Value" ), nCarePos )      
   IF !EMPTY( cCurval ) 
      
      n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, LEN( cCurval ) ) ) == UPPER( cCurval )} ) 
      
      IF n1Result > 0
         cCurval := aList[ n1Result ]
      ENDIF n1Result > 0
      
      SetProperty( cFrmName, cTxBName, "Value", cCurval  )        
      SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos  )        
   ENDIF !EMPTY( cCurval ) 
   
RETU // SetCSBoxVal()   
   
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.


PROC AFNavigate( ;                        // Navigation by UP/DOWN keys in AutoFill Text Box        
                 nDirect ,;  // 1 : Up, 2: Down
                 cFrmName,; 
                 cTxBName,; 
                 aList )      // Items list 

                 
   LOCAL nCarePos := GetProperty( cFrmName, cTxBName, "CaretPos"  ),;        
         n1Result := 0
                   
   LOCAL cCurval  := LEFT( GetProperty( cFrmName, cTxBName, "Value" ), nCarePos )
   
   IF !EMPTY( cCurval ) 
      n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, LEN( cCurval ) ) ) == UPPER( cCurval )} ) 
            
      IF n1Result > 0      
         IF nDirect < 2 
            n1Result -= IF( n1Result > 1, 1, 0 )
         ELSE
            n1Result += IF( n1Result < LEN( aList ), 1, 0 )
         ENDIF   
             
         cCurval := aList[ n1Result ]
         
      ENDIF n1Result > 0
      
      SetProperty( cFrmName, cTxBName, "Value", cCurval  )        
      SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos  )        
     
   ENDIF !EMPTY( cCurval ) 

                 
RETU // AFNavigate()
 
                 
*-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
                 
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: AutoFill in Text Box

Post by Rathinagiri »

Hi Esgici,

Kindly test this code.

I had altered for up/down key problems with other controls and also the backspace problem.

Code: Select all

    /*
     

      AutoFill in Text Box try


    */

    #include "minigui.ch"

    PROC Main()

       aCountries := HB_ATOKENS( MEMOREAD( "Countries.lst" ),   CRLF )
       
       ASORT( aCountries )                    // This Array MUST be sorted
           
       DEFINE WINDOW frmAFTest ;
          AT 0,0 ;
          WIDTH 550 ;
          HEIGHT 300 ;
          TITLE 'AutoFill in Text Box try' ;
          MAIN
         
         
          ON KEY ESCAPE ACTION frmAFTest.Release
         
          DEFINE LABEL lblCountry
             ROW        50
             COL        50
             VALUE      "Country :"
             RIGHTALIGN .T.
             AUTOSIZE   .T.
          END LABEL // lblCountry
               
          DEFINE TEXTBOX txbCountry
             ROW      48
             COL      110
             ongotfocus autofill_init(acountries)
             ONCHANGE AutoFill(aCountries )
             onlostfocus autofill_close()
          END TEXTBOX // txbCountry   
          
          define listbox list1
             row 80
             col 110
             width 200
             items {"Item 1","Item 2","Item 3","Item 4","Item 5","Item 6"}
             value 1
          end listbox
          
       
       END WINDOW // frmAFTest
       
       frmAFTest.Center
       
       frmAFTest.Activate

         
    RETU // Main()

    *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.


    PROC AutoFill( ;                       // Setting CSBox values
                    aList )      // Items list
       
       LOCAL cCurval  := '' ,;
             n1Result := 0,;
             cFrmName := thiswindow.name,;
             cTxBName := this.name,;
             nCarePos := this.caretpos
       cCurval := LEFT( GetProperty( cFrmName, cTxBName, "Value" ), nCarePos )     
       IF !EMPTY( cCurval )
         
          n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, LEN( cCurval ) ) ) == UPPER( cCurval )} )
         
          IF n1Result > 0
             cCurval := aList[ n1Result ]
          ENDIF n1Result > 0
         
          SetProperty( cFrmName, cTxBName, "Value", cCurval  )       
          SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos  )       
       ENDIF !EMPTY( cCurval )
       if ncarepos == 0 .and. len(alltrim(GetProperty( cFrmName, cTxBName, "Value" ))) > 0
          if ascan(alist,{ | c1 | UPPER( c1 ) == UPPER( GetProperty( cFrmName, cTxBName, "Value" ) )}) == 0
             SetProperty( cFrmName, cTxBName, "Value", ''  )       
          endif
       endif
    RETU // SetCSBoxVal()   
       
    *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.


    PROC AFNavigate( ;                        // Navigation by UP/DOWN keys in AutoFill Text Box       
                     nDirect ,;  // 1 : Up, 2: Down
                     cFrmName,;
                     cTxBName,;
                     aList )      // Items list

                     
       LOCAL nCarePos := GetProperty( cFrmName, cTxBName, "CaretPos"  ),;       
             n1Result := 0
                       
       LOCAL cCurval  := LEFT( GetProperty( cFrmName, cTxBName, "Value" ), nCarePos )
       IF !EMPTY( cCurval )
          n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, LEN( cCurval ) ) ) == UPPER( cCurval )} )
               
          IF n1Result > 0     
             IF nDirect < 2
                n1Result -= IF( n1Result > 1, 1, 0 )
             ELSE
                n1Result += IF( n1Result < LEN( aList ), 1, 0 )
             ENDIF   
                 
             cCurval := aList[ n1Result ]
             
          ENDIF n1Result > 0
         
          SetProperty( cFrmName, cTxBName, "Value", cCurval  )       
          SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos  )       
         
       ENDIF !EMPTY( cCurval )

                     
    RETU // AFNavigate()

                     
    *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
                     
function autofill_init(aitems)
local formname := thiswindow.name
local controlname := this.name
ON KEY UP    of &formname ACTION AFNavigate( 1, formname, controlname, aitems )
ON KEY DOWN  of &formname ACTION AFNavigate( 2, formname, controlname, aitems )

return nil

function autofill_close
local formname := thiswindow.name
release key UP of &formname
release key DOWN of &formname
return nil
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: AutoFill in Text Box

Post by sudip »

Hi Esgici & Rathi,

Thanks a lot. Excellent utility!!! :)

Only one suggestion:

When typing in the textbox if right side of the cursor position is automatically selected, then it will be better for user (I did the same thing in one of my VFP utilities, as suggested by one of my clients)

With best regards.

Sudip
With best regards,
Sudip
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: AutoFill in Text Box

Post by Rathinagiri »

Yes Sudip. It can be done by adding the following line after "SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos )"

Code: Select all

SendMessage ( GetControlHandle(cTxBname,cFrmName) , 177 , ncarepos , len(cCurval) )
Don't worry about calling of this internal low level function. :)

It has some disadvantage also. I think we can not use caret position and selection simultaneously. Tell me about your opinion
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: AutoFill in Text Box

Post by sudip »

Hi Rathi,

I inserted the line in 2 places.

It works for "selection". :)
rathinagiri wrote: Don't worry about calling of this internal low level function. :)

It has some disadvantage also. I think we can not use caret position and selection simultaneously. Tell me about your opinion
But there is a problem. I can't use BACK SPACE to delete text, left side of the caret. :( So, you are right. Yes, we can't use caret position and selection simultaneously. ;)

And regarding "worried" about low level function... ha, ha, ha :lol: (Thanks to Roberto, yesterday he patiently explained GetTextWidth() function to this "novice" learner :) )

With best regards.

Sudip
With best regards,
Sudip
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: AutoFill in Text Box

Post by Rathinagiri »

Hi Sudip,

I think I had solved this backspace problem too.

Have a try about this.

Code: Select all

        /*
         

          AutoFill in Text Box try


        */

        #include "minigui.ch"

        PROC Main()

           aCountries := HB_ATOKENS( MEMOREAD( "Countries.lst" ),   CRLF )
           
           ASORT( aCountries )                    // This Array MUST be sorted
               
           DEFINE WINDOW frmAFTest ;
              AT 0,0 ;
              WIDTH 550 ;
              HEIGHT 300 ;
              TITLE 'AutoFill in Text Box try' ;
              MAIN
             
             
              ON KEY ESCAPE ACTION frmAFTest.Release
             
              DEFINE LABEL lblCountry
                 ROW        50
                 COL        50
                 VALUE      "Country :"
                 RIGHTALIGN .T.
                 AUTOSIZE   .T.
              END LABEL // lblCountry
                   
              DEFINE TEXTBOX txbCountry
                 ROW      48
                 COL      110
                 ongotfocus autofill_init(acountries)
                 ONCHANGE AutoFill(aCountries )
                 onlostfocus autofill_close()
              END TEXTBOX // txbCountry   
             
              define listbox list1
                 row 80
                 col 110
                 width 200
                 items {"Item 1","Item 2","Item 3","Item 4","Item 5","Item 6"}
                 value 1
              end listbox
             
           
           END WINDOW // frmAFTest
           
           frmAFTest.Center
           
           frmAFTest.Activate

             
        RETU // Main()

        *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.


        PROC AutoFill( ;                       // Setting CSBox values
                        aList) //Items list
           
           LOCAL cCurval  := '' ,;
                 n1Result := 0,;
                 cFrmName := thiswindow.name,;
                 cTxBName := this.name,;
                 nCarePos := this.caretpos
           cCurval := LEFT( GetProperty( cFrmName, cTxBName, "Value" ), nCarePos )     
           IF !EMPTY( cCurval )
             
              n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, LEN( cCurval ) ) ) == UPPER( cCurval )} )
             
              IF n1Result > 0
                 cCurval := aList[ n1Result ]
              ENDIF n1Result > 0
             
              SetProperty( cFrmName, cTxBName, "Value", cCurval  )       
//              SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos  )       
              SendMessage ( GetControlHandle(cTxBname,cFrmName) , 177 , len(cCurval),ncarepos   )
           ENDIF !EMPTY( cCurval )
           if ncarepos == 0 .and. len(alltrim(GetProperty( cFrmName, cTxBName, "Value" ))) > 0
              if ascan(alist,{ | c1 | UPPER( c1 ) == UPPER( GetProperty( cFrmName, cTxBName, "Value" ) )}) == 0
                 SetProperty( cFrmName, cTxBName, "Value", ''  )       
              endif
           endif
        RETU // SetCSBoxVal()   
           
        *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.


        PROC AFNavigate( ;                        // Navigation by UP/DOWN keys in AutoFill Text Box       
                         nDirect ,;  // 1 : Up, 2: Down
                         cFrmName,;
                         cTxBName,;
                         aList )      // Items list

                         
           LOCAL nCarePos := GetProperty( cFrmName, cTxBName, "CaretPos"  ),;       
                 n1Result := 0
                           
           LOCAL cCurval  := LEFT( GetProperty( cFrmName, cTxBName, "Value" ), nCarePos )
           IF !EMPTY( cCurval )
              n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, LEN( cCurval ) ) ) == UPPER( cCurval )} )
                   
              IF n1Result > 0     
                 IF nDirect < 2
                    n1Result -= IF( n1Result > 1, 1, 0 )
                 ELSE
                    n1Result += IF( n1Result < LEN( aList ), 1, 0 )
                 ENDIF   
                     
                 cCurval := aList[ n1Result ]
                 
              ENDIF n1Result > 0
             
              SetProperty( cFrmName, cTxBName, "Value", cCurval  )       
              SetProperty( cFrmName, cTxBName, "CaretPos", nCarePos  )       

           ENDIF !EMPTY( cCurval )

                         
        RETU // AFNavigate()

                         
        *-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.-._.
                         
    function autofill_init(aitems)
    local formname := thiswindow.name
    local controlname := this.name
    ON KEY UP    of &formname ACTION AFNavigate( 1, formname, controlname, aitems )
    ON KEY DOWN  of &formname ACTION AFNavigate( 2, formname, controlname, aitems )
    ON KEY BACK of &formname ACTION autofilldobackspace(formname,controlname,aitems)

    return nil

    function autofill_close
    local formname := thiswindow.name
    release key UP of &formname
    release key DOWN of &formname
    return nil

    function autofilldobackspace(formname,controlname,alist)
    local nstart := LoWord ( SendMessage( GetControlHandle(controlname,formname) , 176 , 0 , 0 ) ) 
    local nend := HiWord ( SendMessage( GetControlHandle(controlname,formname) , 176 , 0 , 0 ) ) 
    local ccurvalue := getproperty(formname,controlname,"VALUE")
    local n1Result := 0
    if nstart > 0
              n1Result := ASCAN( aList, { | c1 | UPPER( LEFT( c1, nstart-1 ) ) == UPPER( substr(cCurvalue,1,nstart-1 ))} )
              IF n1Result > 0     
                 ccurvalue := aList[ n1Result ]
              ENDIF n1Result > 0
              setproperty(formname,controlname,"VALUE",ccurvalue)
              SendMessage ( GetControlHandle(controlname,formname) , 177 , len(ccurvalue),nstart-1   )
	endif       
    return nil
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: AutoFill in Text Box

Post by sudip »

Hi Rathi,

Thanks a lot! It works! You are a genius :D

Again, I saw an "abnormal" behavior. When pressing back space, it selects left character in stead of deleting it.

Is this behavior Ok or we need some changes?

With best regards.

Sudip
With best regards,
Sudip
User avatar
Rathinagiri
Posts: 5471
Joined: Tue Jul 29, 2008 6:30 pm
DBs Used: MariaDB, SQLite, SQLCipher and MySQL
Location: Sivakasi, India
Contact:

Re: AutoFill in Text Box

Post by Rathinagiri »

That you have to tell. :)

Now, just try Indo for Indonesia and then press backspace.
East or West HMG is the Best.
South or North HMG is worth.
...the possibilities are endless.
User avatar
sudip
Posts: 1454
Joined: Sat Mar 07, 2009 11:52 am
Location: Kolkata, WB, India

Re: AutoFill in Text Box

Post by sudip »

rathinagiri wrote:That you have to tell. :)

Now, just try Indo for Indonesia and then press backspace.
Yes, I tried it. It is not very easy to tell .... :? Most probably you are correct, this should be the ideal behavior of CSBox control. :D

Now, I want to explain what I mean with the help from my old app ... :)
test.JPG
test.JPG (51.56 KiB) Viewed 12289 times
This is HMG forum, but I want to share some of my old codes written in another software tool to mean what I want to say.

Code: Select all

*************************************
InteractiveChange EVENT FOR txtCustnm CONTROL

local mCustnm

ch = lastkey()

* Ignore if Del, Back Space or Space key pressed
if ch = 7 or ch = 127 or ch = 32
	return
endif

mCustnm = left(this.value, this.selstart)
if empty(mCustnm)
	return
endif

select cust
set order to custnm
locate for upper(custnm) = upper(mCustnm)
if found()
	this.value = Custnm
	this.selstart = len(mCustnm)
	this.sellength = len(custnm) - len(mCustnm)
endif


***************************************
Valid EVENT FOR txtCustnm CONTROL

local mCustnm

select ord
if eof() or bof()
	return
endif

select cust
set order to custnm
set exact on
seek upper(alltrim(this.value))
set exact off
if found()
	select ord
	replace custid with cust.custid
	return
else
	select ord
	replace custid with ""
endif

mCustnm = left(this.value, this.selstart)

select Custnm, upper(Custnm) ;
	from Cust ;
	where upper(Custnm) = upper(alltrim(this.value));
	order by 2 ;
	into cursor curTemp

do form frmFind with "curTemp", 1, 1 to mCustnm

if !empty(mCustnm)
	this.value = mCustnm
endif

use in curTemp

select cust
set order to custnm
set exact on
seek upper(alltrim(this.value))
set exact off
if found()
	select ord
	replace custid with cust.custid
else
	select ord
	replace custid with ""
endif
frmFind is a form with listbox for searching records.

Above code is not generic and I never used that code later. :(

Thank you again for sharing this code. I am very happy with this team work :)

With best regards.

Sudip
With best regards,
Sudip
Post Reply