REALbasic University Resources:

RBU: Glossary Defines common REALbasic programming terms
  Archives Previously published columns
Translations: Dutch Courtesy of Floris van Sandwijk
  Japanese Courtesy of Kazuo Ishizuka
  Chinese Courtesy of Dong Li
  RBU Translation Guide Information on Translating RBU into other languages
Books: Matt's Book (2nd Edition!) Ideal for experienced programmers
  Erick's Book Best for beginning programmers
Websites: Mother Ship The publisher of REALbasic
  RB Webring Links to hundreds of REALbasic websites
  RESExcellence Another REALbasic programming column
  REALbasic Developer Magazine The premiere source for REALbasic instruction.

REALbasic University is Sponsored by

Make your Mac do what YOU want it to. Create games, utilities, cool Mac OS X tricks. Download REALbasic now and create your own software.


Print This Article

REALbasic University: Column 016

Two Sample In-House Programs

Last week I described several REALbasic programs I created for personal use. Some of those are fairly complicated -- too complex for me to explain how they work in a few paragraphs (I don't want to dwell excessively on these minor programs, just give you a taste of RB's capabilities).

I do have a couple very simple programs that I thought illustrate the power and convenience of REALbasic. We'll go over these in detail in this week's lesson.

Fast Finger

A year or so ago I discovered ABC's Who Wants to Be A Millionaire and watched it regularly for a while. (I finally gave it up as I just don't have time, but I still like the show.) I even attempted to qualify as a contestant. Unfortunately, while my trivia skills are above average, my speed skills are not. Give me ten seconds to put four ordinary household appliances in alphabetical order and you'll get a blank expression.

I liked playing along with the show at home, but the "Fastest Finger" contest always annoyed me: it goes so fast I have trouble measuring my own time, and by the time I've figured out the third or fourth part of my answer I've forgotten what I picked for the first part!

Of course it's unfair: the contestants have buttons they can push to pick to set the order of their answers while I have to remember everything while keeping an eye on my stopwatch.

So one night -- during the show -- I whipped open REALbasic and created Fast Finger. This is a dirt simple program designed to do just one thing: record how fast you can push four buttons. That's it. If you run it while you're watching the show, you can test yourself against the other contestants during the "Fastest Finger" portion of the show.

It's not fancy and it's nearly useless, but it gave me something to do during the commercials. It also demonstrates a few nice things about REALbasic. And if you're lucky, it might even help you get on the game show!

Since the user interface of Fast Finger is a little complicated, I'm going to give it to you all finished. That way all you have to do is put in a few lines of code (which I'll explain), and you'll be all set to test your finger speed.

First download the following REALbasic project (you'll need Stuffit Expander to decompress it):

fastfinger.hqx

Open the project in REALbasic (it's in RB 2.1 format, so it will work with REALbasic 2.1 or 3.x). This is what Window1 looks like in the IDE.

The project does nothing at this point: it's just an interface, with no code. First, let's add an important property to the main window, Window1. We're adding it to Window1 because we want all routines within Window1 to be able to access this property -- to be able to look at it or change it.

Go to the Edit menu and choose "New Property" and type in "startTime as double" (no quotes) in the dialog box that is displayed. Click Okay or press return.

The first code we'll add is to the Timer1 object. A timer is a special REALbasic object that executes after a certain amount of time has elapsed. You set the amount of time, in milliseconds, with the timer's period property. In this case I've preset Timer1 to 10 milliseconds: that means one hundred times per second, whatever code is in Timer1's Action handler will be executed.

Select Timer1 on Window1 (open the window if it is closed) and press Option-Tab (hold the Option key down while pressing the Tab key). That will open the Code Editor right to Timer1's Action event.

Now put in the following (copy and paste it, if you want):

Source code for Window1.Timer1.Action:

 dim theTime, secs, huns as double 

theTime = microseconds
secs = (theTime - startTime) \ 1000000
huns = (((theTime - startTime) - (secs*1000000)) \ 10000)

Label.text = "00:" + format(secs, "00") + ":" + format(huns, "00")

What the above does is display a clock of the current time. We use REALbasic's microseconds function to find out the current time to 1000th of a second. Then we break that information into two pieces, the number of seconds since the countdown was started (startTime) and the number of hundredths of a second since the countdown was started. Finally, we display the time in a staticText object.

Now let's take a look at our pushButtons. You may have noticed that while we have four visible buttons on Window1, there is only one Button in the Controls list of the Code Editor:

Shouldn't there be four buttons listed?

No, there shouldn't. That's because I've used a special REALbasic feature: a control array. Remember when we learned about arrays while builing GenderChanger? Well, a control array is an array that contains controls. In this case, pushButtons! Just like with a regular array, you access individual objects within the array using an index number. (Notice that in the Control list Button() has parenthesis at the end: that's a reminder that it's a control array.)

(Note that the Button() control array's index starts at zero, so the buttons are number 0-3, not 1-4.)

The benefit of a control array is enormous: instead of having four similar yet separate buttons, each with identical code, I can have one button with a single set of code! I can always tell which button the user has clicked by checking the Index variable that REALbasic sends with all events.

In Detail

How do you create a control array? It's simple. There are a couple ways. With a control selected in the IDE, go to the Properties palette and put a number in the Index field. The Index field is normally blank, but if you put a number there, REALbasic assumes you're wanting to create a control array.

After that if you Duplicate the control, REALbasic will automatically increment the index number for the new control.

Another way to create a control array is to change the names of several identical controls to match. REALbasic will ask if you're wanting to creating a control array. Say yes, and RB will number the controls for you.

In this case, we're just going to pass the button pushed to a method (which we'll write in a second) which will handle the push. All we have to put into the button's Action handler is this (don't include the bold title, of course):

Source code for Window1.Button.Action:

 doButton(index) 

Next, let's go to Window1's Open method. All we're going to do here is make sure that all four of our pushButtons are disabled when the program is first launched. Since our buttons are a control array, this is simple.

Source code for Window1.Open:

   dim i as integer 

for i = 0 to 3
button(i).enabled = false
next

Now go to Window1's Keydown event. This method gets called whenever the user presses a key. What we want to do here is mimic buttons being pushed with key presses. In other words, the user can click button "A", type "A", or type "1" and all those actions have the same result.

To do this we simply use a select case statement (see RB's help for more details) and if the user's typed an appropriate letter or number, we send a command to our "doButton" routine.

Source code for Window1.KeyDown:

   select case uppercase(key) 
case chr(13) // return key
if startButton.enabled then
startEverything
end if
case "1"
doButton(0)
case "2"
doButton(1)
case "3"
doButton(2)
case "4"
doButton(3)
case "A"
doButton(0)
case "B"
doButton(1)
case "C"
doButton(2)
case "D"
doButton(3)
end select

So what does this magical "doButton" routine do? Well, let's find out! Create a new Method by going to the Edit menu and choosing "New Method". For the method name, use "doButton" (no quotes). For the method's parameters, put "index as integer" (that will allow this routine to receive the button number). Leave the return value empty, and click Okay.

In the method's code area, type (or paste in) the following:

Source code for Window1.doButton:

   if button(index).enabled then 
button(index).value = true // make it look pushed
button(index).enabled = false // disable it

// Displays the buttons in the order you pressed them
answers.text = answers.text + button(index).caption + chr(13)

// Check to see if all four buttons have been pressed;
// if so, we stop the clock and enable the Start button.
if not (button(0).enabled or button(1).enabled or button(2).enabled or button(3).enabled) then
timer1.mode = 0
startButton.enabled = true
end if
end if

Now go to startButton's Action method and type in "startEverything" (no quotes). This is going to be a separate routine that initializes the program (resets the clock, turns on all the buttons, etc.).

Create a new Method called "startEverything" with no parameters or return values. Here's the startEverything code:

Source code for Window1.startEverything:

   dim i as integer 

startButton.enabled = false

for i = 0 to 3
button(i).enabled = true
button(i).value = false
next

answers.text = ""
startTime = microseconds
timer1.mode = 2

As you can see, it's simple.

Guess what? We're done! That's the whole program. Run it and you should be able to start the timer, click the buttons, and see what order you clicked them and how long it took, just like on TV. Here's what the program looks like in action:

If you don't feel like typing in all that code, here's the complete program, ready to compile and run.

fastfingerfull.hqx

Count Emails

The second program I wanted to demonstrate is one I wrote one day while studying my Stone Table Software customer list. I was looking at all the emails -- many from foreign countries -- and I started wondering what percentage of my customers come from which countries. Email addresses and domains aren't a surefire way to determine a person's origin, but they can tell you a little.

So I sat down and wrote this little program which accepts a text file of emails (one email per line) and sorts and counts them by extension (.com, .net, etc.).

To start, create a new project in REALbasic. Using the default Window1 that's created for you, drag a listbox onto it. Make the listbox big so it fills all of Window1.

For ListBox1's properties, check the HasHeading option and put "3" for the number of columns. For columnWidths, put "50%, 25%, 25%". (That will divide ListBox1 into three columns, the first at a width of 50% and the others at 25%.) Your properties window should look like this:

Option-Tab with ListBox1 selected to open the Code Editor. Go to ListBox1's Open event. Here we're going to put in the following:

Source code for Window1.ListBox1.Open:

 me.acceptFileDrop("text") 
me.heading(0) = "Domain"
me.heading(1) = "Quantity"
me.heading(2) = "Percentage"

The above does two things: one, it sets ListBox1 to allow text files to be dropped on it, and two, it names the listbox's headers.

While we're thinking about it, let's make sure our project has a "text" File Type created. Go to the Edit menu and choose "File Types". If you see an item there named "text", you're finished. If not, you'll need to add one. (Without the File Type being defined, REALbasic wouldn't know what kinds of files to allow the user to drop on the listbox.)

Click the "Add" button, name it "text" and use "ttxt" for the Creator and "text" for the Type. Click Okay twice and you're finished.

Now go to the DropObject event. This is a major part of the program. What happens is the user drops a text file on the control, so first we check to make sure they dropped a file and then we open it. We load up theList array with each line from the text file, then close it. We call a few other routines -- stripList, sortIt, countDuplicates, and updateList -- and then we're done.

Source code for Window1.ListBox1.DropObject:

  dim in as textInputStream 

if obj.folderItemAvailable then
if obj.folderItem <> nil then
in = obj.folderItem.openAsTextFile
if in <> nil then
// Erase the arrays
redim theList(0)
redim itemCount(0)

do
theList.append lowercase(in.readLine)
loop until in.EOF
in.close
stripList
countDuplicates
updateList
me.headingIndex = 1 // sorts by quantity
end if
end if
end if

We're going to need to create all those methods now, so let's get started. None of the methods have any parameters or return any values, so they're simple. I find it easier to create them all first, then go back and fill in the details later. So go to the Edit menu and choose "New Method" a bunch of times and use the following names for the methods:

stripList countDuplicates updateList

When you've got all three methods ready, let's put in the code, starting with stripList. It's a routine that deletes everything but the email extension from the email addresses. The algorithm used is a simple one: it simply looks for the last phrase deliminated by a period. Since all email address end in a dot-something, the text to the right of the dot is saved and everything else is thrown away (stripped). This way our list of emails is paired down to simple "com", "net", and other extensions.

If other words, this:

idiot@whatever.co.uk yahoo@yahoo.com guy@thisplace.ch moron@aol.com

becomes

uk com ch com

Source code for Window1.stripList:

   dim n, i as integer 

n = uBound(theList)
for i = 1 to n
theList(i) = nthField(theList(i), ".", countFields(theList(i), "."))
next

In Detail

How does the above work? Well, the key line is the code in the middle of the for-next loop. To understand it better, let's mentally run it the way the computer would, using a sample address.

Let's say we have an address line "moron@earthlink.net" (theList(i) is equal to "moron@earthlink.net"). That means the key line in the above is really saying this:

 theList(i) = nthField("moron@earthlink.net", ".", countFields("moron@earthlink.net", ".")) 

Process the end of it first. The code countFields("moron@earthlink.net", ".")) will return a number, the number of fields in the phrase delimited by a period. Since there is only one period in the phrase, there are therefore two fields ("moron@earthlink" and "net", everything to the left and right of the period). So replace the countFields code with the number 2 (the result).

 theList(i) = nthField("moron@earthlink.net", ".", 2) 

So now our code is effectively saying, "Put field number 2 (the last) of 'moron@earthlink.net' into theList(i)." Since the second field is "net" we have effectively changed "moron@earthlink.net" to "net"!

CountDuplicates is a little more complicated. It loops through the entire list and counts how many of each kind exist. If it finds a duplicate, it does two things: it deletes the duplicate from theList and increments the number stored in that item's count. (The item's count is stored in the itemCount array.)

Note how we don't use a for loop for the outermost loop; I used a while loop instead. I did this because the size of the loop changes: when we find a duplicate item we delete it, reducing the number of items in the list. Using a while loop means that the computer will keep repeating through the list until it reaches the last unique item.

Source code for Window1.countDuplicates:

   dim total, n, i, j, found as integer 

i = 1
while i < uBound(theList)
redim itemCount(uBound(theList))
found = 0

n = uBound(theList)
for j = 1 to n
// Check through
if theList(i) = theList(j) and i <> j then
found = j
end if
next // j

if found > 0 then
itemCount(i) = itemCount(i) + 1
theList.remove found
else
i = i + 1
end if
wend

// Figure out percentages
n = uBound(itemCount)
total = 0
for i = 1 to n
total = total + itemCount(i) + 1
next // i

redim percentList(n)
for i = 1 to n
percentList(i) = ((itemCount(i) + 1) / total)
next // i

The final part of CountDuplicates is where we calculate the percentage of each kind of email. To do that we first need a total: not the number of elements in the list, but the total number of emails. So we count through the itemCount array, adding up each item. Since an itemCount element contains a zero if there's only one of that item, we add a one to it to get the actual count.

We finish by initializing the percentList array to the size of our list, then set each element in the array to the appropriate percentage. Note that we again add one to the itemCount value.

Our final method for the program is the updateList method. This is used to display the information in listBox1. It first deletes any existing rows in listBox1 (if you didn't do that, dropping a second file on the existing listbox would display the wrong content), then counts through the list of items.

For each item in the list, it puts the content in the appropriate column of the listbox. The first item is in the addRow command: that's the ".com" or whatever was part of the email address. Once we've added a row, we don't want to add another, we want to work with the row we just added. So we use the lastIndex property which contains the index number of the last row we added. We use the cell method to put the actual item count (again, we add one to it) and the percentage into the correct columns.

Source code for Window1.updateList:

   dim n, i as integer 

listBox1.deleteAllRows
n = uBound(theList)
for i = 1 to n
listBox1.addRow theList(i)
listBox1.cell(listBox1.lastIndex, 1) = format(itemCount(i) + 1, "000")
listBox1.cell(listBox1.lastIndex, 2) = format(percentList(i), "00.#%")
next

Whew! Our program's almost done: we just need to add our arrays as properties to Window1. So with the Window1 code editor window visible, go to the Edit menu and choose "New Property". You'll do this three times, adding the following properties:

itemCount(0) as integer percentList(0) as double theList(0) as string

Save your program and run it. It should display a simple window with a listbox in it.

Oh no! You need some email addresses to test this, don't you. Okay, here's a list of hopefully fictitious addresses I made up. (I tried to use a variety of extensions.) Save them to a text file and you'll be able to drag the text file to the listbox. (Option-click on this link to save the text file to your hard drive.)

idiot@whatever.co.uk yahoo@yahoo.com guy@thisplace.ch moron@aol.com yes@no.no idiot@earthlink.net moron@earthlink.net someaddress@dingo.au moron@sympatico.ca italianguy@h.it frenchguy@paris12.fr idiot@aol.com waffle@panda.be noone@columbiau.edu sg@interkom.de school@drakey.edu barf@is.is ziggy@ipalot.net teacher@mail.k12.md.us moron@yahoo.com jondough@unisono.net.mx babes@blondes.se apple@mac.com none@all.nl

After you drag the file to the listbox, your window should look similar to this:

Once again, for those who don't feel like typing in all that code, here's the complete program, ready to compile and run.

countemails.hqx

Good. That's it for this week. I hope these little programs were helpful and you learned something. For me they represent the real power of REALbasic: the ability to quickly create a solution to a little problem without spending any money.

Next Week: We'll try a little animation by using sprites to create a simple two-dimensional arcade game.

Letters

For those of you who responded to last week's survey on things you'd like to do with REALbasic, thanks! I received many responses and I'll be publishing a summary in a future column. If you haven't sent in your response, feel free to send it in now.

Our first question this week comes from Tim & Sarah Reichelt, who write:

Hi,

I'm sure there is an obvious answer to this, but how can I change the text color in a listBox? I can't even change the color for the whole list and I want to be able to specify different colors for different lines.

Interesting question! Odd that I've never ran into this, but there doesn't seem to be a way to do what you want. REALbasic provides no method for setting the color of either individual lines or the entire contents of a listbox. If this is something you really need, bug REAL Software and get them to add the feature.

In the meantime, there are a couple potential workarounds, but like all workarounds, they have limitations and disadvantages. First, you could check on the web to see if someone has created a replacement listbox which lets you change the text color. There are plug-ins to REALbasic which allow to create more sophisticated controls, and there are RB-made classes which mimic standard controls. (A good place to start is the RB web-ring linked at the top of this page.)

Another approach, if your listbox needs are modest, is to create your own listbox class. It's a bit more complicated than I can detail here, but if you don't need listbox features like multiple columns, editable text, folders, or pictures, it wouldn't be too hard to roll your own using a canvas and a scrollbar control. Remember, a canvas is simply a graphics area: you can draw lines of text in it, each in a different color if you want, and use a scrollbar control to scroll the visible text. (If that sounds like it would work for you and you don't find an existing solution on 'net, let me know: I may be interested in creating this as an example project for RBU.)

I also received a letter from Sir Steven, who includes a slew of questions in a single paragraph:

How exactly do you make global variables. You mentioned once that you could use a method? Do you need to fill in all the edfields? How do you change its value? Are there any other ways to use or make global variables. p.s. Those val() and str() prefixes solved so many problems. I hadn't been able to find anywhere how do do that. --

Learn to use the return key, Sir Steven! Just teasing. The answers to your questions, as best as I can, are as follows.

How to create a global variable? Put the variable as a property inside a module. Modules are inherently global and any property put in a module is available to all parts of your program.

Use a method? A method is REALbasic's equivalent of a procedure/function/subroutine in other languages. (By the way, if you put a method in a module, it too is global.)

Do you need to fill in all the edfields? I'm not sure what you mean by this; are you referring to the fields on the Properties palette? If so, the answer is no: REALbasic will use default (standard) values if you don't override them, so you only need to change the ones you find necessary.

How do you change its value? Once you've created a global variable, you can change its value simply by assigning it a new value (as in "gVar = 10"). Since it's a global, you can do this anywhere in your program.

Are there any other ways to make global variables? No, that's pretty much it. If you define a property that's part of an object (such as a window), the variable is local only to that window. Of course you can still access it from another object by naming it directly, so it's not inaccessible, just awkward. For instance, if Window1 contains a property called myVar, then a routine in Window2 can access it by saying Window1.myVar.

Keep in mind that there are degrees of globalness: a property local to Window1 is essentially global for Window1 (all routines inside Window1 can access the variable). That might be all you need. A variable created inside a particular method, however, is only available within that method: other routines cannot access it. Therefore a true global variable must be created inside a module so that all routines can access it.

I hope that helps; if I misunderstood one of your questions, let me know and I'll try to remedy it next time.

New RBU Feature

Note that at the top of this and all past RBU columns, we now have a special "Print this article" link. This will take you a print-format version of the column. If you prefer to have a local version on your hard drive, you can either use your web browser's "Save as web archive" feature, or use Jim Walker's PrintToPDF driver and print the page as a PDF file.


About the Column
REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers.

Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.)

About the Author
is an author, philosopher, graphic designer, photographer, film director, soccer fanatic, and programmer (among other things). He writes for MacOpinion, runs his own software company, Stone Table Software, which sells the revolutionary Z-Write word processor, and is Publisher and Editor of REALbasic Developer. He lives in Northern California with his cats, Mischief and Mayhem, and is rapidly running out of free time.

See the REALbasic University Archives


REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.

Email This Article - Comment On This Article

.

Reader Specials

Server Racks Online:
Apple Xserve CompatibleServer Racks and Universal Network Racks
42U KVM Switch Solutions:
High-End Mac and Multi-Platform KVM Matrix switching solutions!
Digital Camera Online:
Great prices on Digital Cameras and accessories!
KVM Switches Online:
Great prices on Mac KVM Switches from the leading manufacturers!
LCD Monitors Online:
Great prices on LCD Monitors from the leading manufacturers!
LCD Projectors Online:
Shop online for LCD Projectors from the leading manufacturers!
USB 2.0 Online:
Great prices on USB 2.0 products from the leading manufacturers

Serious Business Software:
Accounting, Sales, Inventory, CRM, Shipping, Payroll & more!

KVM Switch solutions for MACs:
DAXTEN is a KVM switch, KVM extender and monitor splitter specialist for PC, SUN and MAC applications from name brand manufacturers - offices worldwide.

The "Think Different Store: The iPod Accessories Store - iPod cases, iPod mini, iPod photo, speakers, itrip, inMotion, Soundstage and all other iPod accessories

Earn Cash with the ThinkDifferent Store Affiliates Program

Need A Web Site?
Applelinks Web Hosting Starting at 19.95 a Month

iTunes_RGB_9mm

.

iTunes_RGB_9mm

Cool Mac Gear


iPod 1G-2G
iPod 3G
iPod 4G
iPod Mini
PowerBook-iBook
Keyboard Skins
Garageband