A better Drop Picker

This forum is for eXpress++ general support.
Message
Author
User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

A better Drop Picker

#1 Post by rdonnay »

Here is a sample program that drops down a browse below a get when a value is typed in the get and there are 1 or more matches in the browse.

Functionality is similar to Windows drop downs when selecting a file from the file dialog.

Code: Select all

#INCLUDE "dcdialog.CH"
#INCLUDE "appevent.CH"

FUNCTION Main()

LOCAL GetList[0], GetOptions, cName, aNames, cPerformer, oBrowse

cPerformer := Space(30)

aNames := PerformerNames()

@ 0,0 DCSAY 'Enter Performer Name' GET cPerformer ;
      SAYSIZE 0 SAYBOTTOM ;
      KEYBLOCK {|a,b,o|DropDownNames(a,o,@cPerformer,aNames,oBrowse), ;
                       DC_BrowseAutoSeek(a,o,oBrowse,aNames)}

@ 1,16 DCBROWSE oBrowse DATA aNames SIZE 35,10 ;
       FONT '10.Lucida Console' ;
       FILTER {|a|a[2]} ;
       LOSTFOCUS {|a,b,o|o:hide()}

DCBROWSECOL ELEMENT 1 WIDTH 30 PARENT oBrowse

DCREAD GUI FIT TITLE 'Scoped Drop-Down' EVAL {||oBrowse:hide()}

RETURN nil

* -----------

PROC appsys ; RETURN

* -----------

STATIC FUNCTION DropDownNames( nKey, oGet, cPerformer, aNames, oBrowse )

LOCAL i, nFound, cName, nLen, aPerformers, nCount := 0

IF nKey == xbeK_ENTER
  cPerformer := Pad(aNames[oBrowse:arrayElement,1],30)
  oGet:setData(cPerFormer)
ELSEIF (Chr(nKey) < 'A' .OR. Chr(nKey) > 'z') .AND. !nKey $ {xbeK_SPACE,xbeK_BS}
  RETURN nil
ENDIF

oGet:getData()

cName := Alltrim(Upper(cPerformer))
nLen := Len(cName)

nFound := AScan( aNames, {|a|Left(Upper(a[1]),nLen) == cName} )

FOR i := 1 TO Len(aNames)
  aNames[i,2] := .f.
NEXT

IF nFound > 0 .AND. !Empty(cName)
  FOR i := 1 TO Len(aNames)
    IF Left(Upper(aNames[i,1]),nLen) == cName
      aNames[i,2] := .t.
    ENDIF
  NEXT
  oBrowse:goTop()
  oBrowse:forceStable()
  oBrowse:refreshAll()
  oBrowse:show()
  SetAppFocus(oGet)
  DC_ClearEvents()
ELSE
  oBrowse:hide()
  oBrowse:goTop()
  oBrowse:forceStable()
  oBrowse:refreshAll()
ENDIF

RETURN nil

* ------------

STATIC FUNCTION PerformerNames()

LOCAL aNames1[0], aNames2, i

aNames2 := { ;
   'Annie Hall', ;
   'Arlo Guthrie', ;
   'Bette Midler', ;
   'Bob Dylan', ;
   'Bobbie Gentry', ;
   'Buddy Holly', ;
   'Canned Heat', ;
   'Carol Elliot', ;
   'Carole King', ;
   'Caroline Aikin', ;
   'Cat Stevens', ;
   'Catie Curtis', ;
   'Cheryl Wheeler', ;
   'Chuck Prophet', ;
   'Chuck Pyle', ;
   'Crosby, Stills, Nash', ;
   'Danny Kaye', ;
   'Dar Williams', ;
   'Dr Hook', ;
   'Eliza Gilkyson', ;
   'Elton John', ;
   'Elvis Presley', ;
   'Gail Davies', ;
   'Glenn Miller', ;
   'Gordon Rowland', ;
   'Greg Brown', ;
   'Harry McClintock', ;
   'Itzhak Perlman', ;
   'James Taylor', ;
   'Jan Stanfield', ;
   'Jefferson Airplane', ;
   'Jim Croce', ;
   'Jimi Hendrix', ;
   'Jimmy Dean', ;
   'John Denver', ;
   'Joni Mitchell', ;
   'Journey', ;
   'Kevin Welch', ;
   'Krystian Zimermann', ;
   'Led Zeppelin', ;
   'Lyle Lovett', ;
   'Lynn Miles', ;
   'Madeleine Peyroux', ;
   'Michael Jackson', ;
   'Michael Lille', ;
   'Mumbo Gumbo', ;
   'Norah Jones', ;
   'Ray Charles', ;
   'Richard Pryor', ;
   'Richard Shindell', ;
   'Richie Havens', ;
   'Robin Williams', ;
   'Roy Orbison', ;
   'Sam Cooke', ;
   'Slobberbone', ;
   'Steppenwolf', ;
   'Susan Werner', ;
   'Ten Years After', ;
   'The Beatles', ;
   'The Doors', ;
   'The Eagles', ;
   'The Flatlanders', ;
   'The Moody Blues', ;
   'Tim Bays', ;
   'Toby Keith', ;
   'Tom Paxton', ;
   'Tom Petty', ;
   'Tom Prasada-Rao', ;
   'Van Morrison', ;
   'Vance Gilbert', ;
   'Vic Chestnutt', ;
   'Willie Nelson', ;
   'Yo-yo Ma', ;
   'Yundi Li'}

FOR i := 1 TO Len(aNames2)
  AAdd(aNames1,{ aNames2[i], .t. } )
NEXT

RETURN aNames1
The eXpress train is coming - and it has more cars.

Leon Berger
Posts: 36
Joined: Thu Jan 28, 2010 2:30 pm

Re: A better Drop Picker

#2 Post by Leon Berger »

Nice program and very helpful, thanks Roger!

Only a litte bug is in the function PerformerNames():

I miss Freddy Mercury 8-)
Best regards
Leon

User avatar
hz_scotty
Posts: 107
Joined: Thu Jan 28, 2010 8:20 am
Location: Wr.Neustadt / Österreich

Re: A better Drop Picker

#3 Post by hz_scotty »

An error occurs if the input was not found. (oBrowse:arrayElement=0)

for example: press "z" and Enter (there is no name with "z")

Code: Select all

STATIC FUNCTION DropDownNames( nKey, oGet, cPerformer, aNames, oBrowse )

LOCAL i, nFound, cName, nLen, aPerformers, nCount := 0

IF nKey == xbeK_ENTER
  cPerformer := Pad(aNames[oBrowse:arrayElement,1],30)              // <== Error - if input not found !
  oGet:setData(cPerFormer)
ELSEIF (Chr(nKey) < 'A' .OR. Chr(nKey) > 'z') .AND. !nKey $ {xbeK_SPACE,xbeK_BS}
  RETURN nil
ENDIF
add these lines to fix it ....
STATIC FUNCTION DropDownNames( nKey, oGet, cPerformer, aNames, oBrowse )

LOCAL i, nFound, cName, nLen, aPerformers, nCount := 0

IF nKey == xbeK_ENTER
if oBrowse:arrayElement=0
* not found
DC_MSGBOX(nil,nil,{"No Match"},"Error",,1,,,,,,,,,13)
else

cPerformer := Pad(aNames[oBrowse:arrayElement,1],30)
oGet:setData(cPerFormer)
endif
ELSEIF (Chr(nKey) < 'A' .OR. Chr(nKey) > 'z') .AND. !nKey $ {xbeK_SPACE,xbeK_BS}
RETURN nil
ENDIF
best regards
Hans

skiman
Posts: 1185
Joined: Thu Jan 28, 2010 1:22 am
Location: Sijsele, Belgium
Contact:

Re: A better Drop Picker

#4 Post by skiman »

Hi,

I didn't test it, but maybe the following is a nice addtion to the functionality:

Code: Select all

...
nFound := AScan( aNames, {|a| cName $ Upper(a[1]) } )

// FOR i := 1 TO Len(aNames)
//   aNames[i,2] := .f.
// NEXT

IF nFound > 0 .AND. !Empty(cName)
  FOR i := 1 TO Len(aNames)
    aNames[i,2] := IIF(cName $ Upper(aNames[i,1]),.T.,.F.)
  NEXT
  oBrowse:goTop()
  oBrowse:forceStable()
  oBrowse:refreshAll()
  oBrowse:show()
  SetAppFocus(oGet)
  DC_ClearEvents()
ELSE
  oBrowse:hide()
  oBrowse:goTop()
  oBrowse:forceStable()
  oBrowse:refreshAll()
ENDIF
Best regards,

Chris.
www.aboservice.be

D. Schuster
Posts: 38
Joined: Mon Feb 15, 2010 4:01 am

Re: A better Drop Picker

#5 Post by D. Schuster »

Hello,
that is a very helpfull program.
If i select a name from the browse with up/ down-key and "Enter"
the full text appears in the Get-field, but if i select a name with the
mouse pointer then a double click or "Enter" doesn't work.
Is it possible to include such a behaviour as well?
Best Regards
Dieter

User avatar
hz_scotty
Posts: 107
Joined: Thu Jan 28, 2010 8:20 am
Location: Wr.Neustadt / Österreich

Re: A better Drop Picker

#6 Post by hz_scotty »

nice version ;)
best regards
Hans

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: A better Drop Picker

#7 Post by rdonnay »

Thanks to all of your suggestions, here is a more optimized version.

Notice that the selection is now handled via the ITEMSELECTED callback of the DCBROWSE, which allows both the enter key and mouse double click to work. Filling the array tag elements is now optimized as is the stabilizing of the browse. Also Freddie Mercury is back. Entering characters with no match will no longer cause an error nor will it show the browse. Focus no longer needs to be given back to the GET as it never loses focus. This was a good collaboration effort and is why I love this forum. I wish that Alaska Software would provide a forum like this instead of their antiquated news group. Some day, maybe.

Code: Select all

#INCLUDE "dcdialog.CH"
#INCLUDE "appevent.CH"

FUNCTION Main()

LOCAL GetList[0], GetOptions, cName, aNames, cPerformer, oBrowse, oPerformer

cPerformer := Space(30)

aNames := PerformerNames()

@ 0,0 DCSAY 'Enter Performer Name' GET cPerformer ;
      SAYSIZE 0 SAYBOTTOM ;
      GETOBJECT oPerformer ;
      KEYBLOCK {|a,b,o|DropDownNames(a,o,@cPerformer,aNames,oBrowse), ;
                       DC_BrowseAutoSeek(a,o,oBrowse,aNames)}

@ 1,16 DCBROWSE oBrowse DATA aNames SIZE 35,10 ;
       FONT '10.Lucida Console' ;
       FILTER {|a|a[2]} ;
       ITEMSELECTED {|a,b,o|IIF(o:isVisible(), ;
                       (cPerformer := Pad(aNames[oBrowse:arrayElement,1],30), ;
                        oPerformer:setData(cPerFormer),o:hide()),nil)} ;
       LOSTFOCUS {|a,b,o|o:hide()} ;
       NOHSCROLL

DCBROWSECOL ELEMENT 1 WIDTH 30 PARENT oBrowse

DCREAD GUI FIT TITLE 'Scoped Drop-Down' EVAL {||oBrowse:hide()}

RETURN nil

* -----------

PROC appsys ; RETURN

* -----------

STATIC FUNCTION DropDownNames( nKey, oGet, cPerformer, aNames, oBrowse )

LOCAL i, nFound, cName, nLen, nNames

IF (Chr(nKey) < 'A' .OR. Chr(nKey) > 'z') .AND. !nKey $ {xbeK_SPACE,xbeK_BS}
  RETURN nil
ENDIF

oGet:getData()

cName := Alltrim(Upper(cPerformer))
nLen := Len(cName)
nNames := Len(aNames)

nFound := AScan( aNames, {|a|Left(Upper(a[1]),nLen) == cName} )

FOR i := 1 TO nNames
  aNames[i,2] := Left(Upper(aNames[i,1]),nLen) == cName
NEXT

oBrowse:goTop()
oBrowse:forceStable()
oBrowse:refreshAll()

IF nFound > 0 .AND. !Empty(cName)
  oBrowse:show()
ELSE
  oBrowse:hide()
ENDIF

RETURN nil

* ------------

STATIC FUNCTION PerformerNames()

LOCAL aNames1[0], aNames2, i

aNames2 := { ;
   'Annie Hall', ;
   'Arlo Guthrie', ;
   'Bette Midler', ;
   'Bob Dylan', ;
   'Bobbie Gentry', ;
   'Buddy Holly', ;
   'Canned Heat', ;
   'Carol Elliot', ;
   'Carole King', ;
   'Caroline Aikin', ;
   'Cat Stevens', ;
   'Catie Curtis', ;
   'Cheryl Wheeler', ;
   'Chuck Prophet', ;
   'Chuck Pyle', ;
   'Crosby, Stills, Nash', ;
   'Danny Kaye', ;
   'Dar Williams', ;
   'Dr Hook', ;
   'Eliza Gilkyson', ;
   'Elton John', ;
   'Elvis Presley', ;
   'Freddie Mercury', ;
   'Gail Davies', ;
   'Glenn Miller', ;
   'Gordon Rowland', ;
   'Greg Brown', ;
   'Harry McClintock', ;
   'Itzhak Perlman', ;
   'James Taylor', ;
   'Jan Stanfield', ;
   'Jefferson Airplane', ;
   'Jim Croce', ;
   'Jimi Hendrix', ;
   'Jimmy Dean', ;
   'John Denver', ;
   'Joni Mitchell', ;
   'Journey', ;
   'Kevin Welch', ;
   'Krystian Zimermann', ;
   'Led Zeppelin', ;
   'Lyle Lovett', ;
   'Lynn Miles', ;
   'Madeleine Peyroux', ;
   'Michael Jackson', ;
   'Michael Lille', ;
   'Mumbo Gumbo', ;
   'Norah Jones', ;
   'Ray Charles', ;
   'Richard Pryor', ;
   'Richard Shindell', ;
   'Richie Havens', ;
   'Robin Williams', ;
   'Roy Orbison', ;
   'Sam Cooke', ;
   'Slobberbone', ;
   'Steppenwolf', ;
   'Susan Werner', ;
   'Ten Years After', ;
   'The Beatles', ;
   'The Doors', ;
   'The Eagles', ;
   'The Flatlanders', ;
   'The Moody Blues', ;
   'Tim Bays', ;
   'Toby Keith', ;
   'Tom Paxton', ;
   'Tom Petty', ;
   'Tom Prasada-Rao', ;
   'Van Morrison', ;
   'Vance Gilbert', ;
   'Vic Chestnutt', ;
   'Willie Nelson', ;
   'Yo-yo Ma', ;
   'Yundi Li'}

FOR i := 1 TO Len(aNames2)
  AAdd(aNames1,{ aNames2[i], .t. } )
NEXT

RETURN aNames1
The eXpress train is coming - and it has more cars.

D. Schuster
Posts: 38
Joined: Mon Feb 15, 2010 4:01 am

Re: A better Drop Picker

#8 Post by D. Schuster »

Thank you Roger,
that's a wunderfull, helpful function.
Dieter

User avatar
rdonnay
Site Admin
Posts: 4729
Joined: Wed Jan 27, 2010 6:58 pm
Location: Boise, Idaho USA
Contact:

Re: A better Drop Picker

#9 Post by rdonnay »

Here is a web version of the same thing.

http://donnay-software.com/ds/autocomplete.htm

This little app uses the Jquery Widget: AutoComplete

When you click on the above URL it will load the following in your web browser:

Code: Select all

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>autocomplete demo</title>
  <link rel="stylesheet" href="./Jquery/jquery-ui-1.11.4.custom/jquery-ui.css">
  <script src="./Jquery/jquery-ui-1.11.4.custom/external/jquery/jquery.js"></script>
  <script src="./Jquery/jquery-ui-1.11.4.custom/jquery-ui.js"></script>
</head>
<body>

<label for="autocomplete">Choose a Performer: </label>
<input id="autocomplete">

<script>
$( "#autocomplete" ).autocomplete({
  source: "./autocomplete.cxp"
});
</script>

</body>
</html>
When you start typing in the "Choose a Performer" box it will call autocomplete.cxp and pass it the value in the input box.
autocomplete.cxp returns a JSon array with only the performers that match the value in the input box.

Code: Select all

<!--
/// <summary>
/// Returning a JSON data stream
/// </summary>
-->
<%

cTerm := Upper(Pvalue(1))

aNames := { ;
   'Annie Hall', ;
   'Arlo Guthrie', ;
   'Bette Midler', ;
   'Bob Dylan', ;
   'Bobbie Gentry', ;
   'Buddy Holly', ;
   'Canned Heat', ;
   'Carol Elliot', ;
   'Carole King', ;
   'Caroline Aikin', ;
   'Cat Stevens', ;
   'Catie Curtis', ;
   'Cheryl Wheeler', ;
   'Chuck Prophet', ;
   'Chuck Pyle', ;
   'Crosby, Stills, Nash', ;
   'Danny Kaye', ;
   'Dar Williams', ;
   'Dr Hook', ;
   'Eliza Gilkyson', ;
   'Elton John', ;
   'Elvis Presley', ;
   'Freddie Mercury', ;
   'Gail Davies', ;
   'Glenn Miller', ;
   'Gordon Rowland', ;
   'Greg Brown', ;
   'Harry McClintock', ;
   'Itzhak Perlman', ;
   'James Taylor', ;
   'Jan Stanfield', ;
   'Jefferson Airplane', ;
   'Jim Croce', ;
   'Jimi Hendrix', ;
   'Jimmy Dean', ;
   'John Denver', ;
   'Joni Mitchell', ;
   'Journey', ;
   'Kevin Welch', ;
   'Krystian Zimermann', ;
   'Led Zeppelin', ;
   'Lyle Lovett', ;
   'Lynn Miles', ;
   'Madeleine Peyroux', ;
   'Michael Jackson', ;
   'Michael Lille', ;
   'Mumbo Gumbo', ;
   'Norah Jones', ;
   'Ray Charles', ;
   'Richard Pryor', ;
   'Richard Shindell', ;
   'Richie Havens', ;
   'Robin Williams', ;
   'Roy Orbison', ;
   'Sam Cooke', ;
   'Slobberbone', ;
   'Steppenwolf', ;
   'Susan Werner', ;
   'Ten Years After', ;
   'The Beatles', ;
   'The Doors', ;
   'The Eagles', ;
   'The Flatlanders', ;
   'The Moody Blues', ;
   'Tim Bays', ;
   'Toby Keith', ;
   'Tom Paxton', ;
   'Tom Petty', ;
   'Tom Prasada-Rao', ;
   'Van Morrison', ;
   'Vance Gilbert', ;
   'Vic Chestnutt', ;
   'Willie Nelson', ;
   'Yo-yo Ma', ;
   'Yundi Li'}

aNames2 := {}
FOR i := 1 TO Len(aNames)
  IF Left(Upper(aNames[i]),Len(cTerm)) == cTerm
    AAdd(aNames2,aNames[i])
  ENDIF
NEXT

cData := Var2Json( aNames2 )

 ::HttpResponse:ContentType := "text/json;charset=iso-8859-1"
 OutputDebugStringA( Var2Char(cData ) )

 ::HttpResponse:Reset()
 ::HttpResponse:Write( cData )
%>
The eXpress train is coming - and it has more cars.

User avatar
unixkd
Posts: 565
Joined: Thu Feb 11, 2010 1:39 pm

Re: A better Drop Picker

#10 Post by unixkd »

Hi Roger,

Can we have a Database version instead of array ?

Thanks

Joe.

Post Reply