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 057

SimplePaint: Part IV

Our children's painting program is progressing nicely, but there's one very important feature we need to implement to make it worth using: the ability to save pictures.

Saving Pictures

Since the design philosophy behind SimplePaint is that there should be no confusing "file save" dialogs, we've got to come up with a simple way to save pictures. The method I chose is for each picture to be saved with a default name to a default folder. The tradeoff is the user doesn't get to choose the name of the picture or where it is saved, but the benefit is simplicity and no "file save" dialog.

Saving pictures isn't difficult, but it will require several pieces of code. First, open your SimplePaint project and open the Code Editor of paintWindow (double-click on the open paintWindow). In the KeyDown event, add this code (it doesn't especially matter where as long as it's not within another if-then segment):

  
if key = "S" then
savePict
end if

All this does is call the method savePict if the user presses the "S" key. (Note that though we're using an uppercase "S" for key detection, because REALbasic isn't case sensitive by default, this will work for either "s" or "S.")

Now we need to create that method. On the Edit menu, choose "New Method" and call it savePict (leave the parameters and return fields blank).

The way our save routine will work is this: we'll store saved pictures in same folder as the SimplePaint application. We'll just number the pictures so each will have a unique name.

But sharp readers will notice a problem with that system. It works fine during a single session. However, once a few pictures have been saved and the program is relaunched, the counting begins at one, and there's already a "Picture 1" on the disk! Our new picture would overwrite the old one, which would be incredibly rude.

So somehow we must make sure we don't save the new picture on top of an old one. The trick is to find the highest numbered picture on the disk. Adding one to that number will give us our new picture number. Here's how that routine works.

Create a new method called getNumber and have it return an integer. Here's what the code looks like:

  
dim i as integer
dim f as folderItem

i = 0
do
i = i + 1
f = getFolderItem("").child("Picture " + str(i))
loop until not f.exists

return i

As you can see, this is remarkably simple. All we do is increment a value, i, by one, and keep checking to see if a file named "Picture " + i exists or not. If it does, we keep adding one to i. If "Picture i" doesn't exist, we know it's okay to save a new picture with that name, so we return the value of i!

Now go back to savePict and put in this code:

  
dim f as folderItem

f = getFolderItem("").child("Picture " + str(getNumber))
if f <> nil then
f.saveAsPicture(p)
msgBox "'" + f.name + "' Saved!"
else
beep
end if // f = nil

This creates a new folderItem within our default folder (getFolderItem("") returns the folder of your application). The folderItem's name is "Picture " plus a number -- the one calculated by our getNumber method.

To save the picture, we use our folderItem's saveAsPicture method, passing it p, our drawing. That's it!

Getting Rid of a Dialog Box

You may notice that our save routine displays a dialog box. It's just a message box, confirming the save, but for young users, that can be confusing. They won't notice the modal dialog and won't be able to figure out why they can't draw any more.

So how do we tell the user the picture's saved without a dialog? A mere beep could indicate an error, not success.

There's a simple solution right within REALbasic's own online Help!

Bring up REALbasic's online help and type "speech" as the search term. Here's what comes up (you'll probably have to scroll a bit to see this code as it's not at the top of the help window):

Now create a new method like this:

To do speech, I just dragged in the code from RB's help window (code with a dotted border can be dragged into the Code Editor to add it) and modified it.

This code uses a declare statement to access the Mac OS's built-in speech routines (which are not accessible via a standard REALbasic command). Once we've declared a routine, we can call it like any other REALbasic routine. REALbasic just passes our command on to the operating system.

Because system commands are not available for all platforms, we therefore must check the platform before making the OS call. We do that with the conditional compilation commands, #if-#endif, and REALbasic's special "target boolean" values, targetMacOS and targetCarbon, which return true if we're running Mac OS or Carbon (Mac OS X). (Note that because Carbon apps can run under Mac OS, there's no way to tell if your app is really running Mac OS X -- all you can tell is if you're running under Carbon.)

Unfortunately, the online help code doesn't work under Mac OS X (it causes the application to quit). At first I wondered if speech was supported under Carbon, but a quick check on Apple's developer's website showed that the SpeakString command is supported under Carbon. To make it work, I therefore set up a targetCarbon version of the function replacing "SpeechLib" with "CarbonLib" -- and it worked!

(Note that not all Carbon commands are as easily translated. Sometimes the Carbon commands have new names or different parameters from their Mac OS equivalents, which can get complicated to translate. If you don't know what you're doing with declares, it's best not to mess with them unless you enjoy watching your computer crash.)

Here's the finished routine:

  
dim i as integer

#if targetMacOS then
declare function SpeakString lib "SpeechLib" (SpeakString as pstring) as integer
#endif

#if targetCarbon then
declare function SpeakString lib "CarbonLib" (SpeakString as pstring) as integer
#endif

#if targetMacOS then
i = SpeakString(theString)
return true
#endif

return false

If the speech is successful (available), we return true, otherwise false (which would be the case if the user didn't have an operating system capable of speech).

In Detail

You'll notice I first check for targetMacOS then targetCarbon. Why?

Well, since Carbon is only available on Macs, an app running under Carbon will always return true to the targetMacOS question. That means the function will initially be defined via "SpeechLib" -- which will crash a Carbon app. But the second statement checks to see if we're running Carbon. If we are, the function is redefined as calling "CarbonLib" -- and that works.

The final statement only bothers to check for targetMacOS since whatever Mac OS we're running will work with the SpeakString command. (The exception is 68K Mac OS, which requires a different declare, but since REALbasic no longer supports 68K compiling, I didn't bother with that, though you could support it if you're using an older version REALbasic.)

Now we just need to make some minor changes to our savePict method so instead of displaying a dialog we speak a message to the user:

  
dim f as folderItem

f = getFolderItem("").child("Picture " + str(getNumber))
if f <> nil then
f.saveAsPicture(p)
if speakTheString(f.name + " Saved!") then
else
msgBox "'" + f.name + "' Saved!"
end if
else
beep
end if // f = nil

Note that if speech isn't installed, the routine will display the confirmation dialog as before.

Erasing the Screen

We've now given SimplePaint the ability to save pictures, but what happens after that? Usually the child will want to start a new picture -- we need a way to let the child erase the screen.

Go to the KeyDown event of paintWindow and insert in this:

  
if key = chr(8) then
// Erase all
p.graphics.clearRect(0, 0, self.width, self.height)
self.refresh
end if

This just checks to see if the user pressed the Delete key (ASCII 8) and if so, clears the picture with the clearRect method and refreshes the screen. Easy!

Adding an Eraser

It's usually easier to tell children to simply use white to erase, but we'll go ahead and add an erase keyboard command. All it does it change the current color to white.

In paintWindow's KeyDown event add this:

  
// Eraser
if key = "E" then
changeColor(rgb(255, 255, 255)) // white
end if

Now pressing "E" switches the color to white so you can erase.

That's it for today. Next time we'll add some more features like changing the mouse cursor and some sounds.

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

Next Week

More SimplePaint polishing.

Letters

Our letter this week is from David in Arizona. He writes:

Hello Marc,

Thank you for making your REALBasic tutorials available, I have enjoyed reading them.

I have a question that I hope that you have the time to answer.

I have set up a RB program that has three windows. Window1 is an introductory page, followed by Window2 where data is entered, and Window3 where some calculations using the data from Window2 are performed and the results shown.

I would like to have any formulas or data manipulation in Window3 recognize & use the data that was entered while Window2 was showing.

If in window2 a numeric value is entered into a textfield, and then the value of the textfield is assigned to a variable, how can I use this variable in Window3?

When I try to do this, the variable's value is always reset to zero in Window3.

I appreciate your time, help and information.

Thanks!

Peace,

David

Hi David! The simplest solution for your situation is to just to refer to the properties of the other windows as properties of other windows. That sounds way more complicated than it is. Just put the window name in front of the property name, separated by a period.

For example, to delete all the rows of a listBox on window2, put:

 window2.listBox.deleteAllRows 

You can refer to any object or property of another window simply by putting the window's name in front of it (followed by a period). These are all valid:

  
// editFieldB is on window1
editFieldB.text = window2.editField1.text

// This is the same as the previous line
window1.editFieldB.text = window2.editField1.text

// total is a number property
total = val(window2.editField1.text) * val(window2.editField3.text)

The other approach is to use global variables -- add properties to a module and they're accessible by all windows and methods. But that could be more complicated since you'd have to store the stuff from the one window into global properties, then access the globals from the other window. Just accessing another window's data directly is simpler.


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