Doc# 744
     =====================================================================
     Building IN Lists From Checkbox Menus
     =====================================================================
     Products: R:BASE                Version: 3.x, R:BASE 4.0x, R:BASE 4.5
     =====================================================================
     Area: Application Solutions     Catalog: Programming in R:BASE
     =====================================================================
     
     Check box menus are a handy feature in R:BASE. You can check off a 
     number of items and then view the selected records with a form or 
     Browse/edit or print a report.  Using the IN operator in a WHERE 
     clause lets you list a number of items to be retrieved and uses index
     processing for quick retrieval.
     
     Making the IN list from the check box menu result, however, often 
     leads to tearing of hair and beating of breast. The basic technique 
     is the same regardless of datatype and number of choices selected. 
     This article shows four different techniques starting with the most 
     basic. Following through them enables you to understand the process 
     and to add this capability to your applications.
     
     The sample database Concomp is used with all the examples. Note that 
     the techniques shown here are not the only way to solve this 
     programming problem, but they demonstrate the basic technique.
     
     The basic technique
     -------------------
     The checkbox menu puts all the choices into one variable separated by 
     commas (or the current DELIMIT setting). A sample choose variable 
     from a checkbox menu looks like this:
     
        133 Coffin,160 Simpson,167 Watson
     
     The following steps are repeated until all the menu choices have been 
     processed.
        
     1. Get a menu choice. The SSUB function is often used for this, 
        it automatically selects items based on the DELIMIT character.
     
     2. Parse the item and retrieve the part needed for the IN list 
        variable.
     
     3. Add quotes around the item if necessary and add it to the IN 
        list variable.
     
     After all the choices have been processed, parentheses are placed 
     around the final IN list variable.
     
     Technique 1
     -----------
     This technique is the quick and easy method when the data parsed from 
     the menu choice is not text and the text displayed on the menu 
     (if any) does not contain any embedded commas. The example code below 
     displays a two-column popup menu of employee id numbers and last
     names. The employees are selected, then the code parses out the id 
     numbers and builds an IN list for the id's.  This IN list is then 
     used in a WHERE clause.
     
     -- display the menu                
         
         CLS                           
         CHOOSE vchoice FROM #VALUES FOR (CTXT(empid) & emplname) +
         FROM employee AT CENTER,CENTER CHKBOX
     
     VCHOICE looks like this:
 
         133 Coffin, 160 Simpson, 167 Watson 
                                   
     Notice there are three items separated by commas. Each item
     represents one menu selection. Three choices were selected
     from the menu.
                                            
    -- simple error checking      
         IF vchoice = '[esc]' THEN          
           PAUSE FOR 10 USING 'You didn''t + select anything from  +
             the menu' AT CENTER,CENTER DEFAULT  
           RETURN                      
         ENDIF                              
                                       
    -- initialize variables            
         SET VAR vcount INT = 1
         SET VAR vtemp TEXT = (SSUB(.vchoice,.vcount))
    
    VTEMP now has the first comma delimited item.  The SSUB function 
    gets the items from VCHOICE separated by commas:
                                   
         vtemp = 133 Coffin
                                   
    -- parse the values
 
         SET VAR vlist TEXT = (SSUB(.vtemp,-1))                  
    
    VLIST gets the first ID number, 133. The -1 in the SSUB function tells 
    R:BASE to get data based on a space not a comma. The variable VLIST 
    continues to have id numbers added to it for each menu selection. When 
    done, it contains the list of selected numbers.
    
         vlist = 133
                              
    -- get each menu selection, increment the count to get the next menu 
    selection and put it into vtemp                  
         WHILE #PI > 0.00 THEN              
           SET VAR vcount = (.vcount + 1)
           SET VAR vtemp = (SSUB(.vchoice,.vcount))
    
    The WHILE loop gets new values for the variables vtemp and vlist. It
    cycles until all the menu selections have been processed.
         
    -- exit when there are no more items to get from vchoice
           IF vtemp IS NULL THEN            
             BREAK                          
           ENDIF                       
    
    -- build the list adding a comma between each item.
           SET VAR vlist = (.vlist + ',' +  + (SSUB(.vtemp,-1)) )
         ENDWH                         
                              
    -- add the parentheses around the completed list
         SET VAR vlist = ( '(' + .vlist + ')' )                  
    
         vlist = (133,160,167)                                    
    
    It looks just like you would type the list in at the R> prompt.
    R:BASE requires that the variable value includes the parentheses 
    to enclose the list.
                              
    -- edit the employee data for the selected employees
         EDIT USING employee WHERE empid IN &vlist
                              
     
     Technique 2
     -----------
     This technique is the quick and easy method when the data parsed from 
     the menu is text and there are no embedded commas or spaces in the 
     data. The same menu is used as in the previous example, but we 
     retrieve the last names instead of the id's. The second item from
     vtemp is used instead of the first and the items in the IN list have 
     quotes around them.
     
     To retrieve the second item, we simply use the SSUB function with a 
     -2 parameter. That tells R:BASE to return the second item from a 
     space delimited string.
     
     In Technique 1, we placed a comma between the items by concatenating 
     a literal comma to the values. Because the comma is a text value, we 
     needed to surround the comma with quotes. You can do the same thing 
     to concatenate a literal quote to a value. But, to tell R:BASE that 
     you want a literal quote, and you are not ending or beginning a 
     string, you double the quote character. So to concatenate a single 
     quote to the beginning of a text string you place four quotes right
     together, '''', no spaces. The first quote tells R:BASE a text string 
     is starting, the next two tells R:BASE you want a literal quote in 
     the result, and the final quote ends the text string. In the 
     following code, we concatenate the necessary quotes by using four 
     quotes together in the SET VAR command for vlist.
     
     -- display the menu
          CLS
          CHOOSE vchoice FROM #VALUES FOR (CTXT(empid) & emplname) +
            FROM employee AT CENTER,CENTER CHKBOX
     
     -- simple error checking
          IF vchoice = '[esc]' THEN
            PAUSE FOR 10 USING 'You didn''t select anything from  +
              the menu' AT CENTER,CENTER DEFAULT
            RETURN
          ENDIF
     
     -- initialize variables
          SET VAR vcount INT = 1
          SET VAR vtemp = (SSUB(.vchoice,.vcount))
          SET VAR vlist = ( '''' + (SSUB(.vtemp,-2)) + '''' )
          WHILE #PI > 0.00 THEN
            SET VAR vcount = (.vcount + 1)
            SET VAR vtemp = (SSUB(.vchoice,.vcount))
            IF vtemp IS NULL THEN
              BREAK
            ENDIF
     
     -- quotes are added around the text value retrieved using SSUB
          SET VAR vlist = (.vlist + ',' + '''' + (SSUB(.vtemp,-2)) + '''')
          ENDWH
          SET VAR vlist = ('(' + .vlist + ')')
     
     The variable vlist looks like this:
     
          ('Coffin','Simpson','Watson')
     
     -- edit the employee data for the selected employees
          EDIT USING employee WHERE emplname IN &vlist
     
     
     Technique 3
     -----------
     Many times the items on a menu are spaced for clarity, particularly 
     if multi-word columns such as company names or street addresses are 
     displayed.  When you have multi-word items, you can't use the space 
     delimited SSUB option.  You may not get the whole item.  The space
     delimit option of SSUB looks at each space as a potential delimiter, 
     thus four spaces are not considered a single delimiter, but as two 
     spaces delimited by spaces.  A space thus gets returned as part of 
     the resulting IN list.  One way to do menus with multi-word text that 
     makes for a nice display and for easy retrieval of the choices is to 
     place the columns in specific locations. Again, this technique 
     assumes there are no commas in the data displayed on the menus.
     
     This technique uses SGET instead of SSUB to retrieve the desired 
     value from the menu choice.  The difference from the previous 
     techniques is in how the vlist value is retrieved.  As with 
     Technique 2, we are retrieving a text value, the company, so the 
     result is enclosed in quotes. The menu here is a two table display 
     of contact names and company names. The company name is repeated
     for each contact.
     
     -- display the menu
          CLS
          CHOOSE vchoice FROM #VALUES FOR (LJS((contfname & contlname) +
            ,16) + company) FROM contact, customer   +
            WHERE contact.custid = customer.custid AT CENTER,CENTER CHKBOX
 
     -- simple error checking
          IF vchoice = '[esc]' THEN
            PAUSE FOR 10 USING 'You didn''t select anything from   +
              the menu' AT CENTER,CENTER DEFAULT
            RETURN
          ENDIF
 
     -- initialize variables
          SET VAR vcount INT = 1,vlist TEXT, vtemp TEXT
          SET VAR vtemp = (SSUB(.vchoice,.vcount))
     
     -- SGET is used instead of SSUB to parse the menu choice, the company
     -- name begins at position 17
          SET VAR vlist = (''''+(SGET(.vtemp,30,17 ) )+'''')
          WHILE #PI > 0.00 THEN
            SET VAR vcount = (.vcount + 1)
            SET VAR vtemp = (SSUB(.vchoice,.vcount))
            IF vtemp is null THEN
              BREAK
            ENDIF
            SET VAR vlist =  +
              (.vlist + ',' + '''' + (SGET(.vtemp,30,17 )) + '''')
          ENDWH
          SET VAR vlist = ('(' + .vlist + ')')
     
     The variable VLIST looks like this when done:
     
          vlist = ('PC Consultation and Design','Computer
                  Warehouse','Midtown Computer Co.')
     
     -- edit the customer data for the selected companies
          EDIT USING custform WHERE company IN &vlist
     
     It's a little bit more work to extract the last name from the choice, 
     but not much. You need a second temporary variable.  It holds the 
     first name/last name portion of the menu choice.  You get it by using 
     the SGET function and returning the first 16 characters.  Then the 
     vlist value is extracted from this second temporary variable using 
     the space delimit option of SSUB and getting the second item.  For 
     example,
     
     vchoice = Andy Chin    PC Consultation and Design,Bill Jones
               Computer Warehouse,Sharon Brady    Midtown
               Computer Co.
                                
          SET VAR vtemp = (SSUB(.vchoice,.vcount))        
                        
     vtemp = Andy Chin    PC Consultation and Design
    
          SET VAR vtemp2 = (SGET(.vtemp,16,1))
                           
     vtemp2 = Andy Chin
    
          SET VAR vlist = (''''+(SSUB(.vtemp2,-2))+ '''')
                           
     vlist = 'Chin' 
     
     Technique 4
     -----------
     
     So far creating the IN list has been relatively easy even when the 
     data contains embedded spaces. The problem becomes more difficult 
     when the data displayed on the menu contains embedded commas and 
     spaces. You can't use the SSUB function to split apart the menu
     choices. SSUB breaks strings apart by spaces or commas. When your 
     data contains spaces and commas the strings you get with SSUB are 
     not the complete menu choices.  One way to resolve this is to set 
     the DELIMIT character before the CHOOSE so that the menu selections 
     are delimited by something other than commas.
     
     You can leave the DELIMIT character set and use the SSUB function to 
     retrieve the data -- it works based on the current DELIMIT setting -- 
     or you can set the DELIMIT back to a comma and use the SLOC and SGET
     functions to retrieve the data. If you leave the DELIMIT set you need 
     to remember that commas must be replaced by the current DELIMIT 
     character in all commands. It can make the code harder to read, but
     requires fewer commands. Both examples are shown below.
        
     1. This example changes the DELIMIT character and leaves it set 
        until the menu choices are parsed. Notice that once the 
        DELIMIT character is changed, that character is used in all 
        commands where you would normally use a comma to delimit 
        items.  The DELIMIT character is set to ^ (Shift-6), a 
        character that is unlikely to occur in data. Another common 
        choice for the DELIMIT character is @ (Shift-2). The code here 
        is basically identical to that in Technique 3 except for the 
        different delimiter.
     
          SET DELIMIT=NULL
          SET DELIMIT=^
          CHOOSE vchoice FROM #VALUES FOR (LJS((contfname & contlname +
            )^16) + company) FROM contact^ customer   +
            WHERE contact.custid = customer.custid AT CENTER^CENTER CHKBOX
     
     The changed DELIMIT character does not appear on the menu, but is in 
     the choose variable. vchoice looks like this:
     
     VCHOICE = Sarah James     PC Distribution, Inc.^Walter Finnegan
               Computer Distributors, Inc.^Jane Ferguson   Industrial
               Computers, Inc.
     
          IF vchoice = '[Esc]' THEN
            PAUSE FOR 10 USING 'You didn''t select anything    +
              FROM the menu' AT CENTER^CENTER DEFAULT
            SET DELIMIT=NULL
            SET DELIMIT=,
            RETURN
          ENDIF
     
          SET VAR  vlist TEXT^ vtemp TEXT^ vcount INT = 1
          SET VAR vtemp = ( SSUB(.vchoice^.vcount ) )
          SET VAR vlist = ('''' + (SGET(.vtemp^30^17)) + '''')
     
     vtemp looks like this, the same as in the previous technique:
     
     VTEMP = Sarah James     PC Distribution, Inc.
     
     -- The SGET function is used to retrieve the company name.
     
          WHILE #PI > 0.00 THEN
            SET VAR vcount = (.vcount + 1)
            SET VAR vtemp = ( SSUB(.vchoice^.vcount))
            IF vtemp IS NULL THEN
              BREAK
            ENDIF
            SET VAR vlist =   +
              (.vlist + ',' + ''''+(SGET(.vtemp^30^17))+'''' )
          ENDWH
          SET VAR vlist = ('(' + .vlist + ')')
          SET DELIMIT=NULL
          SET DELIMIT=,
     
     VLIST = ('PC Distribution, Inc.','Computer Distributors,
             Inc.','Industrial Computers, Inc.')
     
     -- edit the customer data for the selected companies
          EDIT USING custform WHERE company IN &vlist
          
     2. The other approach sets the DELIMIT character for the duration of 
        the CHOOSE command only. The code then sets it back to a comma 
        and  uses the character it was to split the menu choices apart 
        using  the SLOC and SGET functions. The functions are more complex 
        because you need to put the first part of vchoice into one 
        variable, the second part into another variable. Then the second 
        variable replaces the first for the next iteration.
     
          SET DELIMIT=NULL
          SET DELIMIT=@
          CHOOSE vchoice FROM #VALUES FOR (LJS((contfname & contlname +
            )@16) + company) FROM contact@ customer   +
            WHERE contact.custid = customer.custid AT CENTER@CENTER CHKBOX
          SET DELIMIT=NULL
          SET DELIMIT=,
     
          IF vchoice = '[Esc]' THEN
            PAUSE FOR 10 USING 'You didn''t select anything FROM  +
              the menu' AT CENTER, CENTER DEFAULT
            RETURN
          ENDIF
          SET VAR vlist TEXT, vtemp1 TEXT, vtemp2 TEXT
     
     -- put the first menu choice in vtemp1, put the second into vtemp2
     -- using the location of the first @. The number 200 in the vtemp2
     -- expression is arbitrary, make it long enough to get all the
     -- remaining data from vchoice
     
          SET VAR vtemp1 = (SGET(.vchoice,(SLOC(.vchoice,'@')-1),1 ))
          SET VAR vtemp2 = (SGET(.vchoice,200,(SLOC(.vchoice,'@')+1)))
     
     VCHOICE = Sarah James     PC Distribution, Inc.@Walter
               Finnegan Computer Distributors, Inc.@Jane Ferguson
               Industrial Computers, Inc.
     
     VTEMP1 = Sarah James     PC Distribution, Inc.
     
     VTEMP2 = Walter Finnegan Computer Distributors,
              Inc.@Jane Ferguson   Industrial Computers, Inc.
     
          SET VAR vlist = ('''' + (SGET(.vtemp1,30,17)) + '''')
          WHILE #PI > 0.00 THEN
 
      -- we need to make sure there is a valid value for 
      -- SGET, if there are no more values, the SLOC returns 0 
      -- but subtracting 1 from 0 makes a negative argument for 
      -- the SGET - that is illegal. The embedded IFEQ function 
      -- sets that argument to 0 if there are no more menu choices
     
       SET VAR vtemp1 = (SGET(.vtemp2,(IFEQ((SLOC(.vtemp2,'@')),   +
         0,0,(SLOC(.vtemp2,'@')-1))),1))
       IF vtemp1 IS NULL THEN
         -- need to get the last choice into vlist
         SET VAR vlist = +
           (.vlist + ',' + '''' + (SGET(.vtemp2,30,17)) + '''')
         BREAK
       ENDIF
       SET VAR vtemp2 = (SGET (.vtemp2,200,(SLOC(.vtemp2,'@')+1) ))
       SET VAR vlist = +
         (.vlist + ',' + ''''+(SGET(.vtemp1,30,17))+'''' )
     ENDWH
     SET VAR vlist = ('(' + .vlist + ')')
     
     -- edit the customer data for the selected companies
     EDIT USING custform WHERE company IN &vlist
          
     The same basic techniques are used throughout these examples. The 
     differences lie in how the data is parsed from the menu selection 
     variable, vchoice. The way you set up your menu and the data it 
     displays determines the parsing method to use. Certainly there are 
     other options as well. Some developers use a specific character to 
     delimit the menu items, such as an Alt 255 character or a vertical 
     bar, |, and then search for that character rather than changing the 
     DELIMIT character. Whatever parsing method you use, a WHILE loop 
     repeats to parse all the choices and place them in a single variable 
     list. Then after the parsing is complete, parentheses are added for 
     your finished IN list variable.
     
     Please Note:  The preceding article contains commands that are 
     specific to R:BASE 4.5.  The R:BASE 4.5 commands are included to 
     demonstrate new features and capabilities.  These commands may be 
     excluded if you choose to use these programming techniques with 
     prior versions of R:BASE.