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 041

RBU Pyramid XIV

Welcome back, everyone! I trust everyone had a restful holiday and are geared up for an exciting new year. REALbasic University is back in session, and we're continuing with our RBU Pyramid project.

Adding a Help System

We're nearing the end and almost finished with our game. But a good game needs instructions: what if the user doesn't know the rules of Pyramid? So today we'll add an instruction window.

For RBU Pyramid I wanted a simple help system. Nothing fancy, but competent, and above all, I wanted it easy to create. I could have used the standardize help system I use for my software programs, except that it's complex (it translates an HTML help file to styled text) and it didn't work under Windows (I've since completely rewritten that help system).

The traditional help system I use is a two-paned window, similar to my Z-Write word processor. On the left is a listBox of topics, and on the right is the help text. When you click on a topic, it's displayed on the right. Elegant, but difficult to program, especially when you add in features like the ability to adjust the width of the listBox.

But I did like the idea of multiple topics: a user doesn't like having to wade through a 10-page help file just to find out how to play the game. So one of the first thing I decided was instead of using a listBox-and-EditField combo, I'd use a popupmenu instead. The popupmenu would contain the topics and they'd be displayed in an editField underneath it. I quickly created a window that looked like this:

You should be able to recreate this easily: that's a staticText in the upper left, a popupMenu to its right, and beneath them is an editField. That's it!

The window has the following properties (settings):

The editField should have these properties:

The text property of the editField should be filled with the actual help text. I've already created this, so you can just put in this text file.

For a help system like this, you have several choices with how to get the text into the program. The simplest is just to include the text in the program itself (by putting the text into an editField), which is exactly what we've done. Other choices include putting the text into the resource fork of the program (which wouldn't work for a Windows version of your program) or in an external file (which could be a program if that external file can't be located, i.e. the user moved or deleted it). In our case, the simplest solution is also the best for our needs.

Our next problem is that we must divide our help text into separate topics. Once again, I went for the simplest solution: I decided that each section of text would begin with a headline in a particular format. I chose the following format:

  
*** topic goes here ***

That's three asterisks followed by a space, the topic name, another space, and three more asterisks. By searching the help text for text in that format, I can break the text into various sections and extract the topic names. (In computer programming lingo, this is called parsing.) As long as that particular heading text is unique (there aren't a series of asterisks in the body of the text), we're fine.

The traditional method I'd use for this kind of system would be to set up an array data structure, with the text for each topic as separate string elements in the array. That, however, is more complicated than we need: why not just have program search the help text for the topic name and then scroll to it? That way there's only one piece of text and the topic menu just lets a user easily jump to an appropriate subject.

Coding Popupmenu1

Let's try it! Open the code editor for InstructionsWindow (option-tab while the window is selected in your project list or double-click popupMenu1) and find the code for popupMenu1. Go to the Open event and put in this code:

  
dim s as string
dim i, n as integer

n = 0
s = editField1.text
i = inStr(s, "*** ")
while i > 0
n = inStr(i, s, chr(13)) // rest of line
me.addRow mid(s, i, n - i)
i = inStr(i + 3, s, "*** ")
wend

// select first topic
me.listIndex = 0

What does this do? Well, in simple terms, it scans the help text for help topics and adds them to popupMenu1's menu. There's not much code here, so how does it accomplish this magic? Let's analyze it.

We start out by initializing some variables: n is set to zero, s becomes our help text, and i is an integer representing the first occurrence of "*** " in the text (note the space after the three asterisks).

Then we set up a while loop: while i is greater than zero we keep repeating the loop. Since i is set by the inStr function, which returns the character position of a search-for item within some text, it is set to zero when the search fails. So our while loop will therefore continue until there are no more occurrence of "*** " in the text!

Next, we find the end of the line of the topic. We do that by searching for a return character (chr(13)) within the help text, but starting the search at position i. We save this information into the variable n.

Then we use the mid function to grab the entire topic line. Since n is the end of the line and i is the start, the length (in characters) of the line is n - i. We pass that to mid, starting the selection at i, our start point, and grabbing n - 1 characters. The result is the topic: we pass that to popupMenu1's addRow method, which simply adds a menu item with our topic as the text.

Finally, we reset i with a new search: it's still looking for "*** " but we start the search at i + 3. Note that that's past were we last stopped. If we started right at i, the search would immediately be successful as it would find the same topic again! (Our while loop would also never end -- the program would be stuck forever in an endless loop.)

Finally, we set popupmenu1 so the first menu item is selected.

Since all this happens when the window is first opened, from the user's perspective, the popupmenu simply has a list of help topics in it.

But what happens when the user selects a menu topic? That's pretty simple. Go to popupMenu1's Change event and put in this:

  
dim i, j as integer

if me.listIndex > -1 then
i = inStr(editField1.text, me.text)
j = inStr(i, editField1.text, chr(13))

editField1.selStart = len(editField1.text)
editField1.selStart = i - 1
editField1.selLength = j - i

end if

The first thing we do is check to make sure that popupMenu1.listIndex does not equal -1. That's because -1 means that no menu item is selected. Any other value means the user has chosen an item on the menu, so we then simply search the text for that item.

The variable i is set to starting position of the search-for text (me.text, which is the topic name on the menu), and j is the end of that text's line. Then we just highlight (select) that text by setting the selStart and selLength properties of editField1. Selecting the text effectively scrolls the EditField.

In Detail

Note that before we select the topic headline, we first set editField1.selStart to len(editField1.text): why do we do that?

Look at what that instruction does: it moves the cursor to the end of the text. The next lines then move the cursor to topic headline. So why add add that extra move to the end?

The answer has to do with how REALbasic editField's scroll. When you move the cursor to some text that's scrolled off-screen, REALbasic automatically scrolls the text so the text at the cursor point visible. But that just means it can be anywhere within the viewable area, not necessarily centered or at the top of the field. By first moving the cursor to the end of the text, REALbasic has to scroll backward, stopping when it reaches the cursor position. That effectively puts the topic right at the top of the editField where it's easily seen!

Note that newer versions of REALbasic include a scrollposition property for editFields so you can scroll without moving the cursor, but I wrote RBU Pyramid before those features were available to me, so this is the old-fashioned work around. For extra credit, try rewriting this to use the newer methods.

Remembering the Window Position

We've got our basic help window, but we're not quite done. As usual, we want an elegant Macintosh program. That means a program that will do nice things like remember the location and size of the help window. That's extra work for us, but that's the Macintosh Way.

As you'll see, this is very similar to what we did for HighScoreDialog. First, let's add two new global properties. Open globalsModule and add two properties (Edit menu, "New Property"): gHelpOpen as boolean and gHelpPos as string. The first will tell us if the help window is open or not, the second will contain details about the window's size and position.

Like we did for HighScoreDialog, we need to prevent our window from saving window size changes when we resize it programmatically (we only want it to save when the user changes the window), so let's add a new boolean property to InstructionsWindow: opening as boolean.

Now add the following to the window's Open event:

  
gHelpOpen = true
opening = true
if countFields(gHelpPos, comma) = 4 then
self.left = val(nthField(gHelpPos, comma, 1))
self.top = val(nthField(gHelpPos, comma, 2))
self.width = val(nthField(gHelpPos, comma, 3))
self.height = val(nthField(gHelpPos, comma, 4))
end if // countFields = 4
opening = false

Here we're just setting our global gHelpOpen property to true, meaning that the help window is open. We set opening to true so we can adjust the window position ourselves, and then we change the window's size and position based on the info in gHelpPos. (The if countFields line is just an error check to make sure that gHelpPos contains good data.)

Next, go to the Close event and put in this:

  
gHelpOpen = false
saveWindowInfo

The first line simply says the window is closed, and the second calls a saveWindowInfo method (which we'll write in a moment).

Now go to both the Resized and Moved events and put in this code in each:

  
if not opening then
saveWindowInfo
end if

This just says if we're not in the process of opening, save the new window size and position information.

Now let's write that saveWindowInfo method. Add a new method (Edit menu, "New Method") and name it saveWindowInfo. Here's the code for it:

  
gHelpPos = str(self.left) + comma
gHelpPos = gHelpPos + str(self.top) + comma
gHelpPos = gHelpPos + str(self.width) + comma
gHelpPos = gHelpPos + str(self.height)

As you can see, this just saves the current window details into the string, separated by commas.

Now that we've got this info saved, we need to remember for the next launch of the program. That means adding the info to our preference file. That's easy, of course. Let's open PrefModule and find the loadPrefs routine. Insert in this case statement (where doesn't matter, as long as it's before the end select line):

  
case "helpwindow"
gHelpPos = gDirectives(i).text

This will simply load gHelpPos with whatever help window info has been saved. The reverse, saving gHelpPos, is just as easy. Open savePrefs and put in this line before t.close:

  
// Save Instructions Window position/size
t.writeline "#helpwindow " + gHelpPos

Excellent! Note that unlike the score window, we don't bother saving the open/closed state of the help window.

We're almost finished: we just need a way to make the help window appear.

Adding An Instructions Menu

Open the menu item within your project window. Go to the Options menu and click on the blank item at the bottom of the menu list. Type a hyphen ("-") and press Return. That inserts a separator line. Do that again, but type in "Instructions..." and press Return. While selecting the Instructions menu item, go to the Properties Palette and find the CommandKey property and type in an I. Your menu should look like this:

Now open up the App class (within your project window): we need to do something with that menu command.

Within the Events section, go to EnableMenuItems and insert this code (it doesn't matter where):

  
if gHelpOpen then
OptionsInstructions.text = "Hide Instructions"
else
OptionsInstructions.text = "Show Instructions"
end if
OptionsInstructions.enabled = true

This will enable (ungray) the Instructions menu command. We change the text of the menu item depending on whether the help window is open or closed.

But what about the command itself? Easy: go to the Edit menu and choose "New Menu Handler" and in the dialog that pops up, select "OptionsInstructions" from the popup menu and click okay. That should insert an OptionsInstructions handler within the Menu Handlers section. Go there and put in this code:

  
if gHelpOpen then
instructionsWindow.close
else
instructionsWindow.show
end if

Simple: if the help window is open, it is closed, and vice versa.

Guess what? We're done! At least for this week. RBU Pyramid is starting to look very polished, but there's still a few little things to go. We'll finish them up in a few more lessons. For now, give the program and whirl and check out the new help window!

If you would like the complete REALbasic project file for this week's tutorial (including resources), you may download it here.

Next Week

We tackle the big bear: adding an undo system after the fact.

REALbasic News

REAL Software announced this week the release of REALbasic 4.0. This new version adds some new features including significantly revamped ListBoxes and EditFields and an improved IDE. You can find about all about the latest release at realbasic.com. I'll have a column about these new features in a few weeks.

REALbasic is available for US$149.95 for the Standard Edition and US$349.95 for the Professional Edition packages, direct from REAL Software. REAL Software offers academic and volume discounts as well as license-only options. Upgrades to REALbasic 4 range in price from $29.95 to $89.95 for owners of REALbasic 3.5.

Letters

Our letter this week is from Remek, who has a -- gasp -- math question!

hi

i am new to REAlbasic programming but i have learned enough about it as to solve the mathematical problem that i had

i won't bore you with the reasons for this but this is the problem

i'm try into figure out the product of all the numbers from 1 to 1000

this is a relativly simple task by using

  
i=1
for x = 1 to 1000
x= x * i
next

this i know

however the problem that i have encountered is that if i go higher then 32, as in for x = 1 to 32, i get an answer of 0

could you help?

Okay, my palms are breaking out in a sweat here. Just the word "math" terrifies me and gives me nightmare flashbacks of high school algebra, but I'll do my best to figure this out and explain it in a way that even I can understand.

First, your code above seems to have some troubles. If I'm understanding what you're wanting -- the product of all numbers between 1 and 1000 -- that's a problem that can be represented like this (with the asterix standing for multiply):

  
result = 1 * 2 * 3 * 4 * 5 * 6...

For that to work, you don't want to use x for both the for-next counter and as the result of your calculation as the calculation will change the value of x. You want something more like this:

  
dim i as integer
dim x as integer

listBox1.deleteAllRows
x = 1
for i = 1 to 100
x = x * i
listBox1.addRow str(i)
listBox1.cell(listBox1.lastIndex, 1) = format(x, "###,###")
next // x

Note that I've taken the liberty of rewriting your routine so it displays the results of each pass into a listBox. When you run this, you get this:

As you can see, the calculation does start to go awry after the 33rd iteration. Why is that?

To answer that, we need to learn a little about how computers handle numbers. You've probably heard of the binary system: ones and zeroes. Computers are always binary: everything is either a one or a zero. So how does a computer work with an 8 if there is no eight?

Simple: it uses several ones and zeroes to represent an 8. So the number 8 in binary is "1000". Think about it, with a single digit, the highest binary possible is 1 (zero or one are the only two combinations). But with two digits (or bits, since bit means binary digit), you've got four combinations (00, 01, 10, and 11). That's the numbers 0-3!

If you keep expanding this concept, more digits equals bigger numbers. Three digits eight combinations, four sixteen, five thirty-two. Wait a second, there's a pattern developing...

Digits Combinations
1 2
2 4
3 8
4 16
5 32
6 64
7 128
8 256

See how it works? The number of combinations is the number two raised to the power of the number of digits. So two to the power of 4 is sixteen, two to the power of eight is 256, and two to the power of 16 is 65536, and so on!

Now there's a very simple relationship between the number of digits required to represent a number in binary and the maximum value that can be expressed by those digits. Remember how one bit (1 or 0) can only represent two values? Since zero is one of those values, the maximum number than can be expressed with a one bit number is one. Thus the max number that can be expressed is always one less than the number of combinations!

So with three binary digits (bits), there are eight combinations and the maximum number that can be expressed is 7 (8 minus 1). For eight bits, the max number is 255. For sixteen, it's 65,535.

What this means is that numbers, on a computer, are not infinite: there's always a maximum value that can be stored!

You've heard terms like 8-bit, 16-bit, 32-bit, etc.? Well, that just represents the largest number that particular computer can work with!

Since Macs are 32-bit computers, and REALbasic's number datatype is a 32-bit number, that's all we've got to work with. So how big a number can a 32-bit number represent? Let's see... two to the 32nd power is 4,294,967,296, so the biggest number is 4,294,967,295!

However, there's another issue. Since numbers can also be negative, REALbasic limits integers to a range of plus/minus 2,147,483,648. That means the smallest number you can use is -2,147,483,648 and the largest is +2,147,483,648.

Interestingly, 2,147,483,648 just happens to be exactly half of 4,294,967,296. Do you think that's a coincidence?

Now take a quick look back at the screenshot of our program. See how it ran out of steam (numbers) around the 33 point? See how that number is very near our maximum number? Yup, once the number got to big, it became a zero!

But what if you want to use bigger numbers? Is there nothing you can do?

Well, REALbasic also has the double data type. I know we're used to using them to represent real numbers (also known as fractions or numbers with a decimal point). But doubles are 64-bit numbers. That's quite a bit bigger. According to REALbasic's documentation, a double can be any value between 2.2250738585072012 e-308 and 1.7976931348623157 e+308. Whew! Those are some big numbers!

So if we make the simple change of making x a double instead of an integer:

  
dim i as integer
dim x as double

listBox1.deleteAllRows
x = 1
for i = 1 to 100
x = x * i
listBox1.addRow str(i)
listBox1.cell(listBox1.lastIndex, 1) = format(x, "###,###")
next // x

And we run the program again, we get the following:

Wow, that's much bigger!

However, you can see we still run out of numbers before we even get to sixty iterations! Our little math problem just generates too huge numbers!

Oh well. That's the best we can do, so we'll have to live with that. (There are some clever ways around that, but they're beyond the scope of my extremely limited mathematical skills.)

I hope this little lesson on numbers has helped people: it certainly took me a few years to understand it all! I remember in high school, when I started programming, having to actually go to ask a teacher why a game I was programming wouldn't store the large sums of money I required. It turned out that since my BASIC used 16-bit numbers, 65,535 was the largest value I could use. I didn't really understand the teacher's explanation until years later. But since my game dealt with dollar values in the millions (it was a financial strategy game like Monopoly but with more money), my workaround was to simply make $1,000 the smallest amount of money and add three zeros to whatever number was stored (so 65,535 was really $65.5 million). That worked!

BTW, if you'd like the source for the program above, you can get it here.


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