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 083

OOP University: Part Seven

Last time we learned the difference between a instance of an object and a class of an object, as well as the small but vital difference between and instance and a reference to an instance. Today we continue that lesson by exploring more of the dirty details regarding REALbasic's use of objects. Specifically, how to copy them.

Copying Objects

You'll remember that last time we learned why you can't duplicate an object by simply assigning it to another variable. This does not work:

  
dim d1, d2 as date

d1 = new date
d2 = new date
d1 = d2

So how do you copy one object to another if you can't just use the equals sign? Well, it's good old fashioned grunt work, my friend. You must copy each property one by one. For example:

  
anObject2.theContents = anObject1.theContents

If there are ten properties, that's ten equals statements. And vice versa to go the other direction!

Yes, it's yucky, but you can simplify things a little by adding methods to an object so it knows how to copy itself.

For example, I've expanded the little demo I showed last time to include a real copy method (download it here):

Notice that when you click the "real copy" button, the contents of one object are copied to the other one, not simply the reference pointer as when you use the "fake copy" button. When you use the fake copy, the one instance is destroyed as nothing refers to it any more. But with the real copy, both objects still exist. They have the same contents, but they are still two independent objects. To see this, do a real copy, then change the text in the text fields and update the objects. You'll see they each update independently.

Even better, examine how we accomplish this copying. Within myClass I've added a copy method. It gives external code the ability to duplicate a myClass object (and this is the key) without having to know anything about the object.

Here's the copy method:

  
sub copy(toObject as myClass)
if toObject <> nil then
toObject.name = me.name
toObject.theAmount = me.theAmount
toObject.aProperty = me.aProperty
end if
end sub

Notice this takes a parameter of a preinitialized object of myClass type (that means toObject has been "newed" and is not nil: it points to an instance of the object) and then it copies its properties to that object.

Our code to actually start the copying is simple: when the button is pushed we say:

  
anObject1.copy(anObject2)

That's it! The outer object (in this case anObject1) copies itself to the passed object (anObject2).

See how flexible this becomes? By embedding the copy command within the object itself (the object knows how to copy itself), we separate the dirty details of the copying from our main code. If we add a new property to myClass, or change an existing property of it, we simply have to update the copy method in one place. None of our other code needs to change at all!

This is called encapsulation, and we'll learn more about it next time.

Passing Objects as Parameters

You might have noticed that in our copy method we passed an object to it. Or did we?

Ah! Sharp-eyed readers quickly realized that we didn't actually pass an object: we passed a reference to an object! (That's why we had to test it for nil before we used it.)

Now when you normally pass a variable to a method, you end up with a new copy of the variable within the routine. When that routine ends, that copy is destroyed. The original variable is never modified by the routine.

Look at the following example:

  
function addOneByValue(n as integer) As integer
return n + 1
end function

This takes a number and adds one to it. But n, as referred to by this method, never is changed.

On the other hand, if we use the byref command, a copy of the variable isn't created, but a reference to the original is used, so anything that happens to n within the method actually happens to our original variable.

  
sub addOneByRef(byref n as integer)
n = n + 1
end sub

These two methods of passing info as parameters are known as passing by value and passing by reference. REALbasic's default is passing by value: if you want to pass by reference, you must specify that in the routine's definition.

In Detail

There are advantages and disadvantages to either method of modifying variables (via byref or having a result returned by a function). The main disadvantage of a function is that only one result can be returned.

Let's say you've got a program that let's a user type in some particular text in an editfield. Your program requires that the user put in this text correctly (no extra spaces, case is correct, etc.). The format of the text is crucial and must be correct.

Now you could write a function that takes the text, checks it for errors, and then returns a true or false corresponding to the text's validity. But let's say you also wanted your routine to modify the passed text and correct minor errors. You may not be able to correct all the errors, but you want to correct the simple ones for the user. But you still need to know if the text was valid or not so you'll know if the text needs to be presented back to the user for final approval or not. How would you do this?

Simple: use both methods! Pass the text to the routine as byref, and return the boolean result of success or failure. Something like this:

  
function checkText(byref theText as string) as boolean

// This is legal because we're allowed to modify
// a byref parameter.
theText = trim(theText)

// Do your analysis here.
...

// return false if appropriate (i.e. validation failed)

return true
end function

This allows you to modify theText, while still returning a true/false result of the function. It's the best of both worlds. (You might notice that REALbasic's parseDate function works just like this!)

But what about our object? We didn't specify that as byref, yet our copy method was able to modify the original object!

Exactly! You have just learned the key about passing objects as parameters: objects are always passed as byref!

In a sense, we're back to the original object1 = object2 situation: when REALbasic "copies" the object for the method, it's a fake copy, copying only the reference (the pointer) to the original object, not the object itself. So any modifications made via the passed reference actually happen to the original object, just like when you used the fake copy button and ended up with two pointers to the same object.

This is not a problem as long as you're aware of this behavior. If you're used to the normal by value parameter passing, you might get unexpected behavior when you start passing objects around. I know in some of my early projects I was totally confused by this.

If you would like the REALbasic project file for the above "addOne" example, you may download it here.

Next Week

Okay, we didn't quite get to object actions yet, but we will next week, I promise!

News

The fourth issue of REALbasic Developer is at the printers and will be available in early February. There's still time to subscribe -- order your subscription today and receive RBD 1.4 as your first issue!

As always, back issues are available if you don't want to miss an issue.

Here's a preview of what's inside the February/March issue.

Marketing Your Software
Kicking things off is a cover story by Electric Butterfly's Dave Wooldridge (of UniHelp fame) on software marketing. If you've ever been intimidated by the various tasks of selling your software, here's the article you need to read. Dave explains about establishing brand identity, getting noticed by search engines, writing press releases, advertising, handling payments, and more.

Designing for Aqua
Next, if you've been struggling to make your program fit in with the Mac OS X "Aqua" appearance, Mike Benonis has some handy tips for you. We've also got a handy "Aqua Checklist" compiled by Doug Grinbergs of mactester.com.

Interview with TouchCAD Creator
If you're interested in graphics, you'll be fascinated by the interview with Claes Lundström, a boat designer who wrote the amazing, full-featured $795 TouchCAD in REALbasic.

Postmortem
For this issue's Postmortem, Thomas Reed details the challenging in rewriting an existing program (Coffee Break Pro) in REALbasic.

Much More
And of course all the regular columns are back: Matt Neuburg's column is on hashing, Thomas Cunningham's has a great primer on graphics from the beginner's perspective (which compliments Thomas Reed's Adanced Techniques' graphics focus), and if you're interested in a challenging subject, be sure to read Didier's "Beyond the Limits" which explains how to do steganography (that's the process of hiding secret messages inside a picture) in REALbasic.

Subscribe today!

Letters

This week Nicolas Jullien from France writes with a "$10" question:

Have you got an idea to drag something into a specified row of a listbox. In other terms how can we intercept the draging movment before releasing the mouse?

Optional question (or remark) when I drag onto a target that accept the drag object, there are no surrounding (I don't know the exact term) that tell you that you are effectively draging into it. But if you drag first outside the window, then the surrounding occurs! How to make it work without moving this way?

Nicolas Jullien

Well, Nicolas, you've unfortunately run into one of the limitations of REALbasic. While RB makes drag-and-drop support easy since it does so much for you, the drawback is that you have little control over what's going on during a drag event. For instance, while a drag is taking place, your program receives no mouse events: no mouseEnter, mouseMove, etc. (To see this in my example program, watch how the mouse coordinate status field doesn't update during a drag.) This limitation makes what you are wanting to do difficult.

However, it's not impossible, though there are limitations to the technique.

What we've got to do is use math to figure out which row the user dropped the object on. Let's assume that we want to replace the text of the current row with new dropped text. Here's my example program at work:

To do this, we need to know two things: the height of each row in pixels and the y (vertical) coordinate of where the drop occurred. (To figure out which cell was dropped on in a multicolumn listbox is beyond the scope of my example. For that you'd need to figure out the x coordinate and know the width of each cell.)

The height of each row is something REALbasic knows about, but getting that is difficult, since it's dynamic. Listboxes have a defaultRowHeight property, but it's usually set to -1, meaning that RB will adjust the height as needed for whatever font sizes your listbox uses. In our case, we need a fixed size, so the first thing we do is set this to a specific size (I chose 12 pixels). If your program changes the font size of the listbox, you'd need to be sure to change this value also.

The second piece of info -- the y coordinate of the drop -- we can get through the control's mouseY function. Note that we must convert this to local coordinates as what is returned are window coordinates.

So here's what I did. First I created a new subclass of listBox which I called myLBClass. Then I added a new event, droppedObject, which looks like this:

Then I went to the DropObject event of myLBClass and added this code:

  
dim y, row as integer

// Window coordinates
y = me.mouseY

// Convert to local
y = y - me.top

row = floor(y \ me.defaultRowHeight)
if me.hasHeading then
row = row - 1
end if

// Call new event
droppedObject obj, row

What this does it figure out which row we've dropped something onto and pass that info to our new event, droppedObject. Since droppedObject has two parameters, obj and the row, that gives us what we need to know.

Next, we create an instance of myLBClass in a window. Within that instance, we go to the droppedObject event and add code to do whatever we want to do when something is dropped on a particular row. We could insert the drop at that point, or we could replace the existing row text with new text (my example shows both).

The code for that looks like this:

  
dim s as string

if obj.TextAvailable then
s = "Line " + str(row) + ": " + obj.text

// If you wanted, you could replace the
// text on the indicated row with the new text.
if row > -1 and row < me.listCount then
me.cell(row, 0) = s
else
beep
end if

// Or, Insert dropped text at the passed row
'me.insertRow(row, s)

end if

Note that in my example I'm only dealing with text drops, and if you'd rather have the insert behavior, you'd need to comment out the replace behavior and uncomment the insert line.

By creating this as class, you can easily add this behavior to any listBox in any project simply by adding this class and making your listBox of type myLBClass.

You should also be aware that various settings to a listBox can mess things up. In my example, for instance, I've tried to compensate for the use of listBox headers. But without knowing the exact height of the header, my calculations could be slightly off. To get this to work perfectly you'd need to do a lot of testing and compensate for various situations (i.e. my example may not work for hierarchal listBoxes).

myLBClass basically does what you want. However, there's one serious caveat: because REALbasic allows no events to happen during a drag, you have no way to draw anything to indicate to the user that a "row drop" is about to take place. The user is dropping blind. That is not a good user-interface technique. Unfortunately, there is no way around this without implementing your own drag-and-drop system (not using REALbasic's), and even that has problems since REALbasic automatically allows dragging of text from editFields and there's no way to turn that off.

What most RB programmers do is just have drops do some sort of default behavior and do that consistently. For example, you could have drops always create a new row at the end of the listBox. Or, if a user has a row selected, the drop could be inserted right after the current row (that's what my Z-Write word processor does when you drag text to the Section List). The main point is to do it consistently so the user knows what to expect.

As to the second part of your question, about the drag highlighting, this is another problem with REALbasic. I know no workaround, though there could be one I'm not aware of (feel free to write me if you know of one).

If you want my DragExample project file, you can grab 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