revUp - Updates and news for the Revolution community
Issue 101 | Dec 10th 2010 Contact the Editor | How to Contribute

Collecting User Preferences: Part 2
Successfully collect user preferences and ensure they are not lost

by Klaus Major

Hello and welcome to part 2 (of 2) of my newsletter article. You can read part 1 here.

Please download the little example stack here, you will find everything discussed below in the stack scripts of the main- and the substack.

Additionally to the things we already learned in the first article, we will learn how to manage preferences that are stored on different cards, converting multi line text to one line and converting binary data to one line text.

I recommend that you check these terms in the Livecode dictionary for more information (this is a great place to learn more about terms you haven't met before):

base64encode
base64decode

These two functions convert binary data to a string and vice versa. eMail attachments are handled this way, so can be transferred over the internet without problems.

urlencode
urldecode

These functions will convert a multiline string to a single line string. All special characters like tabstops and carriage returns are converted to alphanumerical values.
CR -> %0D%0A
TAB -> %09
etc...



To check it out, you should open the example stack, go to the prefs, do some settings, import an image, close the prefs stack and then quit LiveCode without saving. When you open the stack the next time all your input is there as you left it!

And as an extra bonus, we will also see how to write a generic script that will work for the current settings as well as for all controls that we might add in the future.

The trick is to set a custom property of the controls whose content needs to be saved. In my example stack I named the custom property "saveme" (without the quotes) and set it to TRUE. This way we can simply loop through ALL objects on a card and will only handle controls that have this property set to true.

If we try to query a custom property of an object that does not have this custom property at all, we do not get an error fortunately, and that is exactly what we need here. You can check this for yourself in the "Inspector" of the fields, buttons and images I use in my example.

And doing so for your preferences is also pretty easy:
1. Select your control.
2. Doubleclick your object if the Inspector palette is already not open.
3. Select "Custom Properties" from the popup button at the top of the palette.
4. Click + to add a new custom property.
4. Name it whatever you want, but this should of course be the same name for all of your objects!
5. Enter the word true in the field.
That's it!

And here is what's going on in the example stack:
1. We load the preferences text file, if present, and set the content of our controls to the stored values. Please see the first part of the article to get an explanation of the functions that will return a place to store the prefs, save and load that preferences text file.

There are surely a lot more ways to make this possible but I choose this format:

  • One prefs per line and the infos divided by a TAB, so we can "set the itemdel to TAB" and access all necessary information
  • Number of card TAB Type of control TAB Name of control TAB Style of control (only necessary for buttons!) TAB Content of control TAB Rect of control (only necessary for images!)

1. Number of card
2. Type of control
3. Name of control
4. Style of control (only necessary for buttons!)
5. Content of control
6. Rect of control (only necessary for images!)

## We collect and store the prefs when the user closes the stack
on closestack
   mk_collectprefs
end closestack

# This handler will collect all necessary prefs and pass them
# to the function that will store them on the users HD
command mk_collectprefs
   lock screen
   
   put empty into tAllPrefs
   
   ## Loop through all cards:
   repeat with i = 1 to the num of cds
      put the num of controls of cd i into tNumofcontrols
      
      ## Loop through all control on card:
      repeat with k = 1 to tNumofcontrols
         
         ## This work for ALL controls, even if we add more of
         # them later to our stack!
         ## As long as they have their custom property "saveme"
         # set to TRUE!
         if the saveme of control k of cd i <> true then
            next repeat
         end if
         
         ## Now we can collect the contents of the control
         ## Gives -> button "name of button"
         put the name of control k of cd i into tName
         put word 1 of tName into tType
         put word 2 of tName into tShortName
         
         ## Important step!
         replace QUOTE with empty in tShortName
         
         ## Now we need to differ:
         switch tType
            case "field"
               put empty into tStyle
               
               ## Now we need to URLENCODE the text, so we can store
               # it in ONE line!
               put urlencode(the text of fld tShortName of cd i) into \
                      tContent
               break
               
               ## No scrollbar in this example, but we take this into
               # account anyway!
            case "scrollbar"
               put the thumbpos of sb tShortName of cd i into tContent
               break
               
            case "button"
               
               ## Here we need to differ between Menubuttons like
               # option and combo boxes and radio buttons and checkboxes
               put the style of btn tShortname of cd i into tStyle
               switch tStyle
                  case "radiobutton"
                  case "checkbox"
                     put the hilite of btn tShortname of cd i into tContent
                     break
                  case "menu"
                     put the label of btn tShortname of cd i into tContent
                     break
               end switch
               break
               
            case "image"
               put empty into tStyle
               
               ## Apparently the imagedata of an empty image is NOT
               # empty,
               ## so we need this little cumbersome check to see if
               # the image is REALLY empty:
               if the filename of img tShortName of cd i = empty AND \
                      the text of img tShortname of cd i = empty then
                  put empty into tContent
               else
                  
                  ## Here we get the binary imagedata and convert it to
                  # a ONE line string
                  put the imagedata of img tShortName of cd i into \
                         tImageData
                  
                  ## Step 1: convert binary data to text data:
                  put base64encode(tImageData) into tImageDataText
                  
                  ## Step 2: convert this string to ONE line:
                  put urlencode(tImageDataText) into tContent
               end if
               
               ## Now we also need to save the rect of the image,
               # since setting the text of an image
               ## requires the image to have the SAME dimensions as
               # the source image!
               ## Otherwise we get visual GARBAGE :-)
               put TAB & the rect of img tShortName of cd i after \
                      tContent
               break
         end switch
         
         ## NOW we can prepare the list of the prefs to store
         ## We use this format here:
         ## Number of card TAB Type of control TAB Name of control
         # TAB Style of control (only neccessary for buttons!) TAB
         ## Content of control TAB Rect of control (only
         # neccessaryfor images!)
         put i & TAB & tType & TAB & tShortName & TAB & tStyle & \
                TAB & tContent & CR after tAllPrefs
         
         ## End all controls
      end repeat
      
      ## End all cards
   end repeat
   
   ## Get rid of trailing CR
   delete char -1 of tAllPrefs
   
   ## Now we call the function that will store this to a text file
   put mk_setprefs(tAllPrefs) into tResult
   
   ## Better doublecheck!
   if tResult <> empty then
      answer "There was an error when saving the preferences!" & \
             CR & tResult
   end if
end mk_collectprefs

Checking if the file was written successfully at the end of this script is of course optional, but keep in mind that the developers (WE!) are always blamed if something goes wrong, so its best to take care of everything that might go wrong! ;-)

2. Now we need handlers that collect the users input and pass it to the function mentioned above that will store the preferences to a file, take the stored info and "restore" the users input when the user opens that prefs stack the next time.

They are located in the stack script of the substack "Preferences_sub".
When the user closes the stack, we collect his/her input and write this info to the users harddisk:

## we collect and store the prefs when the user closes the stack
on closestack
   mk_collectprefs
end closestack

## This handler will collect all necessary prefs and pass them
# to the function that will store them on the users HD
command mk_collectprefs
   lock screen
   
   put empty into tAllPrefs
   
   ## Loop through all cards:
   repeat with i = 1 to the num of cds
      put the num of controls of cd i into tNumofcontrols
      
      ## Loop through all control on card:
      repeat with k = 1 to tNumofcontrols
         
         ## This work for ALL controls, even if we add more of
         # them later to our stack!
         ## As long as they have their custom property "saveme"
         # set to TRUE!
         if the saveme of control k of cd i <> true then
            next repeat
         end if
         
         ## Now we can collect the contents of the control
         ## Gives -> button "name of button"
         put the name of control k of cd i into tName
         put word 1 of tName into tType
         put word 2 of tName into tShortName
         
         ## Important step!
         replace QUOTE with empty in tShortName
         
         ## Now we need to differ:
         switch tType
            case "field"
               put empty into tStyle
               
               ## Now we need to URLENCODE the text, so we can store
               # it in ONE line!
               put urlencode(the text of fld tShortName of cd i) into \
                      tContent
               break
               
               ## No scrollbar in this example, but we take this into
               # account anyway!
            case "scrollbar"
               put the thumbpos of sb tShortName of cd i into tContent
               break
               
            case "button"
               
               ## Here we need to differ between menubuttons like
               # option and combo boxes and radio buttons and checkboxes
               put the style of btn tShortname of cd i into tStyle
               switch tStyle
                  case "radiobutton"
                  case "checkbox"
                     put the hilite of btn tShortname of cd i into tContent
                     break
                  case "menu"
                     put the label of btn tShortname of cd i into tContent
                     break
               end switch
               break
               
            case "image"
               put empty into tStyle
               
               ## Apparently the imagedata of an empty image is NOT
               # empty,
               ## so we need this little cumbersome check to see if
               # the image is REALLY empty:
               if the filename of img tShortName of cd i = empty AND \
                      the text of img tShortname of cd i = empty then
                  put empty into tContent
               else
                  
                  ## Here we get the binary imagedata and convert it to
                  # a ONE line string
                  put the imagedata of img tShortName of cd i into \
                         tImageData
                  
                  ## Step 1: convert binary data to text data:
                  put base64encode(tImageData) into tImageDataText
                  
                  ## Step2: convert this string to ONE line:
                  put urlencode(tImageDataText) into tContent
               end if
               
               ## Now we also need to save the rect of the image,
               # since setting the text of an image
               ## requires the image to have the SAME dimensions as
               # the source image!
               ## Otherwise we get GARBAGE :-)
               put TAB & the rect of img tShortName of cd i after \
                      tContent
               break
         end switch
         
         ## NOW we can prepare the list of the prefs to store
         ## We use this format here:
         ## Number of card TAB Type of control TAB Name of control
         # TAB Style of control (only neccessary for buttons!) TAB
         ## Content of control TAB Rect of control (only
         # neccessaryfor images!)
         put i & TAB & tType & TAB & tShortName & TAB & tStyle & \
                TAB & tContent & CR after tAllPrefs
         
         ## End all controls
      end repeat
      
      ## End all cards
   end repeat
   
   ## Get rid of trailing CR
   delete char -1 of tAllPrefs
   
   ## Now we call the function that will store this to a text file
   put mk_setprefs(tAllPrefs) into tResult
   
   ## Better doublecheck!
   if tResult <> empty then
      answer "There was an error when saving the preferences!" & \
             CR & tResult
   end if
end mk_collectprefs

Hint:
Of course we could take a snapshot of the user image and store it or simply copy the original image file to our preferences folder, but this is an example about how to store binary data in a text file so I did it this way.

Well, that's it basically.
Now we are ready to store user preferences in our standalones and have a script that is ready for the future, since it will always work, even if we add fields, buttons, scrollbars and/or images!

Have fun!

About the Author

Klaus Major is a experienced scripter with expertise dating back many years. His company, major-k, offers custom software development and LiveCode mentoring. Klaus also enjoys playing
electric bass guitar in jazz and rockbands.

Main Menu

What's New

Use Coupon
XMASMARKET
for 10% discount on any Marketplace purchase