PP Documents
Volume Number: 12
Issue Number: 4
Column Tag: Getting Started
PowerPlant and Documents
By Dave Mark, MacTech Magazine Regular Contributing Author
Note: Source code files accompanying article are located on MacTech CD-ROM or
source code disks.
Last month, we implemented a window containing a scrolling TextEdit view. Though
you could type text in a window, there was no way of saving the text out as a file or of
loading a text file into a text editing window. What’s missing here is the concept of a
document. PowerPlant features a sophisticated set of document-handling classes that
allow you to quickly tie a file to a window. Each file/window pairing is known as a
document. Although PowerPlant does support a more complex model (multiple
windows tied to a single file, for example), the most common document approach ties a
single file to a single window, all controlled by the LSingleDoc class.
This month, we’re going to examine a sample application that ships with
CodeWarrior. The application is called DocDemo, and it implements a simple TextEdit
window that supports most standard document behaviour. That is, you can Save a
document, Save As... under a new name, Open an existing document, and Print a
document. DocDemo supports Apple events and is recordable. You can find DocDemo on
the CodeWarrior CD. On CodeWarrior 8, it is in a folder called Document Demo.
My original goal for this month’s column was to add the LSingleDoc class to last
month’s program, allowing you to save your text window as a file and open an existing
file in a text window. But when I saw DocDemo, I changed my mind. DocDemo does
everything I wanted to do, but also adds printing and great Apple event support. This is
definitely a great learning program. Cool!
Getting Started with DocDemo
Before you go any further, you might want to find a copy of DocDemo on your
CodeWarrior disk (or download it from whatever site you go to to pick up the Getting
Started source code each month [such as ftp://ftp.mactech.com/pub/src/ - man]).
Figure 1 shows the project window for the PowerPC version of DocDemo. Take a look
at the files grouped under the name Application. The file CDocDemoApp.cp contains
main() and the member functions that make up our main application class.
CDocDemoApp is derived from LDocApplication, which is derived from
LApplication. If you plan on building an application that supports multiple
documents, CDocDemoApp.cp makes a great starting point.
(Remember, a class that starts with “L” belongs to PowerPlant. All the classes
that you add to your PowerPlant programs will start with “C”.)
Figure 1. The DocDemoPPC.µ project window
Where CDocDemoApp.cp implements the application, the file CTextDoc.cp
implements a single text-based document. CTextDoc is derived from LSingleDoc,
which is derived from LDocument.
CDirtyText.cp implements the actual text stream, the text stored in memory
that appears in one of the DocDemo windows. The word “dirty” refers to the state of a
text stream, either changed since the last save (dirty) or not.
Later in the column we’ll step through each of these three source code files,
pointing out the highlights. The remaining three files in the Application group are
resource files. Notice that the Constructor resources are stored separately from the
rest of the resources. As mentioned in a previous column, this is a good idea. When
you double-click on the file DocDemo.rsrc, your favorite resource editor is launched
(in this case, the creator code of DocDemo.rsrc is set to launch Resorcerer - feel free
to change it to use ResEdit if that’s your preference). When you double-click on the
file DocDemo.PPob, Constructor is launched.
Figure 2. The main window for DocDemo.PPob
Take some time to look through the resource files, especially the Constructor
file. If you haven’t seen it yet, this would be an excellent time to check out
Constructor 2.1, the version that shipped on CW8. It has a cool new look and a great
new menu editor. Yup, Constructor now does menus! Figure 2 shows the main
Constructor window for DocDemo.PPob. Very nice...
The main view of interest here is the one named “Text Window”. If you
double-click on it, you’ll see something very similar to the scrolling text pane we
created last month, with an LScroller enclosing an LTextEdit pane. Figure 3 shows the
pane info window for the LTextEdit pane. Notice that the Pane ID is set to the four byte
value 'Text' and that the Text ID checkbox is checked. When the Text ID checkbox is
checked, the value in the Pane ID field is represented as a sequence of 4 characters
(like a resource type) instead of as a long integer.
Notice also that the Class ID is set to the four byte value 'Dtxt'. This value will
come into play when we register our classes in the source code. We’ll pass this value
as a parameter to URegistrar::RegisterClass() in the CDocDemoApp constructor.
You’ll see this in a bit, when we explore the project source code.
Figure 3. The LTextEdit pane info window for the pane
Running DocDemo
Take some time to put DocDemo through its paces. The important things to test are the
ability to save and reopen text files. If you have AppleScript installed on your machine
(and you should), run the Script Editor, click the Record button, then run DocDemo.
While recording, create a new window, type some text, then save the document to disk.
Go back to the Script Editor and click the Stop button. Figure 4 shows the results when
I did this on my Mac. The line saying “make new document” was a result of selecting
New from the File menu. The line following it was a result of doing a Save. Notice
that the action of typing my text was not recorded. Once you’ve been through the code,
see if you can figure out why this action wasn’t captured and how to make this happen.
Figure 4. A script recorded using Script Editor
while running DocDemoPPC
Here’s another interesting thing to try. You may experience an unrecoverable
crash with this one, so be sure you save any open docs first! In DocDemo, open a new
window, type some text, then save the document on your hard drive. Without closing
the window, select Open from the File menu. Select the file you just saved (it’s
already open, right?) from the SFGetFile list, then sit back and watch some exception
handling kick in. Remember that option-Apple-escape forces a quit; you might end up
using it. You might want to repeat this experiment with the debugger turned on.
OK, enough play. Let’s check out the source code.
CDocDemoApp.cp
You’ll notice a strong similarity between CDocDemoApp and last month’s
PPTextEdit.cp file. While PPTextEdit’s application class, CPPStarterApp, was
derived from LApplication, CDocDemoApp is derived from LDocApplication
(LDocApplication is derived from LApplication). Here are some important things
to look at as you go through the source in CDocDemoApp.cp:
CDocDemoApp overrides OpenDocument(), MakeNewDocument(),
ChooseDocument(), ObeyCommand(), and FindCommandStatus().
ObeyCommand() and FindCommandStatus() should be familiar to you from
previous columns. In DocDemo, they don’t do much, since we haven’t added any
commands specific to DocDemo.
OpenDocument() gets called on an 'odoc' Apple event and opens the file specified
in the incoming FSSpec. In a truly recordable application, OpenDocument()
should never be called directly. When you want to do an open, you should post an
'odoc' event, which will cause OpenDocument() to get called. Remember, Apple
events are what get recorded. Without the Apple event, the process of opening a
document won’t be recorded. To post an 'odoc' Apple event, call
LDocApplication::SendAEOpenDoc().
MakeNewDocument() gets called in response to a kAECreateElement Apple event.
When you select New fom the File menu, PowerPlant sends itself a
kAECreateElement Apple event. Again, this is vital if you want your app to be
recordable. To see this in action, take a look in CDocDemoApp::ObeyCommand().
Notice that cmd_New is not handled and that this causes a call of
LDocApplication::ObeyCommand(). LDocApplication::ObeyCommand()
sends the kAECreateElement Apple event in response to cmd_New.
MakeNewDocument() uses new to create a new CTextDoc.
• Take a look at the ChooseDocument() source code:
void
CDocDemoApp::ChooseDocument()
{
StandardFileReply macFileReply;
SFTypeList typeList;
UDesktop::Deactivate();
typeList[0] = 'TEXT';
::StandardGetFile(nil, 1, typeList, &macFileReply);
UDesktop::Activate();
if (macFileReply.sfGood) {
OpenDocument(&macFileReply.sfFile);