TweetFollow Us on Twitter

September 94 - BUILDING AN OPENDOC PART HANDLER

BUILDING AN OPENDOC PART HANDLER

KURT PIERSOL

[IMAGE 006-016_Piersol_html1.GIF]

OpenDoc, Apple's compound-document architecture, brings users a new, more powerful metaphor for working with documents. Writing code to support OpenDoc is a lot like writing a normal application. This article gives an overview of what's involved in writing OpenDoc code and presents a simple working example.

OpenDoc provides a new way to write application code for the Macintosh and a number of other desktop platforms. By following the OpenDoc guidelines, you can produce applications that share files, windows, and interface elements seamlessly. The process of writing an OpenDoc application, which we call apart handler , is much like writing any Macintosh application. There are differences as well, of course, and this article will help you understand them.

OpenDoc applications are designed to allow code from several sources to cooperate in producingcompound documents , documents that can embed almost any kind of content inside them. Each piece of content in the document (eachpart ) includes its own part handler, the code that's used to edit and view it. To achieve this, OpenDoc part handlers must cooperate in a number of ways. They must sort out how events are passed, where data is stored on the disk, and where drawing is allowed to occur on windows or printed pages.

This article starts with a brief overview of OpenDoc and then talks about implementing a simple part handler. It will show you the absolute basics, much as TESample does for TextEdit in the Macintosh Toolbox. You'll learn about a simple example of building a part handler, included on this issue's CD: a clock that can handle two different display modes, digital and analog. The clock updates itself every second and allows the user to select the display mode from a menu.

A quick caveat: The sample code provided on the CD is from the alpha version of OpenDoc, but by the time you read this, a beta version should be available. When you begin implementing your own part handler, you may find that some details of the API have changed; however, the overall structure will be the same. The sample clock, for instance, is specific to C++ and the alpha version of OpenDoc. The final version of OpenDoc will be based on IBM's System Object Model (SOM), which will allow part handlers to be written in a variety of languages, both object-oriented and procedural. Similarly, theXMP prefix on OpenDoc class names (you'll see a lot of them in this article) will be changed toOD beginning with the beta release.

This is perhaps a good time to mention a bit more about SOM. This technology is the basic mechanism that OpenDoc part handlers use to communicate with one another. SOM solves many hard problems associated with using object-oriented languages, including those of subclassing across language boundaries, altering base classes under dynamic linking, and long-term maintenance of object-oriented APIs.

OVERVIEW OF OPENDOC

Before getting into the specifics of OpenDoc and how to write part handlers, we'll talk about some of the basic services you'll see in OpenDoc and where your own code fits into the OpenDoc architecture.

PART HANDLERS
Part handlers are what provide OpenDoc with its ability to handle different kinds of content in a single document. You, the developer community, will write the various part handlers that plug into OpenDoc.

Part handlers are a lot like existing applications. They handle events, draw and print, and read and store data onto disk. Every part handler provides a series of entry points that allow OpenDoc to request any of these actions from the part handler. In addition, the API has a number of "bookkeeping calls," which allow OpenDoc to provide undo services and notify part handlers when their environment has changed.

Overall, there are about 50 calls in the OpenDoc part API that a part needs to implement. This is a lot, but it actually maps fairly closely with the number of things you'd have to do to write any Macintosh application. In addition, you can ignore many of these calls in many cases. For instance, if you don't allow embedding of other parts within your part, there are about ten calls that you can safely ignore. If you don't update your display asynchronously, but simply wait for update calls, there are additional calls that you can ignore. In many typical cases, this means that you can build a part handler very quickly from existing code.

Part handlers are packaged as shared libraries in the Macintosh version of OpenDoc. This won't always be the case on other OpenDoc platforms, but you can count on the API being the same on all platforms. The alpha version of OpenDoc uses the Apple Shared Library Manager to dynamically link your part handler into OpenDoc, while the beta version will use SOM. These versions will have different linker behavior but will essentially require the same basic packaging of your code: a shared library.

In either case, you'll find that OpenDoc is an object-oriented API. That means you'll be talking to OpenDoc objects, and your part handler will itself be an OpenDoc object (or set of objects). This doesn't mean that your code has to be built from the ground up in C++, though. SOM will provide interfaces to many languages, including C.

Because part handlers are themselves objects, we often refer to them as "part objects" or "parts" in conversation. In fact, what the user would call a "part" in a document is really the combination of some persistent data stored in the document file and a set of objects that OpenDoc uses to display and manipulate the stored data. OpenDoc chooses appropriate part handlers based on the type of data stored in the document.

RUNTIME OBJECTS
As we describe how to write a part handler, we'll mention some runtime objects that interact with your code. In OpenDoc, these objects can be located at run time using the session object (XMPSession), to which your part object will be given a pointer when it's initialized. The session object is very important because it's your link to the rest of the OpenDoc objects that are running in the document.

There's a whole list of objects that the session object makes available. Of these, only three will be important for the purposes of this article: the arbitrator, the dispatcher, and the undo stack.

  • The arbitrator is an object of class XMPArbitrator. The arbitrator for a session is the place where part handlers register their ownership of certain resources. The menu bar, the keystroke stream, and the current selection are all examples of resources that the arbitrator tracks.
  • The dispatcher is the object that dispatches events to the various part handlers. It's an object of class XMPDispatcher. It's used in our example as a way to register for background time.
  • The undo stack is an object of class XMPUndo that allows OpenDoc to support multilevel undo across part handler boundaries.

Each of these objects will be discussed as it's encountered.

A RUNNING START

To give you a running start, we've built a small object-oriented framework for parts that implements the direct interface to OpenDoc. This framework is a precursor to the new part handler framework that Apple is building, and is included here simply as sample code. Our sample clock uses this framework. The good thing about the framework is that it clearly separates the work that any part handler must do to be OpenDoc compliant from the specific work performed in putting up a clock.

The framework divides the work of a part into three objects: a frame object, a facet object, and a part object. OpenDoc itself doesn't require that you create anything but a part object, but for the sake of clarity the framework divides the labor among several smaller objects. For easy reference, here's a list of the classes we'll be discussing throughout the article and their corresponding source files, included on the CD:

    CPart       FWPart.h, FWPart.cpp
    CFrame      FWFrame.h, FWFrame.cpp
    CFacet      FWFacet.h, FWFacet.cpp
    CClockPart  ClockPar.h, ClockPar.cpp
    CClockFrame ClockFra.h, ClockFra.cpp
    CClockFacet ClockFac.h, ClockFac.cpp

The classes defined by the framework generally start with the letterC , hence the classes CPart, CFrame, and CFacet. These three parts are helper objects for three OpenDoc classes, XMPPart, XMPFrame, and XMPFacet. XMPPart objects are OpenDoc part handlers: you'll subclass XMPPart when writing your own. OpenDoc uses the frame and facet objects to help part handlers lay themselves out in a window. How these classes work together is probably the single most complex thing to understand in OpenDoc.

XMPPart, the class from which part handlers are derived, is simply the base class of every OpenDoc part handler. It's the class that actually handles the drawing, editing, and storage. Every part handler is an implementation of some subclass of XMPPart.

CPart, in the framework, is a class derived from XMPPart. It's just a default implementation of the basic XMPPart behavior. As such, CPart is a treasure trove of information about the correct way to "ignore" calls that aren't interesting because your part handler doesn't support embedding, update asynchronously, or use offscreen bitmaps.

Every part is embedded in another part, with the exception of theroot part, the top-level part in each compound document. When a part is embedded in another part, there's an object that's used to store information about the shape of the embedded part. This boundary between a container and an embedded part is aframe -- an instance of the class XMPFrame. Every frame has a single part displayed inside it. The container actually embeds the frame; it knows nothing about the part inside.

Any part can be displayed in several frames at the same time. This makes it easy for a part to be visible in several windows or to have several different presentations. For example, a charting part might want to have one frame displaying the chart and another allowing the data to be edited in a table.

A facet (an instance of an XMPFacet object) is a visible part of a frame. There can be many facets displaying within any given frame. This is a useful property, for instance, when a container wants to "split" windows. Both XMPFrames and XMPFacets have a field, partInfo, for storing information specific to the part being displayed. This is rather like a window refCon, a handy place to store information independent of the object itself. The CFrame and CFacet objects are designed to be plugged into the partInfo fields of their XMPFrame and XMPFacet counterparts. The containing part creates the XMPFrame and XMPFacet objects and then allows your part handler to initialize their partInfo fields. In the framework, the actual work of drawing the part on the screen is done in the CFacet object. The work of deciding what shape the embedded part will take is done in the CFrame object. As we describe the specific operations, we'll point out the class in which the code resides.

INITIALIZATION CODE

The first bit of code we'll consider is the initialization code for each part object. Each distinct part in a document gets an instance of the part object, so if there are seven little clocks running in different windows (or the same window, for that matter) there are seven instances of the clock part object. This means that you probably want to come up with a scheme to share any global data so that you aren't wasting space with many copies of it. Both the Apple Shared Library Manager and SOM support systemwide global storage, so this should be straightforward.

Resources are a special case. You'll want to be very polite about not permanently fiddling with the resource chain or making assumptions about where your resource file is in the chain. We suggest saving the previous head of the resource chain, setting your file to be the end of the chain, and using the single-level resource calls (such as Get1IndResource) to find the resources you're after. Since you'll probably want to share the resources among separate instances of your part object, it may be better to detach the resources you get and manage them yourself instead of counting on any particular application heap to have the correct resource map.

THE CONSTRUCTOR
The first step in initialization is theconstructor . You should never do anything that could possibly fail in a constructor. This pretty much limits you to operations like setting pointer variables to NULL, setting numeric variables to appropriate values, and making similar assignments from constants.

You can see a good example in ClockPar.cpp. The clock part simply sets up its fields with appropriate constant values.

XMPPART::INITPART
The next phase of initialization takes place in the InitPart method that every part object implements. The InitPart method is called by OpenDoc after the part object has been created, and here youcan attempt things that can fail. This is where you should attempt to allocate any extra memory you need for your part instance, get resources if you need them, and set up your persistent storage.

Let's examine how OpenDoc's storage system looks to a part handler. When your part object is created, the InitPart method is passed astorage unit object in which you can persistently store information. A storage unit is really just a list of namedproperties , each of which has one or more values . Each value is an entire stream, like an existing Macintosh file. You can do read, write, seek, insert, and delete operations on individual values.

Each value has a type, much like the type code associated with a Macintosh file. Every property in a storage unit can have one or more values, each with their own type code. Thus, you can store multiple representations of any property. You can make up any property names you like. One special property name, kXMPPropContents, is used by OpenDoc to determine which handler goes with which part at run time. Every part object should have a property named kXMPPropContents so that OpenDoc can determine what part handler to run.

In our sample, CClockPart has an Initialize method, which is called by CPart::InitPart. It sets up the menu bar for the clock and sets up a focus set for obtaining system resources from the arbitrator (more about this later). A good example of code to set up persistent storage can be found in the implementation of CPart. The framework calls its own method, called CheckAndAddProperties, to make sure that the storage unit is set up correctly.

DRAWING CODE

Now that your initialization code is in place, you'll want to make sure you can get your part to draw onscreen. OpenDoc will call your part with the Draw method and tell you which facet should be drawn.

Our sample, CClockPart, inherits some code from CPart that asks the CFacet object to do the drawing. Notice, though, that before it does this, CPart::Draw sets up the graphics port for drawing using the clipping information from the facet. This is very similar to the basic drawing model for the Macintosh, where you draw using the appropriate graphics port and clipping region. You can find the rest of the drawingcode in CClockFacet::Draw. This code consists of just the straightforward QuickDraw calls and attendant calculations needed to display either the digital or the analog clock face.

We use a utility class called CDrawInitiator to set up the drawing environment reliably. The constructor of this class does all the work of setting the graphics port's clipping region and origin. Later, the destructor restores the port to its previous state. This is a tricky bit of C++ coding that takes advantage of the object allocation behavior of stack-based objects in C++.

HANDLING LAYOUT
One of the features of CClockPart is that it presents a round shape when it's embedded. To do this, it uses the XMPFrame object's layout negotiation features. To understand this, you need to understand the notions of canvas, shape, and transform in OpenDoc.

  • A canvas is simply a drawing context. On the Macintosh it can be either a QuickDraw graphics port or a QuickDraw GX view port.
  • A shape is a way of describing an area of a canvas. OpenDoc supports describing shapes in terms of polygons, regions, or rectangles on the Macintosh.
  • A transform is a geometric transformation appropriate to the type of canvas in use. In QuickDraw, the only transformation available is an offset. In QuickDraw GX, the transformations are much more powerful, capable of scaling, rotating, and offsets, as well as some more interesting transformations such as skewing.

An OpenDoc frame has a set of shapes associated with it. One, called theframe shape , is how the container tells an embedded object how to lay itself out. Your part handler should use the frame shape to decide what to display and how to lay it out. When your part is finished laying itself out, it can optionally specify to the container exactly what part of the frame shape it plans to use. This shape is called theused shape of the frame. Finally, the embedded part can specify anactive shape , which is the subset of the frame shape that you want to use for determining whether you receive a mouse event. Often the used shape and the active shape are set to be the same shape. The container can take care of filling in any areas left untouched by the part handler.

For example, assume we have a clock part embedded in a word processor that does text wrapping. The word processor allows its embedded frames to be laid out as rectangles. When the clock is embedded, it uses the frame shape (the rectangle given by the word processor) to determine the size of the clock face. After determining the size, it sets its used and active shapes to match the shape of the round clock face. The word processor is now free to wrap text around the round clock face, and any clicks in the rectangular frame shape that aren't actually on the clock face are passed through to the word processor. Those clicks can be used to manipulate the text that's wrapped close to the clock face.

CClockPart negotiates to get the frame shape to match the clock's round face. This works but is not strictly necessary. Instead, it could simply set its used shape to match the round area of the clock. Either method will ensure that the container knows how to clip any underlying parts so that they don't draw in the clock's area.

A quick aside about shape negotiation: Negotiation is rather straightforward in OpenDoc, but knowledgeable programmers will notice that there is little support for constrained negotiation. This is not an oversight, but instead a fundamental design choice. It's up to the embedding part toconstrain layout according to its model of content. This means that constraint strategies like "Boxes & Glue" or "Springs & Wires" are the province of your part handler, not OpenDoc. You can implement any of a number of layout constraint schemes on top of OpenDoc, but every part handler may constrain layout within its own frame.

You can see what happens when the container reshapes the clock in the method CClockFrame::FrameShapeChanged. CClockFrame requests a round shape from the container and then invalidates the correct areas so that redrawing occurs. For most parts, the standard behavior is to redo the layout based on the new shape, update the active and used shapes of the frame, and then invalidate the proper areas.

EVENT HANDLING

Our next area of implementation is the event handling for the clock part. This is much like writing the event handling for any Macintosh application, with one difference: you don't poll the system for events by calling WaitNextEvent. Instead, when there's an event for your part handler OpenDoc calls your part's HandleEvent method.

XMPPART::HANDLEEVENT
The code inside HandleEvent is usually a switch statement, just as in applications today. There are some minor differences, which are nicely illustrated by the code that CClockPart inherits from CPart. This code effectively delegates the various events to code that can handle each event, using a switch statement. Notice the behavior for mouse-down events, which calls CFacet::HandleMouseDown, which in turn causes the frame to become active if it isn't already.

The notion of activation in OpenDoc is closely tied to the object discussed briefly earlier, the arbitrator. An object is "active" if it owns some of the foci in the arbitrator. Afocus is just a shared data structure or system service of some kind, such as the menu bar or keystroke stream.

When CClockFrame is told to activate, it requests a set of foci from the arbitrator. In this case, it wants the menus and selection focus. These two, with the addition of the keystroke stream, constitute the basic focus set that almost every part asks for when it wants to allow editing. You can find the code that sets up the focus set in CClockFrame::InitClockFrame. In CFrame::ActivateFrame, the focus set is requested.

Notice that the part is requesting the menu focus before it attempts to put up its menu bar. This is the basic rule to follow in all cases. If there's an arbitrator focus for the resource, you must request it and succeed in getting it before it's OK to use the resource. OpenDoc uses the arbitrator to carefully manage the sharing of data and system services, so it's very important to do the right thing and ask for foci whenever you need shared resources.

PUTTING UP A MENU BAR
One of the things that CClockPart does is to set up a menu bar. You can see the code for this in CClockPart::Initialize. The initialization code gets a reference to its menu bar object and then calls the AddMenu method of the menu bar object to add its menus. Finally, it registers command IDs to pass back when menu items are selected.

OpenDoc provides a menu bar object to help you set up menus and display them when your part has obtained the menu focus. The major reason for this object to exist at all is to support compatibility with Microsoft's proprietary OLE 2.0 document architecture. This object hides the complex menu- mixing behavior of OLE 2.0 behind a simple interface that works correctly in either an OpenDoc or an OLE 2.0 container.

Later, during execution of CFrame::FocusStateChanged, the menu bar object is asked to display itself. The actual code invoked is in CPart::InstallMenus, and basically just calls the Display method of the menu bar object.

GETTING IDLE TIME
You can get background time, delivered as idle events, on any of your frames. This is done by getting the dispatcher from the session object and registering particular frames for idle time.

You can see an example of this sort of registration in CClockPart::Initialize. In this case, the part itself is registering to receive idle events, but individual frames can also be registered. Once a frame or part has been registered for idle time, it will receive idle events in its HandleEvent method.

UNDO AND REDO
Although CClockPart is too simple to support undo, it's worthwhile to look at how you would go about adding undo support to your OpenDoc part handler. We've tried to make it as simple and unobtrusive as possible to do multilevel undo in OpenDoc.

The first step is to create code that tells OpenDoc you've done something that can be undone. You do this by getting the undo object, an instance of XMPUndo, from the session object. You then call the XMPUndo::AddActionToHistory method, which takes a hunk of data that you create to hold instructions about how to undo the latest action. OpenDoc never looks inside this hunk of bits; it merely stores it for later.

The code might look like this:

fSession->GetUndo()->AddActionToHistory(thisPart, myUndoData,
    kXMPSingleAction, myUndoString, myRedoString)

myUndoData is a pointer to the undo data, and myUndoString and myRedoString are strings to show in the Edit menu, to tell the user what action will be undone or redone.

Once the information is on the undo stack, simply calling the XMPUndo::Undo and XMPUndo::Redo methods will cause the system to send the correct messages to the parts to get the last action undone. This allows the user to undo actions that were made in other parts, without your part knowing precisely what needs to be done.

When the undo object is told to undo or redo, it calls your part handler back using the Undo or Redo method. If you never post undo actions, you never need worry about having these methods called, and you can ignore them. The XMPUndo object will always return exactly what you store in it, and it makes sure that undo and redo operations are invoked in the correct order. When the Undo object is finished withthe undo data, it asks your part to dispose of it by calling your part's DisposeActionStatemethod. This means that you can safely put pointers to other data into the undo data, since you'll get a chance to dispose of the data, and anything it points to, at a later time.

On some systems, such as one that supports persistent undo stacks, you may be asked to read and write your undo data against a persistent storage medium. This is not the case on the Macintosh, but OpenDoc does allow for it. You can safely ignore this until it becomes an issue on some platform you choose to support.

STORAGE

Eventually it becomes time to save a document. We've already discussed the OpenDocstorage environment to some degree. The storage unit object in OpenDoc is set up for the part by the OpenDoc libraries themselves, so generally a part never needs to talk directly to the file system just to read and write its own data. This system supports not only compound document storage, but also a versioning system that allows for multiple drafts.

DEALING WITH STORAGE UNITS
Once you've been given a storage unit, you typically get it ready by using the Focus call. To minimize the API, a set of common functions that can apply to the entire storage unit, a particular property, or a particular value has been abstracted out. Properties and values within a storage unit are not represented by distinct objects, but are instead captured in the focus state of the storage unit: the Focus method sets up the context for later calls. For example, the Remove method can apply to an entire property or to a single value of it, depending on whether the storage unit was focused on the property or on a value. Focusing can be absolute (when you pass a particular property ID or value index) or relative (when you pass a position code).

The read operation is performed with the XMPStorageUnit::GetValue method, and the write operation with XMPStorageUnit::SetValue. The position can be set or read with XMPStorageUnit::SetOffset and XMPStorageUnit::GetOffset. Efficient inserts and deletes can be performed with XMPStorageUnit::InsertValue andXMPStorageUnit::DeleteValue. You can also use the latter call to truncate a given value.

Typically, your part will focus on the kXMPPropContents property and do various reads or writes, depending on whether your part is being internalized (read in from storage) or externalized (written out). If your part is sufficiently large and complex, you'll probably want to use inserts and deletes to store changes to your persistent data. This has two useful effects: it makes your data more randomly accessible, and it makes the OpenDoc draft system store changes more efficiently.

This draft system allows a user to save a draft of a document and return to view the draft at any future time. Where possible, it stores only the changes between succeeding drafts, instead of storing entire copies of the document for each draft. By using OpenDoc's storage APIs, you automatically get this efficient storage of separate versions with no additional work on your part. OpenDoc only watches the storage operations, though; it doesn't attempt to detect differences on its own. If you use insert and delete operations, OpenDoc's storage system can efficiently store the changes between drafts.

BASIC I/O FOR YOUR PART
When your part is brought into memory, your InitPartFromStorage method is called, and it's passed a storage unit. You are then responsible for reading the storage unit and getting ready to receive other messages. This will happen once, and never again until the object is deleted from memory. Later, when the document is being saved, your part's Externalize method is called. You must immediately write anything you need to store persistently out into your storage unit, before returning from this method.

Your part is also free to write to its storage unit, as well as read from it, whenever it wants to. For part handlers that "virtualize" themselves from disk, this means that OpenDoc won't get in your way.

The CClockPart::InternalizeContent and CClockPart::ExternalizeContent methodsare called by the framework in response to the standard methods InitPartFromStorageand Externalize. They demonstrate focusing a storage unit and doing read and write operations. CClockPart's storage needs are very simple; it just reads and writes a few flags into its storage unit.

PART INFORMATION ATTACHED TO FRAMES
As mentioned earlier, the XMPFrame objects associated with embedding have a partInfo field, which is used like a window refCon by your code. When the document is saved, you may be asked to save the contents of this partInfo field to a particular storage unit. Your part will be called using the XMPPart::WritePartInfo method. Your responsibility is to write enough information to be able to reconstruct the partInfo field. Later, when the document is reopened, your part object will be called with the XMPPart::ReadPartInfo method. This is your cue to read the data back into memory and set up the part info for that frame object once again.

These partInfo fields are useful when you want to write a part that can have several visible frames, each with a different presentation. The chart example we used earlier is a case in point. We would want to allow a chart to be viewed as a table of data or a chart, possibly one of various chart kinds. By storing information about what to display in the frame's part info, you're freed from writing your own data structure to remember what kind of display to do in what frame. Instead, you store that information as a part of the frame's part info and implement WritePartInfo and ReadPartInfo methods to save and restore the data. CClockPart doesn't actually use the partInfo field of its frames in a persistent fashion. It simply inherits code from CPart, which reconstructs the appropriate CFrame and CFacet objects at run time. This is completely adequate for simple parts.

BEING A GOOD OPENDOC CITIZEN

Now that we've covered the basics, there are a few last details to implement before we've got a good basic part. Since a part can have multiple frames, and a frame can be visible in multiple facets, we need to make sure our part handler does the right thing and avoids stepping on the toes of other parts.

ADDING AND REMOVING FACETS
When a part becomes visible (that is, when a facet appears), OpenDoc notifies the part with a call to the FacetAdded method. This is when your part should do any special setup it needs to (for instance, you may want to register for idle time on the frame associated with that facet). Similarly, OpenDoc calls your part handler's FacetRemoved method when the facet goes away; here you should clean up any actions you took in response to FacetAdded.

ADJUSTING MENUS
When your part handler acquires the menu focus, OpenDoc calls its AdjustMenus method. Your job is to correctly update the menus so that the right elements arechecked, enabled, and so on. You can see an example in CClockPart::DoAdjustMenus,which is called by the inherited code from CPart::AdjustMenus.

RELINQUISHING ARBITRATOR FOCI
Once you've acquired any focus from the arbitrator, you'll eventually be called on to release it. This will happen via three methods: XMPPart::BeginRelinquishFocus, XMPPart::CommitRelinquishFocus, and XMPPart::AbortRelinquishFocus. The first method is called to ask your part if it's willing to relinquish a focus it owns. It should, if at all possible, say yes. It's possible, though, that you won't give up a focus, because your part object is in a mode. For instance, you wouldn't give up the serial port focus if you were in the middle of an XMODEM transfer.

Once your part has responded to the XMPPart::BeginRelinquishFocus call, you can expect another call shortly after that which informs your part that the focus has really been given to another frame, or that it hasn't. The first case is signaled by XMPPart::CommitRelinquishFocus, and the second case is signaled by XMPPart::AbortRelinquishFocus.

Occasionally, under difficult conditions, your part will simply be informed that it has either acquired a focus (through the XMPPart::FocusAcquired method) or lost a focus (through the XMPPart::FocusLost method). If your part has lost a focus, you're expected to avoid inappropriate behavior, such as attempting to adjust menus or display a menu bar when you don't have the menu bar focus.

FREEING MEMORY
Your part is expected, if possible, to free some memory on request. When it's needed, you'll be called with the XMPPart::Purge method. You're given a size that's the amount of memory requested. If you can manage it, you should free any unneeded memory from your part's data structures. Don't free anything you need to keep running, of course. You might free any resources you were holding, or free some cached data. CClockPart, our example, is so simple that it has almost nothing to purge.

IN CLOSING

By now you should have a good idea of what's involved in writing an OpenDoc part handler. As you've seen, it's much like writing an application today: you still write code to handle events, deal with storage issues, draw to the screen, and so on. The main differences are really in the "packaging" of the code and in the environment it runs in. (Some previously messy areas have even been cleanly abstracted for you. The storage system is a good example: no more ugly file handling code; you just deal with storage units and let OpenDoc handle the details.)

But the differences for users are amazing. No more worrying about which application can open which document. Instead, when they select a particular type of content to work with, the tools they need to work with that content simply appear. In user tests, many people thought that this radically wonderful technology was just a bug fix, and that it was finally working the way it was always supposed to. There can be no better indication that OpenDoc is a step in the right direction.


KURT PIERSOL, the chief architect of OpenDoc, previously led the Apple Event project and was an early technical lead for AppleScript. He's responsible for making technologies fit together at Apple. Kurt also likes to wear suspenders, though that has very little to do with his software architectural responsibilities. *

Thanks to our technical reviewers David Austin, Ray Chiang, Mark Minshull, Alan Spragens, and Borek Vokach-Brodsky. *

 
AAPL
$99.76
Apple Inc.
+2.09
MSFT
$44.08
Microsoft Corpora
+0.45
GOOG
$520.84
Google Inc.
+9.67

MacTech Search:
Community Search:

Software Updates via MacUpdate

Macgo Blu-ray Player 2.10.9.1750 - Blu-r...
Macgo Mac Blu-ray Player can bring you the most unforgettable Blu-ray experience on your Mac. Overview Macgo Mac Blu-ray Player can satisfy just about every need you could possibly have in a Blu-ray... Read more
Apple iOS 8.1 - The latest version of Ap...
The latest version of iOS can be downloaded through iTunes. Apple iOS 8 comes with big updates to apps you use every day, like Messages and Photos. A whole new way to share content with your family.... Read more
TechTool Pro 7.0.5 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
PDFKey Pro 4.0.2 - Edit and print passwo...
PDFKey Pro can unlock PDF documents protected for printing and copying when you've forgotten your password. It can now also protect your PDF files with a password to prevent unauthorized access and/... Read more
Yasu 2.9.1 - System maintenance app; per...
Yasu was originally created with System Administrators who service large groups of workstations in mind, Yasu (Yet Another System Utility) was made to do a specific group of maintenance tasks... Read more
Hazel 3.3 - Create rules for organizing...
Hazel is your personal housekeeper, organizing and cleaning folders based on rules you define. Hazel can also manage your trash and uninstall your applications. Organize your files using a... Read more
Autopano Giga 3.7 - Stitch multiple imag...
Autopano Giga allows you to stitch 2, 20, or 2,000 images. Version 3.0 integrates impressive new features that will definitely make you adopt Autopano Pro or Autopano Giga: Choose between 9... Read more
MenuMeters 1.8 - CPU, memory, disk, and...
MenuMeters is a set of CPU, memory, disk, and network monitoring tools for Mac OS X. Although there are numerous other programs which do the same thing, none had quite the feature set I was looking... Read more
Coda 2.5 - One-window Web development su...
Coda is a powerful Web editor that puts everything in one place. An editor. Terminal. CSS. Files. With Coda 2, we went beyond expectations. With loads of new, much-requested features, a few... Read more
Arq 4.6.1 - Online backup to Google Driv...
Arq is super-easy online backup for the Mac. Back up to your own Google Drive storage (15GB free storage), your own Amazon Glacier ($.01/GB per month storage) or S3, or any SFTP server. Arq backs up... Read more

Latest Forum Discussions

See All

This Week at 148Apps: October 13-17, 201...
Expert App Reviewers   So little time and so very many apps. What’s a poor iPhone/iPad lover to do? Fortunately, 148Apps is here to give you the rundown on the latest and greatest releases. And we even have a tremendous back catalog of reviews; just... | Read more »
Angry Birds Transformers Review
Angry Birds Transformers Review By Jennifer Allen on October 20th, 2014 Our Rating: :: TRANSFORMED BIRDSUniversal App - Designed for iPhone and iPad Transformed in a way you wouldn’t expect, Angry Birds Transformers is a quite... | Read more »
GAMEVIL Announces the Upcoming Launch of...
GAMEVIL Announces the Upcoming Launch of Mark of the Dragon Posted by Jessica Fisher on October 20th, 2014 [ permalink ] Mark of the Dragon, by GAMEVIL, put | Read more »
Interview With the Angry Birds Transform...
Angry Birds Transformers recently transformed and rolled out worldwide. This run-and-gun title is a hit with young Transformers fans, but the ample references to classic Transformers fandom has also earned it a place in the hearts of long-time... | Read more »
Find Free Food on Campus with Ypay
Find Free Food on Campus with Ypay Posted by Jessica Fisher on October 20th, 2014 [ permalink ] iPhone App - Designed for the iPhone, compatible with the iPad | Read more »
Strung Along Review
Strung Along Review By Jordan Minor on October 20th, 2014 Our Rating: :: GOT NO STRINGSUniversal App - Designed for iPhone and iPad A cool gimmick and a great art style keep Strung Along from completely falling apart.   | Read more »
P2P file transferring app Send Anywhere...
File sharing services like Dropbox have security issues. Email attachments can be problematic when it comes to sharing large files. USB dongles don’t fit into your phone. Send Anywhere, a peer-to-peer file transferring application, solves all of... | Read more »
Zero Age Review
Zero Age Review By Jordan Minor on October 20th, 2014 Our Rating: :: MORE THAN ZEROiPad Only App - Designed for the iPad With its mind-bending puzzles and spellbinding visuals, Zero Age has it all.   | Read more »
Hay Ewe Review
Hay Ewe Review By Campbell Bird on October 20th, 2014 Our Rating: :: SAVE YOUR SHEEPLEUniversal App - Designed for iPhone and iPad Pave the way for your flock in this line drawing puzzle game from the creators of Worms.   | Read more »
My Very Hungry Caterpillar (Education)
My Very Hungry Caterpillar 1.0.0 Device: iOS Universal Category: Education Price: $3.99, Version: 1.0.0 (iTunes) Description: Care for your very own Very Hungry Caterpillar! My Very Hungry Caterpillar will captivate you as he crawls... | Read more »

Price Scanner via MacPrices.net

2013 15-inch 2.0GHz Retina MacBook Pro availa...
B&H Photo has leftover previous-generation 15″ 2.0GHz Retina MacBook Pros now available for $1599 including free shipping plus NY sales tax only. Their price is $400 off original MSRP. B&H... Read more
Updated iPad Prices
We’ve updated our iPad Air Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers, including the new iPad Air 2 and the... Read more
Apple Pay Available to Millions of Visa Cardh...
Visa Inc. brings secure, convenient payments to iPad Air 2 and iPad mini 3as well as iPhone 6 and 6 Plus. Starting October 20th, eligible Visa cardholders in the U.S. will be able to use Apple Pay,... Read more
Textkraft Pocket – the missing TextEdit for i...
infovole GmbH has announced the release and immediate availability of Textkraft Pocket 1.0, a professional text editor and note taking app for Apple’s iPhone. In March 2014 rumors were all about... Read more
C Spire to offer iPad Air 2 and iPad mini 3,...
C Spire on Friday announced that it will offer iPad Air 2 and iPad mini 3, both with Wi-Fi + Cellular, on its 4G+ LTE network in the coming weeks. C Spire will offer the new iPads with a range of... Read more
Belkin Announces Full Line of Keyboards and C...
Belkin International has unveiled a new lineup of keyboard cases and accessories for Apple’s newest iPads, featuring three QODE keyboards and a collection of thin, lightweight folios for both the... Read more
Verizon offers new iPad Air 2 preorders for $...
Verizon Wireless is accepting preorders for the new iPad Air 2, cellular models, for $100 off MSRP with a 2-year service agreement: - 16GB iPad Air 2 WiFi + Cellular: $529.99 - 64GB iPad Air 2 WiFi... Read more
Price drops on refurbished Mac minis, now ava...
The Apple Store has dropped prices on Apple Certified Refurbished previous-generation Mac minis, with models now available starting at $419. Apple’s one-year warranty is included with each mini, and... Read more
Apple refurbished 2014 MacBook Airs available...
The Apple Store has Apple Certified Refurbished 2014 MacBook Airs available for up to $180 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free.... Read more
Refurbished 2013 MacBook Pros available for u...
The Apple Store has Apple Certified Refurbished 13″ and 15″ MacBook Pros available starting at $929. Apple’s one-year warranty is standard, and shipping is free: - 13″ 2.5GHz MacBook Pros (4GB RAM/... Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** At the Apple Store, you connect business professionals and entrepreneurs with the tools they need in order to put Apple solutions to work in their Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** The Apple Store is a retail environment like no other - uniquely focused on delivering amazing customer experiences. As an Expert, you introduce people Read more
Position Opening at *Apple* - Apple (United...
**Job Summary** As businesses discover the power of Apple computers and mobile devices, it's your job - as a Solutions Engineer - to show them how to introduce these Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.