Issue 63 * January 15 2009

Document Centric Applications
Handling multiple document windows in an application

by Steven Checkley

Document WindowsIf your application needs to be built around the creation of documents (which I think is called document instantiation by my professional developer friends) and you would like the user to be able to edit more than one at a time, you might find yourself scratching your head quite a lot working out how to do this.

I was at first but as things usually turn out in Rev, creating lots of document windows is actually very easy to do.

This article looks into one way of handling multiple document windows in an application. I've also built a demonstration stack that contains the example code looked at here.


The basic set up
Getting going is pretty simple. What we're going to do is set up a template document stack and then use the clone command to create multiple instances of it. Each time you use the clone command, the stack is created in memory only, so no stacks or other files are saved to the desktop.

In the example stacks, I've set up my document stack as a substack. It's important to set its destroystack property to true (take a look in the stack properties window). The reason for this is that when the user closes this cloned window, we want to dispose of it for good. If not, you'll quickly find that the application browser contains lots of stacks that we've finished with, even if memory problems aren't something we need to deal with these days.

You'll also find that whilst you're designing and developing your application, cloned windows will ask you whether you want to save them when you close them. This this won't happen in the built application, so you don't need to worry about handling this. However, make sure that when you're testing the cloning aspect of the application in the IDE, any subsequent edits are made in the original stack and not the disposable clone. Can't tell you how many times I've made a tweak here and there and then realised they were to the wrong stack!

Creating instances
To create an instance of a window, we will use the clone command. It is likely that your application will need to create an instance whenever the user creates a new document or opens a saved file. Consequently, it would make sense to create a command that can be used in both cases.

So here's the script that will create an instance:

command createNewInstance pWindowName
   local tTempID
   -- The default document window is a substack called
   -- "Untitled". We need to clone this.
   
   clone stack "Untitled"
   
   -- We'll generate an instance ID and rename the stack
   -- "Copy of Untitled" to it.
   -- Just in case we generate two the same, the repeat loop
   -- will add additional characters.
   
   repeat
      repeat 4
         put numtochar(random(26)+64) after tTempID
      end repeat
      if tTempID is not in gStackInstances then exit repeat
      put numtochar(random(26)+64) after tTempID
   end repeat
   
   set the name of stack "Copy of Untitled" to tTempID
   put tTempID into line (number of lines of gStackInstances)+1 \
   of gStackInstances
   
   -- If no window name is supplied for this stack then we will
   -- assume it's new.
   
   if pWindowName is empty then
      set title of stack tTempID to "Untitled" && \
      gStackUntitledCounter
      add 1 to gStackUntitledCounter
   else
      set the title of stack tTempID to pWindowName
   end if
end createNewInstance 

Easy, eh?

When you clone a stack, Rev will automatically name it as "Copy of..." plus whatever the source stack is called. Consequently, we should rename the stack to something else so things don't get confusing later on. Hence this is why I create a unique ID and change the name of the stack to it.

Personally, I like to keep a list of the windows that have been created but it's not strictly necessary. That said, by doing this, I have a list of stacks that I can interrogate (e.g. ask each stack for a piece of information, if I'm building up a list) or fire commands at them (e.g. telling them that they must save their data during an autosave cycle).

Finally, there's the titling of the window to consider. If it's being created for the purposes of loading data into it, it is likely that we know what name to give it, rather than call it "Untitled". In the code, if the paramter pWindowName is empty, we'll name the window "Untitled" and add a counter to it as convention dictates. However, if it contains a value, the window will be titled with it.

By the way, note the difference between the name of a stack and the title of a stack. These are self explanatory but I find that it can be pretty easy to mix the two up in a rapid coding session!

using the command
All that's needed to execute our command is simply to add createNewInstance to an event. This is exactly what you'll find in the 'Create a New Document' button.

You'll find rather more code in the "Open a Document" button because this button reads in the data from the file, creates the instance and deposits the data accordingly. To derive the name of the file, it also does something clever with itemDelimiter.

So what's the best way of storing data in these many document windows?

Well, you could just save the entire cloned stack! This would obviously save any data in fields and store the status of any checkboxes and radio button options but if you update your application's document window, your user's saved data will be trapped in the old window not be able to take advantage of any improvements.

Therefore, it's probably fair to say that the user's data will need to be written to a file and then read back in again at a later date. Whilst they're working on it, it needs to be held in a temporary state somehow. So then, if you have lots of data to keep live, you're then presented with another problem: how to store each unique bunch of it all in memory.

I'd say that global variables are pretty much out of the question because globals are generally specified and named in code. I guess that you could create unique, instance related globals by concatenating chunks of text using the do command and indeed, this was a method I employed once back in the old HyperCard days (when things weren't so easy to do and I needed very fast access to big chunks of lots of data on a Mac Plus). However, this method is staggeringly ineffecient and creates complicated and bizarre looking code to boot.

An alternative is to create a special card in the document stack to dump data into whilst we're working on it. Again, this is a method I've used before in my old HyperCard days. Unfortunately, the dimensions of your stack can make cramming in many fields tight and cumbersome, especially if you have lots of data to store. And if you forget what you've called a field, you'll find yourself to-ing and fro-ing back and forth to the card.

The easist way to store live data is to store it as one or many custom properties of the stack. That way, any script in the document stack or indeed, any script in a library can access the data nice and quickly. If you take a peek at the example stacks, you can see that this is method I've used to store the path to the saved data file, thus avoiding having to ask the user where to save the file each time.
conclusion

Hopefully this article has provided some insight into how to create an application with multiple documents. Although the sample stacks here are quite basic, feel free to base any projects of yours around them.

 

Main Menu What's New

Get The Megabundle