| |||||||||||||||||||||||||||||
|
| |||||||||||||||||||||||||||||
Print This Article REALbasic University: Column 083
OOP University: Part SevenLast 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 ObjectsYou'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:
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:
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:
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:
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 ParametersYou 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:
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.
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.
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 WeekOkay, we didn't quite get to object actions yet, but we will next week, I promise! NewsThe 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 Designing for Aqua Interview with TouchCAD Creator Postmortem Much More
LettersThis week Nicolas Jullien from France writes with a "$10" question:
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:
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:
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 See the REALbasic University Archives
REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.
| |||||||||||||||||||||||||||||