Matt Neuburg is the author of REALbasic: The Definitive Guide, and a member of the editorial board of REALbasic Developer. This article was originally published in REALbasic Developer Issue 1.5 (April/May 2003).
And now, a report from the past about the future. REALbasic 5 was in an early beta stage as this issue of REALbasic Developer was being assembled, but by the time you read this, it will have been released. This new version of REALbasic represents a major overhaul, and promises to be the most worthwhile upgrade REAL Software has produced in a long time. So even though by now (when you read this) things may look a bit different from how they do now (when I’m writing this), now (whatever that means) seems the most appropriate moment to introduce you to REALbasic 5. Sheesh, all this time-travelling is confusing!
As reported elsewhere in this issue, the compiler has been replaced, allowing for the first time extensive improvements to the REALbasic language itself. These improvements range from small but welcome changes to major gains on the object-oriented programming front.
A number of syntactical constructs have been rationalized. In a For … Next clause, the counter need no longer be an integer, and a new construct, For Each … In, makes it easy to iterate over an array. Single-statement conditions can now appear as a single line of code. The rules on the placement of Dim and Const statements are somewhat relaxed. Function results can now be ignored. A subroutine’s parameters can now be assigned to. The limitations on what sorts of values can be passed as ByRef parameters have been lifted somewhat. Subroutine parameters can be optional.
Here’s some silly code just to illustrate some of these differences; things you couldn’t have done in REALbasic 4.5 are commented:
Some New Syntax Constructs:
Sub testNewFeatures() dim ii, a(5) as integer, i as double for i = 0.1 to 5.1 // NEW ii = i if ii > 2 then callinc(ii,5) // NEW a(i) = ii next dim s as string // NEW for each i in a // NEW s = s + " " + str(i) next msgbox s End Sub Sub callinc(byref orig as integer, what as integer) what = 100 // NEW call inc(orig) // NEW (triple play!) End Sub Function inc(byref orig as integer, what as integer = 1) As boolean orig = orig + what End Function
The “triple play” line ignores a function result, omits a parameter, and passes a ByRef parameter as ByRef, all in just three words of previously illegal code.
Even more wonderful is that you, the programmer, can now change the language yourself, as a benefit of improvements to the object model. You can redefine arithmetic and comparison operators so that they are meaningful for your own classes. You can define conversion operators, which are implicitly applied wherever you use an instance of your class where some other type is expected. And you can write a function call as what looks like a property assignment; if you combine this with a private property (now called “protected”), you’ve got linguistically enforced accessors (getters and setters).
For example, imagine you’ve a class called StringWrapper with a protected property s as string. Here, this class won’t have any purpose except to illustrate the new linguistic features. We’ll give StringWrapper a constructor, a setter, and overrides to the plus operator, the comparison operators, and string coercion:
Methods of StringWrapper:
Sub stringWrapper(s as string) self.s = s End Sub Sub itsString(s as string) self.s = s End Sub Function operator_add(sw as stringWrapper) As stringWrapper return new stringWrapper(self.s + sw.s) End Function Function operator_compare(sw as stringWrapper) As integer return strcomp(self.s, sw.s, 0) End Function Function operator_convert() As string return self.s End Function
Watch what we can now do with a StringWrapper; in essence, we can treat it like a string:
dim sw1, sw2 as stringWrapper sw1 = new stringWrapper("hi") sw2 = new stringWrapper("deho") msgbox sw1 + sw2 // "hideho" sw2.itsString = "hi" dim v as variant v = (sw1 = sw2) msgbox v // "true" v = (sw1 is sw2) msgbox v // "false"
In the first three lines nothing new happens; we use our constructor to create two StringWrapper instances, one whose s is "hi" and the other whose s is "deho". Now, however, we proceed to do something absolutely amazing. We apparently add the two StringWrappers together, something that in the past would have been absolutely impossible with objects; the Operator_Add method interprets this action for us, and generates a new StringWrapper whose s value is the concatenation of the s values of the two operands. In the same statement, we now blithely hand this new StringWrapper over to MsgBox, which causes it to be implicitly coerced to a string; that’s possible thanks to the Operator_Convert method.
In the next line we assign a value to a StringWrapper's itsString property. But a StringWrapper has no itsString property! Behind the scenes, ItsString is a function, which is called so as to set the StringWrapper’s s property — which we could not have done directly, because it's protected.
Finally, we compare our two StringWrappers to each other, in two different ways. First we use the equals operator. In the past this would have failed, because two object comparands were equal only if they were references to one and the same object. But our Operator_Compare method reinterprets this to mean the same thing as comparing the s properties of the two objects, and the comparison succeeds. The question now is, if you can't use the equals operator to learn whether two objects are the same object, how do you do it? The answer is to use the new “is” operator, as demonstrated by the last lines of our code.
There are other linguistic improvements; here are the most notable. Arrays can now function as stacks, thanks to the new Pop command. A method that overrides an inherited method can call the inherited implementation by referring to the superclass as Super, rather than having to use the superclass’s name. Pragmas can be turned on and off within a handler. And a handler can now end with a Finally clause, which will be executed no matter what (unless the handler returns first) — whether or not an exception is encountered, and whether or not that exception is handled — which provides a convenient way to clean up.
Sockets have been a major thorn in REALbasic’s side since the beginning, usable for some purposes but generally undependable. Now sockets have been rewritten from the ground up to make them far more reliable and powerful, with accompanying changes to the class architecture.
The basic socket class is now SocketCore, which is abstract, existing only to encapsulate functionality common to its two subclasses, TCPSocket and UDPSocket. UDPSockets are for UDP communications, and utilize a new Datagram class to package their messages. The old Socket class now refers to TCPSocket. REALbasic Pro users also get SSLSocket, a TCPSocket subclass for secure client-side communications.
Socket events and methods are much as before, except that TCPSocket now has a sendProgress event that lets you track how fast your data is being sent; it also lets you abort the send in the middle. Buffered writing, as described on p. 619 of my book, may now be completely irrelevant, and the old trick of polling the socket to speed up communications should no longer be necessary (though I presume it would still be the way to implement a synchronous socket operation).
A surprising bonus is that REAL has provided some TCPSocket subclasses that know some client protocols. HTTPSocket functions as an http client, and can both Get and Post. POP3Socket and SMTPSocket can receive and send email, respectively, and employ the new EmailMessage, EmailHeaders, and EmailAttachment classes. (There are also analogous subclasses of SSLSocket: HTTPSecureSocket, POP3SecureSocket, and SMTPSecureSocket.)
Because of their independent, asynchronous nature, memory management of sockets has always been a dubious business. Now the rules are rationalized: a socket’s reference count is incremented when you tell it to Connect or Listen, and decremented when the connection closes. This means you don’t have to maintain a socket as a property; you can create a socket as a local variable, set it going, and forget about it. The socket will persist as long as it’s connected, and, if there are no more references to it, will go out of existence afterwards.
To see how simple all this can make your TCP communications, let’s fetch a web page. Suppose our window has two EditFields: EditField1 will hold the URL for the page we want to fetch, EditField2 will hold its HTML when it arrives. We make an HTTPSocket subclass called HTTPDemo, with just this code in its PageReceived event handler:
window1.editfield2.text = content
Now a button in our window runs this code:
new httpdemo.get editfield1.text
We enter the desired URL and press the button; there is a short pause, and presto! The page has been downloaded and its HTML appears.
The most sweeping and important socket change is the implementation of a new ServerSocket class, available to REALbasic Pro customers only. Page 628 of my book explains the problem: the old Socket class, when told to Listen, could not be made to handle multiple simultaneous incoming connections, so you couldn’t build even an elementary web server that could serve a page containing images. “What’s needed,” I say there, “is a listener-dispatcher architecture, where a single listener on a single port receives incoming connection requests and hands each one off to a new Socket as they arrive.”
That’s exactly how ServerSocket behaves. You make a ServerSocket subclass instance; it has to persist, so it will be a property, probably of your Application instance. Now you specify a Port, and tell it to Listen on that port. The ServerSocket maintains a pool of TCPSockets. When a connection comes in, the ServerSocket hands it off to one of these TCPSockets, and when that connection closes the ServerSocket releases that TCPSocket from the pool and replaces it with a new one.
Your role in all this is to help the ServerSocket populate its pool. Whenever the ServerSocket instance needs a TCPSocket to help keep the pool up to size, it gets an AddSocket event. Now it’s up to your ServerSocket subclass code to supply a TCPSocket instance (or a subclass, of course) that will actually do the communicating. The previously mentioned memory management considerations apply: the TCPSocket instance you supply can be a local variable generated right in the AddSocket event handler, and will persist as long as it’s connected.
Menu items and menus are now completely dynamic. This means you can add and remove menus — not just menu items, but the words that appear in the menubar itself — while your application runs, something that was previously possible only through a Declare. Also, dynamic menu items are no longer dependent upon menu item arrays, though you can still use them if you want.
All this is the result of a major overhaul in how menus work. Everything is now a MenuItem — the menubar, the menus in it, and the menu items within them. Thus, your application has a folder-like hierarchy of MenuItems, and you can access and manipulate these through new methods and properties that look like a cross between an array and a FolderItem hierarchy. For example, I’ll add a completely new menu:
dim m, mNew as menuitem m = self.menubar mNew = new menuitem mNew.text = "Pep Boys" mNew.name = "PepBoys" m.append mNew mNew = new menuitem mNew.name = "PepBoysManny" mNew.text = "Manny" mNew.autoEnable = true m.child("PepBoys").append mNew
The only problem is that in the IDE you can’t make a menu handler for PepBoysManny unless a menu item by that name exists, so either your new MenuItem must be a MenuItem subclass, so that you can implement its Action event handler, or you have to fool the IDE by creating the menu item manually (in the menubar editor), making the menu handler, and deleting the menu item.
Did you notice that a MenuItem now has an AutoEnable property? If this is set to true, there’s no need for an EnableMenuItems event handler to enable the menu item; it will enable itself, provided there’s a menu handler for it. If you want more control over enablement of a dynamically created menu item, you can use the Enable event handler if it’s a MenuItem subclass, or you can maintain a reference to the dynamically created menu item in a property somewhere, but you can’t refer to the menu item directly by name, because at compile time no such menu item exists; your code must work its way down the hierarchy, see if the menu item exists, and enable it as desired, like so:
Enablement of a Dynamic Menu Item:
dim m as menuitem m = self.menubar m = m.child("PepBoys") if m <> nil then m = m.child("PepBoysManny") if m <> nil then m.enabled = enableManny end end
Did you also notice that I referred to the menubar as a property? It can be a property of a window or of the application. The reason is that your project can now have more than one menubar! Changing the current menubar can be a quick way to destroy and create large numbers of menus and menu items on the fly. On Mac, the current menubar is the one belonging to the frontmost window, or to the application if the frontmost window has none. On Windows, this also means that each window of a non-MDI application can have its own menus.
There’s a change in how REALbasic draws — one that could cause your applications to misbehave. Figure 1 and Figure 2 were drawn with the same code, in REALbasic 4.5 and 5.0 respectively. The first square is wider and taller than the second square in the first drawing, the same size as the second square in the second drawing.
|Figure 1: DrawRect in RB 4.5|
|Figure 2: DrawRect in RB 5|
What’s happened is that the implementation of DrawRect has changed. This is probably a good thing in the long run; you can see me describing the behavior of DrawRect on p. 305 of my book, and pointing out that, along with DrawPolygon, it’s different from all the other drawing commands. After this change, only DrawPolygon remains an odd man out.
The Application instance, and individual windows, now have a DockItem property which has, in turn, a Graphics property you can draw into to change your application’s icon, or the window’s minimzed icon, in the Dock. So, for example, to make your application’s Dock icon a black circle, you’d say:
Changing the Dock Icon
dim g as graphics g = app.dockitem.graphics g.fillOval 0,0,g.width,g.height
To change it back to the default, you’d tell the DockItem to ResetIcon and possibly to UpdateNow.
String functions need no longer be called globally; instead, they can be accessed as methods of a string reference, as if strings were objects. New byte-wise ReplaceB and ReplaceAllB functions round out the repertoire, and EditFields now accept cross-platform line-ending characters as paragraph breaks. New functions let you convert strings to Base64 and quoted-printable, as well as doing MD5 hashing.
Text encodings have long been a major problem. Things were bad enough when REALbasic went cross-platform, but they were exacerbated two years ago when it started running on Mac OS X, where strings are Unicode and can arrive in various encodings. Things got particularly silly when it turned out that the way to learn a string’s encoding, regardless of its language, was with the inappropriately named and unreliable GuessJapaneseEncoding. In REALbasic 5, the programming interface is much improved, with convenient methods to let you obtain an encoding by name, ask for a string’s encoding, convert its encoding, or specify an encoding if you know the encoding but REALbasic doesn’t, all without the inconvenience and overhead of passing through a TextEncoding instance.
Various new window features on Mac OS X were highly speculative at the time of writing this article — meaning that they were pretty much busted in practice, though great ideas in theory. However, a great idea is still worth describing, especially since these features might work by the time REALbasic 5 gets into your hands.
There are two new window types. The Metal Window provides the brushed-metal look so familiar from iTunes. Observe that you may have to turn on the window’s Composite property in order to prevent cosmetic glitches in a metal window. The other new window type is the Drawer. The approach here is to designate a window as a Drawer in the IDE and then show it in code, specifying its parent, with ShowWithin.
Mac OS X windows can now have toolbars, those collapsible areas across the top containing clickable icons, as in the Finder. REALbasic’s implementation of these is rather ingenious: you drag one or more ToolbarItem controls into your window, but you don’t try to position them properly or make them the right size, and you don’t resize the window to allow room for the toolbar; rather, REALbasic observes the presence of the ToolbarItems and generates the toolbar for you at runtime. Toolbar items in the running application will presumably be clickable but I don’t know what, if anything, will happen about making them draggable.
There is now, as you probably know unless you’ve been vacationing on Alpha Centauri, the exciting promise of the REALbasic IDE running natively on Windows, so that development and debugging will be possible directly on that platform. Details on this effort, as it comes to fruition, must be left to other times and other articles. Here, I’ll just summarize the broadest Windows-related changes in REALbasic itself.
Cross-platform compatibility is improved, with controls and functionality on Windows working better than ever. The all-important Registry, where Windows keeps its system configuration information and application preferences, is now accessible through the new RegistryItem class. Inter-application communication by way of COM, the Windows equivalent of Apple events, is enabled through the OLEObject class (though a REALbasic application cannot itself be a COM server). And the OLEContainer class lets your application embed ActiveX controls; these are packets of interface-plus-functionality, rather like a REALbasic control plug-in, and are common coin among Windows users and programmers. An even simpler way to add an ActiveX object to your project, in the Windows version of the IDE, is through an item in the File menu. Right-clicking on an ActiveX control lets you see its type library.
Finally, here’s a quick run-down on some of the many other small changes you can expect to see; this list, while not exhaustive, should suggest the breadth of this update.
An Application instance is now required, and all new projects include one. Control properties and methods that make no sense for a control without a visible interface have been moved to RectControl. Timers can now be started over with the Reset method, and turned on and off with the Enabled property. Real files can be easily copied into a virtual folder. FolderItems now return error codes after operations, and their paths can now be relative. It is now much easier to get a random number in a given range. Bitwise math operations are moved off to a separate class, and include shifts. Information can now be stored in and retrieved from the user’s keychain.
In the IDE, breakpoints can be created or deleted with a mouse-click. At compile time, a list of all compile errors can appear, instead of your having to learn about and fix them one at a time. You can choose whether or not to break on exceptions. When debugging, the application no longer runs “in the IDE”; it’s completely separate, so if it crashes, REALbasic itself is untouched.