Important: The information in this document is obsolete and should not be used for new development.
Recipes--The Edition Manager
The recipe and sample code in this section demonstrate how to add publish and subscribe support to your application. Classes and methods that supply Edition Manager support are shown in Figure 31-1.Recipe--Adding Publish and Subscribe Support to Your Application
MacApp handles much of the work of interacting with the Edition Manager, including handling section Apple events and displaying and responding to Edition Manager menu commands. To add publish and subscribe support to your application, you perform these additional steps:
The sample code shown in this recipe is from the Calc application.
- Include MacApp header files for the Edition Manager in your source code.
- Include Edition Manager resources in your resource definition file.
- Base your document class on
TEditionDocument
.- Provide a document initialization method. Your method should do the following:
- Call the
IEditionDocument
method of its parent class.- Create a selection designator for the document and call
SetUserSelection
to set it.
- Specify the data format for your subscriber sections.
- Override the
DoWriteData
method to publish data.- Override the
DoReadData
method to subscribe to data.- Notify the Edition Manager when the user selection changes.
- Override the
DoAddBorder
method to supply a border adorner.- Add a dependency to trigger publisher updates (optional).
Include MacApp Header Files for the Edition Manager
To use MacApp support for the Edition Manager in your source code, you need to include specific MacApp header files. For example, the Calc application includes the following headers in the fileUCalc.h
:
#ifndef __EDITIONS__ #include <Editions.h> #endif #ifndef __UAPPLEEVENTS__ #include "UAppleEvents.h" #endif #ifndef __UDESIGNATOR__ #include "UDesignator.h" #endif #ifndef __UEDITIONDOCUMENT__ #include "UEditionDocument.h" #endif #ifndef __USECTION__ #include "USection.h" #endifThe first file shown, Editions.h, contains Edition Manager interface definitions. The remaining files contain definitions for MacApp Edition Manager classes and other Edition Manager support.Include Edition Manager Resources in Your Resource File
To be able to respond to section Apple events from the Event Manager, your application must include MacApp's Edition Manager resources. These resources are in the fileEditions.rsrc
. To include them in your application, add the following line to your resource definition file:
include "Editions.rsrc" not 'ckid';The wordsnot 'ckid'
ensure that you will not copy the resource that is used for source-code control.Edition Manager menu commands are normally displayed in the Edit menu, so your application should include an Edit menu
'CMNU'
resource that contains these commands. MacApp provides a default Edition Manager Edit menu in the fileDefaults.rsrc
. You can include this resource in your application by adding the following line to your resource definition file:
include "Defaults.rsrc" 'CMNU' (mEditionMgrEdit);If you prefer, you can modify your own Edit menu definition to include the Edition Manager items. The easiest way to do this is to cut the text for the items from the Edit menu'CMNU'
resource in MacApp'sDefaults.r
file and paste them into the Edit menu resource in your resource definition file.You also need to add the Edition Manager menu to your menu bar, by adding its ID to your
'MBAR'
definition. For example, this'MBAR'
definition from the Calc application is defined in the fileCalc.r
:
resource 'MBAR' (kMBarDisplayed, #if qNames "Calc", #endif purgeable) { {mApple; mFile; mEditionMgrEdit; mFormat; mCalculation} };Base Your Document Class on TEditionDocument
To take advantage of MacApp's Edition Manager support, your document class should descend from theTEditionDocument
class. For example, theTCalcDocument
class begins its definition as follows:
class TCalcDocument : public TEditionDocumentProvide a Document Initialization Method
The initialization method for your document class has two responsibilities for initializing MacApp's Edition Manager support. First, it should call the initialization method of its parent class,TEditionDocument
, with a line like the following (fromICalcDocument
):
this->IEditionDocument(itsFile, kCalcScrapType, kSignature);In this call, the Calc document class passes constants that specify its file, its preferred scrap type (for cutting and pasting), and the application's signature (to identify the application that created the document). The IEditionDocument method calls theInitUSectionManager
method, which initializes the MacApp Section Manager unit.The second responsibility of your initialization method is to create a selection designator and use it to set the document's
fUserSelection
field. The Calc document does so with the following code, from theICalcDocument
method:
TRegionDesignator*aRgnDesignator; RgnHandle aRgnHandle; // Create a user selection designator. aRgnHandle = MakeNewRgn(); aRgnDesignator = new TRegionDesignator; aRgnDesignator->IRegionDesignator(aRgnHandle, TRUE); aRgnDesignator->SetDesignationRect(CRect(1, 1, 2, 2)); // Set the document's fUserSelection field. SetUserSelection(aRgnDesignator);This code creates a new region and region designator, initializes the region designator, sets the designator to designate the top-left cell in the spreadsheet, and calls SetUserSelection. TheTCalcDocument::
SetUserSelection method callsInherited
to set thefUserSelection
field to the new designator, then performs its own user-selection operations.
- Note
- In this snippet of sample code, failure handling is left as an exercise for the reader.
Specify the Data Format for Your Subscriber Sections
You can skip this step if your application subscribes only to sections with data in one of the standard formats:'PICT'
,'TEXT'
, or 'snd '
.If you are not using one of the standard formats, you override two methods in your document class:
GetPublishPreference
andGetSubscriberFormatsMask
. These methods are described next.GetPublishPreference
TheGetPublishPreference
method returns the document's preferred section type. For theTEditionDocument
class,'TEXT'
is the preferred type. Your version ofGetPublishPreference
should look something like the following:
FormatType TYourDocument::GetPublishPreference() { return ((long) kYourSectionPreference); }GetSubscriberFormatsMask
TheGetSubscriberFormatsMask
method calls theGetPublishPreference
method to help it determine which section format mask to use. In theTEditionDocument
class,GetSubscriberFormatsMask
is defined as follows:
SignedByte TEditionDocument::GetSubscriberFormatsMask() { // Return a mask corresponding to the edition format type to display // within the subscriber dialog box. This mask may consist of as // many types as are supported in the application. // Construct the mask as follows: // GetSubscriberFormatsMask =[kPICTformatMask] [+] // [kTextformatMask [+] [ksndFormatMask] if (this->GetPublishPreference() == 'TEXT') return (kTEXTformatMask); else if (this->GetPublishPreference() == 'PICT') return (kPICTformatMask); else if (this->GetPublishPreference() == 'snd ') return (ksndFormatMask); else return 0; }Your version ofGetSubscriberFormatsMask
should look something like the following:
SignedByte TYourDocument::GetSubscriberFormatsMask() { if (this->GetPublishPreference() == kYourSectionPreference) return (kYourFormatMask); else return Inherited::GetSubscriberFormatsMask(); }Override the DoWriteData Method to Publish Data
MacApp calls your document'sDoWriteData
method as part of processing a Publish command. You overrideDoWriteData
to write your data to an edition container file by writing to the passed stream. The kind of data you write depends on theaScrapType
parameter passed toDoWriteData
. In the Calc application, theTCalcDocument::DoWriteData
method can publish three kinds of data:'TEXT'
,'PICT'
, or its own kind of spreadsheet data:
void TCalcDocument::DoWriteData(const OSTypeaScrapType, TDesignator*aDesignator, TStream* aStream) // Override. { SignedByte savedState; Handle textHandle; if (aScrapType == 'TEXT') { textHandle = fCellsView->DesignatorAsTEXT(aDesignator); savedState = LockHandleHigh(textHandle); aStream->WriteBytes((*textHandle), GetHandleSize(textHandle)); HSetState(textHandle, savedState); DisposeHandle(textHandle); } else if (aScrapType == fScrapType) this->DoWritePrivateTypes(aDesignator, aStream); }This DoWriteData method calls theTCellsView::DesignatorAsTEXT
method to write the data for the selected spreadsheet cells.DesignatorAsTEXT
iterates over the selected cells and asks each cell to write its data, delimited by a tab or a carriage return character, to a handle stream. It then returns the handle containing the text. (For more information on theDesignatorAsTEXT
method, see "Recipe--Using a Handle Stream to Write Text to a Handle," beginning on page 601.)Override the DoReadData Method to Subscribe to Data
MacApp calls your document'sDoReadData
method as part of processing a Subscribe command. You overrideDoReadData
to read your data from an edition container file by reading from the passed stream. The kind of data you read depends on theaScrapType
parameter passed toDoReadData
. In the Calc application, theTCalcDocument::DoReadData
method can read either'TEXT'
data or its own kind of spreadsheet data:
void TCalcDocument::DoReadData(const OSTypeaScrapType, TDesignator*aDesignator, TStream* aStream, long length) // Override. { if (aScrapType == 'TEXT') this->DoReadTEXT(aDesignator, aStream, length); else if (aScrapType == fScrapType) this->DoReadPrivateTypes(aDesignator, aStream); }The DoReadTEXT method reads text data, and the DoReadPrivateTypes method reads Calc's private spreadsheet data.Notify the Edition Manager When the User Selection Changes
Whenever the user changes the current selection, your application should call theUserSelectionChanged
method of the view class that displays your document's data. In theTView
class, theUserSelectionChanged
method calls theUserSelectionChanged
method of the view's document.If your view class overrides
UserSelectionChanged
, your version ofUserSelectionChanged
should callInherited
, as Calc'sTCellsView
class does:
void TCellsView::UserSelectionChanged(TView* changedView) { if (!fCalcDocument->fShowSectionBorders) { // Invalidate all adorners if not showing borders. CAdornerIterator iter(this); for (TAdorner* theAdorner = iter.FirstAdorner(); iter.More(); theAdorner = iter.NextAdorner()) { if ((theAdorner->fIdentifier == kPublisherAdornerID) || (theAdorner->fIdentifier == kSubscriberAdornerID)) { theAdorner->InvalidateAdorner(this); } } } // Call Inherited to inform the document of the change. Inherited::UserSelectionChanged(changedView); } // TCellsView::UserSelectionChangedThe Calc application also overrides the UserSelectionChanged method in theTCalcDocument
class:
void TCalcDocument::UserSelectionChanged(TView* changedView) // Override. { TDesignator * userSelection; // Let document's selection designator know designation has changed. userSelection = this->GetUserSelection(); if ((userSelection != NULL) &&userSelection->DescendsFrom( TRegionDesignator::GetClassDescStatic())) CopyRgn(fCellsView->fSelections,((TRegionDesignator *) (userSelection))->fDesignation); Inherited::UserSelectionChanged(changedView); }If the user selection has changed, this method updates the document's user selection designator by copying the view's current selection region.Override DoAddBorder to Supply a Border Adorner
TheDoAddBorder
method does nothing in theTEditionDocument
class. You override this method in your document class to supply an adorner object to draw the border for your publisher or subscriber. The following is theDoAddBorder
method from the Calc document:
void TCalcDocument::DoAddBorder(TSection* aSection) { // Adding a section--give it a section adorner. if (fCellsView) { if (aSection->GetSectionType() == stSubscriber) fCellsView->CreateSubscriberAdorner((TSubscriber *)aSection); else fCellsView->CreatePublisherAdorner((TPublisher *)aSection); // Invalidate the borders of publishers/subscribers if we aren't // showing all borders. if (!fShowSectionBorders) { CAdornerIterator iter(fCellsView); for (TAdorner * anAdorner = iter.FirstAdorner(); iter.More(); anAdorner = iter.NextAdorner()) if ((anAdorner->fIdentifier == kPublisherAdornerID) || (anAdorner->fIdentifier == kSubscriberAdornerID)) anAdorner->InvalidateAdorner(fCellsView); } } }The CreateSubscriberAdorner method creates an adorner to draw a subscriber border, and the CreatePublisherAdorner method creates an adorner to draw a publisher border. Both methods add the adorner to the view's list of adorners.Add a Dependency to Trigger Publisher Updates
The Calc application uses dependencies to ensure that a publisher is notified whenever a cell within the publisher is changed. This is not a requirement, but it is an approach you may wish to emulate in your application.To make a publisher a dependent of each cell in the published selection, the
TCalcDocument
class overrides theTEditionDocument
methodAddSectionAndBorder
with the following implementation:
void TCalcDocument::AddSectionAndBorder(TSection* aSection) { Inherited::AddSectionAndBorder(aSection); this->AddPublisherDependency(aSection); }This AddSectionAndBorder method first callsInherited
to add the section and its adorner, then calls AddPublisherDependency, also a method ofTCalcDocument
. The AddPublisherDependency method iterates over the cells in the published section, adding the section as a dependent of each cell.The
TCell::Recalculate
method makes the following call whenever a cell changes:
this->Changed(mValueChanged, this);As a result, the publisher is notified whenever the contents of one of its cells changes.Appendixes