Handling Frame Layout
Parts lay themselves out for display in a document according to the demands of their content and in negotiation with their containing parts. This process takes place through the mechanism of the part's display frames. Methods illustrating how SamplePart handles its display frames are included in this section.SamplePart is a noncontainer part--it does not support embedding of other parts in it--so its frame layout considerations are somewhat simpler than those of container parts. Nonetheless, SamplePart participates in the layout negotiations of its containing part when it is itself embedded in another part. In addition, SamplePart supports multiple display frames, displaying itself in multiple frames and synchronizing those displays as necessary.
The DisplayFrameAdded Method
OpenDoc calls theDisplayFrameAddedmethod when a new display frame is created for the part, for example, after the containing part calls the draft'sCreateFramemethod. Generally, in response to theDisplayFrameAddedcall, a part should set itself up to manage the new frame and ensure that it can handle the frame's display requirements.The
SamplePartobject's implementation of theDisplayFrameAddedmethod performs the following actions:
Listing 2-9 shows the implementation of the
- Sets up the presentation and view type correctly.
The method checks the new frame's presentation and view type. If the frame's presentation is not the SamplePart main presentation type, the method sets it to be so. If the view type is null, the method sets it to frame view, the most typical preferred type.
- Stores part info data for the new frame.
SamplePart creates aCFrameInfoobject for this purpose and stores a reference to it in the frame's part info field.
- Sets the frame's window disposal flag.
If the frame being added is a root frame, then it has a window associated with it, and the window must be disposed of when the frame is removed. The window disposal flag is checked in SamplePart's internalCleanupWindowmethod.
- Updates the part's frame list.
Finally, the method creates a proxy for the new frame and adds a reference to the proxy to its internal display frame list.
DisplayFrameAddedmethod.Listing 2-9
DisplayFrameAddedmethod
void SamplePart::DisplayFrameAdded( Environment*ev, ODFrame* frame ) { SOM_Trace("SamplePart","DisplayFrameAdded"); if ( frame->GetPresentation(ev) != gGlobals->fMainPresentation ) frame->SetPresentation(ev, gGlobals->fMainPresentation); if ( frame->GetViewType(ev) == kODNullTypeToken ) frame->SetViewType(ev, gGlobals->fFrameView); CFrameInfo* frameInfo = new CFrameInfo; frame->SetPartInfo(ev, (ODInfoType)frameInfo); if ( frame->IsRoot(ev) ) frameInfo->SetShouldDisposeWindow(kODTrue); CFrameProxy* proxy = new CFrameProxy; proxy->InitFrameProxy(ev,frame); fDisplayFrames->Add(proxy); this->SetDirty(ev); }The DisplayFrameConnected Method
OpenDoc calls theDisplayFrameConnectedmethod if the part is embedded and the containing part reads the display frame into memory, having previously written it to storage. This occurs when the frame becomes visible through scrolling or other actions. OpenDoc calls this method instead ofDisplayFrameAddedbecause a new frame is not being created; an existing one is being reconnected to the part.The
SamplePartobject's implementation of theDisplayFrameConnectedmethod performs the following actions:
Listing 2-10 shows the implementation of the
- Updates the part's frame list.
The method iterates over SamplePart's list of display frames, attempting to match the frame's ID number with the ID numbers of the frame proxies in the list. If there is no match, the method adds the frame. If there is a match, the method updates the proxy's internal fields with information obtained from the frame.
- Ensures that the presentation is meaningful.
The part editor must be able to display the frame, so it must recognize the presentation. In SamplePart's case, the method compares the frame's presentation to the main presentation stored in the globals structure. If it differs, the method sets it to be the main presentation.
- Handles the root frame case.
If the frame is a root frame, the method does two things: it sets the window disposal flag tokODTrue, and it sets the view type to frame view.
DisplayFrameConnectedmethod.Listing 2-10
DisplayFrameConnectedmethod
void SamplePart::DisplayFrameConnected( Environment*ev, ODFrame* frame ) { SOM_Trace("SamplePart","DisplayFrameConnected"); ODBoolean found = kODFalse; CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { if ( proxy->GetID() == frame->GetID(ev) ) { proxy->SetFrame(ev,frame); found = kODTrue; } } if ( found ) { if ( frame->GetPresentation(ev) != gGlobals->fMainPresentation ) frame->SetPresentation(ev, gGlobals->fMainPresentation); if ( frame->IsRoot(ev) ) { CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev); frameInfo->SetShouldDisposeWindow(kODTrue); if ( frame->GetViewType(ev) != gGlobals->fFrameView ) frame->SetViewType(ev, gGlobals->fFrameView); } } else { this->DisplayFrameAdded(ev, frame); } }The DisplayFrameRemoved Method
OpenDoc calls a part'sDisplayFrameRemovedmethod when its containing part has permanently removed one of the part's display frames. Generally, implementations of theDisplayFrameRemovedmethod perform any actions required to remove the frame, including removing frames embedded within the removed frame, relinquishing foci, and updating the part's internal frame list.The
SamplePartobject's implementation of theDisplayFrameRemovedmethod performs the following actions:
If any of the preceding actions causes an exception to be thrown, the method catches it in its
- Relinquishes any foci owned by the frame.
The method calls theSamplePartobject's internalRelinquishAllFocimethod, which instantiates a temporary frame object to wrap the reference returned by the arbitrator for each of the foci a SamplePart frame could own: the selection focus and the menu focus. TheRelinquishAllFocimethod compares the focus owner with the frame to be removed, and, if they are equal, relinquishes the focus through the arbitrator and notifies the part that the focus is lost.The
RelinquishAllFocimethod uses theTempODFrameclass, a C++ template class declared in the file TempObj.h, and theODObjectsAreEqualfunction, defined in the file ODUtils.h. They are described in Appendix A, "OpenDoc Utilities."
- Cleans up the display frame references.
The method calls theSamplePartobject's internalCleanupDisplayFramemethod. If this frame (that is, the frame to be removed) has a source frame, theCleanupDisplayFramemethod gets a reference to the source frame and to its frame info object. It invalidates the source frame to force it to redraw without any possible effects of having been synchronized with this frame. The method notifies the source frame that it is going away and releases this frame's reference to the source frame (decrementing the source frame's reference count).If this frame is a root frame, then it is in a part window which is being closed, so the
CleanupDisplayFramemethod notifies the source frame that it no longer has a part window. Conversely, if the frame has a part window, the method closes and removes it.If the frame being removed has a dependent frame, the
CleanupDisplayFramemethod notifies it that its source frame is being removed and releases its own reference to the dependent frame.
- Cleans up any window associated with the frame.
The method calls theSamplePartobject's internalCleanupWindowmethod, which checks this frame'sShouldDisposeWindowflag. If the flag is true, the method retrieves references to the frame's OpenDoc window object and its Mac OS platform window structure. It releases the OpenDoc window object, then closes and disposes of the platform window.
- Cleans up the frame and removes it from the part's internal frame list.
The method sets to null its pointer to its frame info object, then deletes the object using theODDeleteObjectutility macro (which is defined in the ODUtils.h file). Finally, the method removes this frame from its internal display frame list and sets the part's dirty flag.
CATCH_ALLhandler, which displays an error dialog box to the user and propagates the error.Listing 2-11 shows the implementation of the
DisplayFrameRemovedmethod.Listing 2-11
DisplayFrameRemovedmethod
void SamplePart::DisplayFrameRemoved( Environment*ev, ODFrame* frame ) { SOM_Trace("SamplePart","DisplayFrameRemoved"); TRY CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev); this->RelinquishAllFoci(ev, frame); this->CleanupDisplayFrame(ev, frame, kFrameRemoved); this->CleanupWindow(ev, frame); frame->SetPartInfo(ev, (ODInfoType) kODNULL); ODDeleteObject(frameInfo); CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { if ( ODObjectsAreEqual(ev, proxy->GetFrame(ev), frame) ) { fiter.RemoveCurrent(); delete proxy; } } this->SetDirty(ev); CATCH_ALL this->DoDialogBox(ev, frame, kErrorBoxID, kErrRemoveFrame); SetErrorCode(kODErrAlreadyNotified); RERAISE; ENDTRY }The DisplayFrameClosed Method
OpenDoc calls theDisplayFrameClosedmethod when a frame is closed as a result of the user closing its document. The SamplePart implementation of theDisplayFrameClosedmethod is virtually identical to that of itsDisplayFrameRemovedmethod except it does not cache runtime information, so it does not set the part's dirty flag. Also, theDisplayFrameClosedmethod does not delete the frame proxy object because closed frames may be reconnected before the document is finally closed.Listing 2-12 shows the implementation of the
DisplayFrameClosedmethod.Listing 2-12
DisplayFrameClosedmethod
void SamplePart::DisplayFrameClosed( Environment*ev, ODFrame* frame ) { SOM_Trace("SamplePart","DisplayFrameClosed"); TRY CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev); this->RelinquishAllFoci(ev, frame); this->CleanupDisplayFrame(ev, frame, kFrameClosed); this->CleanupWindow(ev, frame); frame->SetPartInfo(ev, (ODInfoType) kODNULL); ODDeleteObject(frameInfo); CListIterator fiter(fDisplayFrames); for ( CFrameProxy* proxy = (CFrameProxy*) fiter.First(); fiter.IsNotComplete(); proxy = (CFrameProxy*) fiter.Next() ) { if ( proxy->GetID() == frame->GetID(ev) ) { proxy->Purge(ev); } } CATCH_ALL this->DoDialogBox(ev, frame, kErrorBoxID, kErrRemoveFrame); SetErrorCode(kODErrAlreadyNotified); RERAISE; ENDTRY }The AttachSourceFrame Method
OpenDoc calls a part'sAttachSourceFramemethod during creation of a part window from a containing part. That is, if SamplePart is embedded in a frame of another part, and that frame is opened into a part window, the containing part iterates over its embedded frames and adds new corresponding frames in the part window. After each new embedded frame is created, the containing part calls theAttachSourceFramemethod.Listing 2-13 shows the implementation of the
AttachSourceFramemethod.Listing 2-13
AttachSourceFramemethod
void SamplePart::AttachSourceFrame( Environment* ev, ODFrame* frame, ODFrame* sourceFrame ) { SOM_Trace("SamplePart","AttachSourceFrame"); CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev); frameInfo->SetSourceFrame(ev, sourceFrame); frameInfo = (CFrameInfo*) sourceFrame->GetPartInfo(ev); frameInfo->SetDependentFrame(ev, frame); }The FrameShapeChanged Method
OpenDoc calls a part'sFrameShapeChangedmethod whenever the part's display frame's shape has been changed, either by the user or by the containing part (if this part is embedded). OpenDoc passes a pointer to the frame whose shape has changed with the method call. The basic responsibility of this method is to update all synchronized frames by propagating the new frame shape to them. To do so, the method finds all the synchronized frames, pointers to which are stored in this frame'sCFrameInfoobject, and calls each frame'sRequestFrameShapemethod.Listing 2-14 shows the implementation of the
FrameShapeChangedmethod.Listing 2-14
FrameShapeChangedmethod
void SamplePart::FrameShapeChanged( Environment* ev, ODFrame* frame ) { SOM_Trace("SamplePart","FrameShapeChanged"); if ( !frame->IsRoot(ev) ) { TempODShape frameShape = frame->AcquireFrameShape(ev, kODNULL); CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev); ODFrame* displayFrame; if ( frameInfo->HasSourceFrame() ) { displayFrame = frameInfo->GetSourceFrame(ev); TempODShape frameShapeCopy = frameShape->Copy(ev); TempODShape returnShape = displayFrame-> RequestFrameShape(ev, frameShapeCopy, kODNULL); displayFrame->Invalidate(ev, kODNULL, kODNULL); } if ( frameInfo->HasDependentFrame() ) { displayFrame = frameInfo->GetDependentFrame(ev); TempODShape frameShapeCopy = frameShape->Copy(ev); TempODShape returnShape = displayFrame-> RequestFrameShape(ev, frameShapeCopy, kODNULL); displayFrame->Invalidate(ev, kODNULL, kODNULL); } } }