Monday, March 25, 2013

Observer Pattern

Did you know Real Studio includes several "design pattern" example projects, including: Decorator, Factory, Interpreter, LazyInitialization and Singleton?

Recently, someone on the mailing list asked how to do the Observer pattern using Real Studio. Here is a quick example to demonstrate it.

What is the Observer pattern?

You use the observer pattern when you have an object that needs to notify one or more other objects of specific changes. This is the definition from wikipedia:


"The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods."

For this example, I have a window with three radio buttons to set a color (Red, Green, Blue). When a radio button is clicked, the "observer" windows are notified of the change and change their background color to match what was clicked.

You can download the project from here:


Implementation Details
This design pattern is implemented using two interfaces: SubjectInterface and ObserverInterface.

The MainWindow implements SubjectInterface. It is responsible for tracking all the objects that want to observer (get notified of ) the change in color. This is done using an array of ObserverInterface objects.

The ObserverWindow implements ObserverInterface. This has one method (Notify) to implement which changes the background color of the window to the selected color.

When you click the "Add Observer" button, a new ObserverWindow is created and added to the array of observer objects.

When the color is changed by clicking a RadioButton, the code loops through all the observer objects in the array and call their Notify method.

The Remove All Observers button removes the open windows from the observer objects array. They will no longer get notified when the color changes.

This example project will also be included with Real Studio starting with 2013r1.

Monday, March 18, 2013

Firefox 19 & Real Studio Web Applications

A bug report was filed with us recently that troubled me a bit, regarding WebPopupMenus not working in the recently released Firefox 19. The bug is that when the user clicks on a WebPopupMenu, they are not able to select anything using the mouse.

I've done some research into this and it appears to be a bug in Firefox 19. Here's what we know:
  • It only manifests in Firefox 19 on OS X
  • It is related to the use of the CSS3 transform-3d property (which we use for other things)
Unfortunately, this is a particularly nasty bug because users have no way to knowing it's coming and when most other sites work perfectly fine, they usually assume that the bug is with your app.

It's worth pointing out that:
  • Firefox 18 does not exhibit this behavior
  • Firefox 20 (now in beta) does not exhibit this behavior (Due April 1, 2013)
The pain regarding this bug should only last a couple more weeks as Firefox 20 should run perfectly fine on any system that supports Firefox 19. 

For those of you that want to be able to detect Firefox 19 in your apps (in case you want to warn your users), you can put this code in the Session.Open event handler:

If Session.Platform = WebSession.PlatformType.Macintosh and _
    Session.Browser = WebSession.BrowserType.Firefox and _
    CDbl(NthField(Session.BrowserVersion,".",1)) = 19 then

  MsgBox("Please upgrade your browser to Firefox 20")
End If

Bridges

I rather admire bridges - both the real ones that cross chasms, channels & all other manner of things, but also those in programming.



Why?

They both make it possible to solve difficult problems with very elegant solutions, but both can take some time to construct.  And, both are extremely useful.  For instance, in the IDE we use a bridge pattern to deal with Preferences on a platform specific basis - except for the small chunk of code that is the Preferences public API - nothing else in the IDE is even aware that there are underlying differences in how they are handled.  And therein lies the beauty of the bridge pattern.

So, how do you construct such a thing?

For something like Preferences it is reasonably straight forward. I know people would love to peek at the IDE code.  I'm not going to publish that, but I will give you a sample that is similar.

I've used a Module as a namespace to contain everything AND so I can actually have private classes that can be hidden away - they can be opened & reviewed, but they have no public API so the rest of your code can only use the one public class.

First things first - the public API - this is the module itself. (I'm going to trim some bits out to keep things short but you'll get the idea)

Module CrossPlatformPrefs

  Private Sub InitMPrefs()

    #if TargetWin32
      mPrefsImpl = new WindowsPrefs
    #elseIf TargetMacOS
      mPrefsImpl = new OSXPrefs
    #elseif TargetLinux
      mPrefsImpl = new LinuxPrefs
    #endif
 
  End Sub

  Function ReadPreferenceString(name as String) As String

    // may raise a "Preference not found exception"
 
    if mPrefsImpl is nil then
      InitMPrefs
    end if
 
    if mPrefsImpl is nil then
      raise new PrefsNotUsableException
    end if
 
    return mPrefsImpl.ReadPreferenceString( name )
  End Function

  Function ReadPreferenceStringArray(name as String) As String()

    // may raise a "Preference not found exception"
 
    if mPrefsImpl is nil then
      InitMPrefs
    end if
 
    if mPrefsImpl is nil then
      raise new PrefsNotUsableException
    end if
 
    return mPrefsImpl.ReadPreferenceStringArray( name )
  End Function

  Sub WritePreferenceString(name as string, value as String)  

    // may raise a "Preference not usable exception" - can't initialize
    // may raise a "Preference not found exception" - no such preference
 
    if mPrefsImpl is nil then
      InitMPrefs
    end if
 
    if mPrefsImpl is nil then
      raise new PrefsNotUsableException
    end if
 
    mPrefsImpl.WritePreferenceString( name, value )
 
  End Sub

  Sub WritePreferenceStringArray(name as string, value() as String)

    // may raise a "Preference not usable exception" - can't initialize
    // may raise a "Preference not found exception" - no such preference
 
    if mPrefsImpl is nil then
      InitMPrefs
    end if
 
    if mPrefsImpl is nil then
      raise new PrefsNotUsableException
    end if
 
    mPrefsImpl.WritePreferenceStringArray( name, value )
 
  End Sub

  Private mPrefsImpl As PrefsReaderWriter


End Module



You'll notice that the Module itself mostly just instantiates the right preferences implementation then passes any other method calls along to that implementor. And thats the beauty of the bridge pattern.

You can have many different implementations with one public API.

Each of the implementors implements a common interface - PrefsReaderWriter.

Protected Interface PrefsReaderWriter
  Function ReadPreferenceString(name as String) As String

  Function ReadPreferenceStringArray(name as String) As String()


  Sub WritePreferenceString(name as string, value as string)


  Sub WritePreferenceStringArray(name as string, value() as string)  

End Interface


(again for brevity I've not listed EVERY method just a handful for illustrative purposes)


This interface is IN the module and Private so only classes in the module can use it and nothing outside the module can use it. We want ALL access to Prefs to go through the methods in the module.

One implementor for OS X might look like (with the guts removed again to keep this article short)



Private Class OSXPrefs
Implements PrefsReaderWriter
  Function ReadPreferenceString(name as String) As String
  End Function

  Function ReadPreferenceStringArray(name as String) As string()       
  End Function

  Sub WritePreferenceString(name as string, value as String)
  End Sub

  Sub WritePreferenceStringArray(name as string, value() as String)   
  End Sub

End Class



Windows & Linux implementations would be similar but their internals would vary from the OS X one.  On Windows you'd read & write to the Registry. On Linux perhaps a configuration xml list or key value pair in a file that is prepended with a period so its hidden in most UI's and directory listings (this is very common). And, on OS X you'd used code to write to the property list - perhaps code from MacOSLib to read and write CFPreferences as Apple suggests.

Oh, and yes, I even included a couple of exceptions so you can tell what happened 



Private Class PrefsNotUsableException
Inherits RuntimeException

Private Class PrefsNotFoundException
Inherits RuntimeException

And all of this is private inside the module so the ONLY public API to the Preferences is through the API the module provides.

The bridge is a very handy pattern and really useful especially when you need to have a common API cross platform but need different implementations.

Friday, March 15, 2013

Quickly Access the Documentation Wiki using LaunchBar

If you use OS X, you may have heard of LaunchBar, a system utility that makes it easy to launch applications, search web sites and have multiple copy and paste buffers.

If you use LaunchBar, you might find this documentation tip useful. I'm going to show you how to add your own index so that you can use LaunchBar to quickly search the Real Software Documentation wiki.

First, here is an example of how you can use LaunchBar to search Wikipedia:

If you haven't already, open LaunchBar by pressing Command+Space. In the drop-down window type "wiki" and then a space. Now you can type a search term and press return to open a browser at wikipedia with the topic you specified displayed.




You can easily do the same thing for the Real Studio Documentation Wiki.

First, open the LaunchBar window and click the gear icon. In the menu select Index and then Show Index. This opens the LaunchBar Index window.

Towards the bottom of the sidebar you will see an entry that says "Search Templates (UTF-8)". Click on that to see all the search templates. Click the "Add.." button to add your own template.

Give the new template the name rswiki and paste this in the Details:

http://docs.realsoftware.com/index.php?&search=*&go=Go

Save your changes and close the window.

Now bring up LaunchBar again with command+space. Type "rswiki" and a space. Enter your search term (such as "WebListBox") and press return. The documentation wiki will appear in your default web browser at the WebListBox page.