The standards recipes are listed as follows:
According to the SOMObject Developer Toolkit Users Guide.
In OpenDoc, we recommend the use of the first form to make a parent-method call.
For example, if a part (called MyPart) needs to call its parent ODPart's InitPart method, it should use the following macro (defined in MyPart's implementation header file):
MyPart_parent_ODPart_InitPart(somSelf,ev,storageUnit,partWrapper) |
Note:
There are other macros that are generated by SOM for making parent-method calls. However, those forms may be ambiguous if multiple inheritance is used. Some of these short forms exist only for backward compatibility and may not be supported in future versions of SOM.
What Is an ODByteArray?
An ODByteArray is defined as a sequence of octets:
typedef struct { unsigned long _maximum; unsigned long _length; octet *_buffer; } _IDL_SEQUENCE_octet; typedef _IDL_SEQUENCE_octet ODByteArray; |
where:
Some rules about ODByteArray:
Why Use ODByteArray?
The original OpenDoc function accepted and returned buffer pointers and their corresponding sizes. However, the function has been updated to use ODByteArray. This renders the OpenDoc function CORBA compliant and ready for distribution.
Usage example
The following examples creates ODByteArray on the stack. There is no restriction that ODByteArray is a stack variable. It can be allocated in the heap.
// Allocate the byte array on the stack ODByteArray ba; // Set up the byte array ba._length = size; ba._maximum = size; ba._buffer = ptr; // Get the value su->SetValue(ev, &ba); // ba will be deallocated when the function exits. // ptr needs to be explicitly deallocated by client. |
// Allocate the byte array on the stack. The fields are not pre-filled. ODByteArray ba; // Get the value ODULong length = su->GetValue(ev, desiredLength, &ba); // Use the buffer. SomeFunction(ba._buffer); // ba will be deallocated when the function exits. // ptr needs to be explicitly deallocated by client. SOMFree(ba._buffer); |
// Byte array is allocated on the stack. // None of the fields in the ODByteArray needs to be set prior to use. ODByteArray ba = container->GetID(ev); // Use the buffer in the ODByteArray. . . . // Deallocate the buffer. // Even though the client of the method does not allocate // the memory, it is the client is responsibility to deallocate it. SOMFree(ba._buffer); // Byte Array is deallocated from the stack when the function exits. |
This section describes how object references should be compared in OpenDoc and the motivation behind it.
IsEqualTo
In many places in the part code, object equality is determined by comparing pointers. This will fail when OpenDoc is used in a distributed environment. That is why ODObject has the IsEqual method. The purpose of this method is to determine whether two object references belong to the same object. If so, kODTrue is returned; otherwise, kODFalse.
This section describes the storage (or memory) responsibility of client arguments (or parameters).
Part Properties are items that affect a part's behavior or presentation but are not strictly held in the part's content. Typical examples are background color and default font. These properties can be retained on a per-part or a per-frame basis.
Part frames in an OpenDoc document are maintained in a strict containment-based hierarchy, and a standard protocol is established for allowing properties to be maintained and inherited through this hierarchy. This mechanism allows properties to be changed within a section of a document without requiring specific changes to each sub-component within that section. For example, a top-level page layout component can change the page background color and have that color change propagate in to a contained text part which contains a drawing part which contains a clock such that the backgrounds all remain in sync.
Note:
A given component can decouple from the standard property. For example, a component could decouple to maintain an independent background color. In this case, it does not respect changes to this property from its container and it explicitly prevents propagation of the property to its embeds to maintain a strict property hierarchy.
Two things are required for this mechanism to work properly:
Properties within OpenDoc have two aspects: a property name and a value type. The property name is a unique ISOstring that denotes the desired intent of the property. The value type denotes the format the property is being held in. OpenDoc allows you to specify multiple value types so you could pass, for example, a background color property with RGB and HSB values and a part inspecting the property would be free to use whichever was appropriate for it.
An initial set of agreed standard properties for use within OpenDoc for OS/2 is given here. For each, the #define name is shown for both the property and primary value. The actual ISOstrings can be found in StdProps.idl in the OpenDoc toolkit:
Background color | |
property | kODBackgroundColor |
value | kODRGB2 |
Foreground color | |
property | kODForegroundColor |
value | kODRGB2 |
Default font | |
property | kODFont |
value: | kODFontNameSize (actually an ISOString with the same format as OS/2's Presparams) |
Background transparency | |
property | kODBackgroundTransparency |
value | kODBoolean |
While the first three of these properties can be thought of as properties of a container which embedded parts (frames) may or may not choose to respect, the background transparency should be thought of as a property of an embedded display frame which is controlled from the container side. Examples used in this recipe pertain directly to the first three.
Non-container parts should maintain only those standard properties which are relevant to their function; it makes no sense for a non-container image part to concern itself with the default text font (unless its editing capabilities include creation of text on an image). These parts should retain the current values persistently, and handle changes to the values either through direct user interaction, such as selection from a menu or drag and drop from a system color palette, or through notification of changes from its container through the part's ContainingPartPropertiesUpdated method. A sample implementation for this method for non-container parts is:
SOM_SCOPE void SOMLINK MyPartContainingPartPropertiesUpdated (MyPart *somSelf, Environment *ev, ODFrame* frame, ODStorageUnit* propSU) { MyPartData *somThis = MyPartGetData(somSelf); MyPartMethodDebug("MyPart","ContainingPartPropertiesUpdated"); RGBColor rgb; SOM_TRY // is the background color in there??? if (ODSUExistsThenFocus( ev, propSU, kODBackgroundColor, kODRGB2) ) { // read the value being passed in StorageUnitGetValue( propSU, ev, sizeof(rgb), &rgb); // it *should* be different, but check to be sure if (rgb != _fBGColor) { // Change our value. // This method should call SetChangedFromPrevious and // invalidate the frame somSelf->SetBGColor(ev, frame, rgb ); } } SOM_CATCH_ALL SOM_RETURN } |
In addition, when an embed is instantiated and its first display frame is added, it should ask its containing part for values for the standard properties using the AcquireContainingPartProperties method (of ODPart). It is conceivable that an embed not even retain the value and instead query the value whenever it was needed (such as when creating a thumbnail, or even when the Draw method was called), but calling up the hierarchy and building the set of standard properties each time should be expected to have relatively poor performance when compared to accessing instance data. An example of how to query your container's properties and extract the background color is:
SOM_Scope void SOMLINK MyPartDisplayFrameAdded( MyPart *somSelf, Environment *ev, ODFrame* myFrame) { MyPartData *somThis = MyPartGetData(somSelf); MyPartMethodDebug("MyPart","DisplayFrameAdded"); SOM_TRY ... other code // now lets get our container's std properties TempODFrame containingFrame = myFrame->AcquireContainingFrame(ev); TempODPart containingPart = containingFrame->AcquirePart(ev); TempODStorageUnit suProps = containingPart-> AcquireContainingPartProperties(ev, myFrame); // Is the background color in there? if (ODSUExistsThenFocus( ev, propSU, kODBackgroundColor, kODRGB2) ) { // read the value directly into our instance variable StorageUnitGetValue( propSU, ev, sizeof(_fBGColor), &_fBGColor); } SOM_CATCH_ALL SOM_RETURN } |
An additional point concerns whether or not an embed should respect its container's standard properties. Ideally, a part will not only retain its current value for a standard property of interest, but also a flag specifying whether this value is coupled to that of its container. This allows an embed to change its background color and know not to follow in response to its container sending a background color change notification. Also ideally, a user interface would be provided whereby the user could control this coupling. For cases where no direct mechanism is to be provided, an alternative protocol is proposed: the part should start out initially coupled with its container. If its background color is changed directly to something other than that of its container, it becomes decoupled and stops responding to changes in this property from its container. Such a property can be re-coupled by changing the property in the embed to the same value that its container holds, as will be illustrated in the sample code for a container.
Container parts have the greatest responsibility with regard to part properties. The are responsible for remembering and using the properties (as are embedded parts), propagating changes to embedded parts, and responding to queries from embedded parts. When a standard property is changed directly in a container, it is responsible for building a storage unit containing the modified property and passing the information to its embedded parts. This notification is performed by calling the part's ContainingPartPropertiesUpdated method on some or all of its embeds as appropriate. For a property like background color, the container should iterate over all of its embedded frames and make this call on the associated Part; for background transparency it is feasible to change the property for only those embeds that are selected.
When receiving a ContainingPartPropertiesUpdated notification from a container's container, the secondary container has several responsibilities:
SOM_SCOPE void SOMLINK MyPartContainingPartPropertiesUpdated ( MyPart *somSelf, Environment *ev, ODFrame* frame, ODStorageUnit* propSU) { MyPartData *somThis = MyPartGetData(somSelf); MyPartMethodDebug("MyPart","ContainingPartPropertiesUpdated"); SOM_TRY // is the background color in there??? if (ODSUExistsThenFocus( ev, propSU, kODBackgroundColor, kODRGB2) ) { RGBColor rgb; StorageUnitGetValue( propSU, ev, sizeof(RGBColor), &rgb); // are we doing background color matching? (its the default) if (_fMatchBGColor) { // change our value (do not propagate here, done below) // I've put a parameter on this method to signify whether to propagate // the color change in the SetBGColor call... somSelf->SetBGColor(ev, frame, rgb, kODFalse); // we are not following this change so we will NOT propagate // this property } else { // If the new value matches our current value we re-couple. // The embeds already know our color // so we don't need to pass it along. if (rgb == _fBGColor) { _fMatchBGColor = kODTrue; } // If this is the only property in the su, abort here // so as to NOT propogate it if (propSU->CountProperties(ev) == 1) { return; } // Otherwise remove this property and leave the rest else { propSU->Focus(ev, kODBackgroundColor, 0, 0, 0, kODPosUndefined); propSU->Remove(ev); } } } // now pass any remaining notification along to embeds ODFrame *subframe = (ODFrame*)_fEmbeddedFrames->First(); while (subframe) { TempODPart embeddedPart = subframe->AcquirePart(ev); if (embeddedPart) { embeddedPart->ContainingPartPropertiesUpdated(ev, subframe, propSU); } subframe = (ODFrame*)_fEmbeddedFrames->After(subframe); } SOM_CATCH_ALL SOM_RETURN } |
A container should not call ContainingPartPropertiesUpdated on its embedded frames unless it is changing a properties value. It should not call this for a newly-created embed. It is the responsibility of the embed to query its container if it is interested in using its standard properties. To respond to such a query, the container must implement AcquireContainingPartProperties. Such a request must be passed up the hierarchy to the document root part and each frame in the hierarchy must insure that the appropriate inheritance is maintained. An example of this is shown here:
SOM_Scope ODStorageUnit* SOMLINK MyPartAcquireContainingPartProperties( MyPart *somSelf, Environment *ev, ODFrame* embedFrame) { MyPartData *somThis = MyPartGetData(somSelf); MyPartMethodDebug("MyPart","AcquireContainingPartProperties"); SOM_TRY ODStorageUnit* propSU = kODNULL; TempODFrame myFrame = embedFrame->AcquireContainingFrame(ev); // sanity check if (!_fDisplayFrames->Contains(myFrame)) { return kODNULL; } // if we are not the root part, pass the request up the chain if (!myFrame->IsRoot(ev)) { TempODFrame containingFrame = myFrame->AcquireContainingFrame(ev); TempODPart containingPart = containingFrame->AcquirePart(ev); propSU = containingPart->AcquireContainingPartProperties(ev, myFrame); } // If we are the root part OR our container didn't give // us back an SU, create one if (!propSU) { propSU = somSelf->GetStorageUnit(ev)->GetDraft(ev)->CreateStorageUnit(ev); } // default to leave the existing property alone ODBoolean wemadeit = FALSE; // if the std prop isn't there, create it and we write it if (!ODSUExistsThenFocus( ev, propSU, kODBackgroundColor, kODRGB2) ) ODSUForceFocus( ev, propSU, kODBackgroundColor, kODRGB2 ); wemadeit = kODTrue; } // If we created the prop OR we are decoupled // from our container... write our color if (wemadeit || !_fMatchBGCo| ) { StorageUnitSetValue( propSU, ev, sizeof(RGBColor), &_fBGColor); } SOM_CATCH_ALL SOM_RETURN // return the filled storage unit return propSU; } |
An additional note is that it can be beneficial for root containers to maintain values for the standard properties even if they do not use them directly. This would enable a user to make a change of any standard property at the root level and have it propagated throughout the document. For example, a PageLayout component that does not deal directly with text could still maintain the default font for drag and drop and even provide a menu item for changing the value, which could then be propagated to any embedded text-handling components. This would allow the user to change the default font for all text in a layout even though the root component does not handle text itself.
As mentioned above, background transparency should actually be thought of as a property of an embedded display frame that is controlled by its container. Because there is no AcquireEmbeddedPartCapabilities, it is not feasible for a 2-way dialog regarding transparency to occur so the following protocol is recommended.
By default containers and embeds should assume that the embed is opaque (that is, the value of kODBackgroundTransparency is kODFalse). As per the recipe for facet windows, to reduce flicker, the container can use the embed's UsedShape as a clip shape for rendering its own background; the embed has sole responsibility for rendering its area and should fill its background appropriately. If the user selects an embedded frame and changes its background rendering to transparent, the containing part should stop including the embed's UsedShape in its accumulated background clip shape. An example of processing for this is:
// net embed shape for background clipping. collect in WINDOW coords ODShape* contentClip = (ODShape*)facet->GetPartInfo(ev); // clear out any old clip shapes contentClip->Reset(ev); // utility shape TempODShape embedShape = facet->GetFrame(ev)->CreateShape(ev); // iterate over all embedded facets ODFacet* embFacet; ODFacetIterator* facets = facet->CreateFacetIterator(ev, kODChildrenOnly, kODFrontToBack); for (embFacet = facets->First(ev); facets->IsNotComplete(ev); embFacet = facets->Next(ev)) { // get the embed's associated graphics object Proxy* p = somSelf->ProxyForFrame(ev, embFacet->GetFrame(ev)); GtkEmbed* embedObj = (GtkEmbed*)_picture->Object(p->id); // if we got it and it is an embed that should be clipped off... if (embedObj && embedObj->IsEmbed() && !embedObj->IsTransparent()) { // get a copy of the embed's usedShape TempODShape tmpShape = embFacet->GetFrame(ev)->AcquireUsedShape(ev, kODNULL); embedShape->CopyFrom(ev, tmpShape); // convert to window coords xform = embFacet->AcquireWindowFrameTransform(ev, kODNULL); embedShape->Transform(ev, xform); ODReleaseObject(ev, xform); // add it to aggregate contentClip->Union(ev, embedShape); } } delete facets; |
Whenever an area including a transparent embed is invalidated, the container will draw in the background first, before the embed is drawn. In addition to changing its clipping, the container should notify the embed that this property has changed. If the embed implements background transparency, it would then stop drawing its background in its Draw method, and only render its foreground (text or other content). This is implemented in the Draw method as follows:
SOM_Scope void SOMLINK MyPartDraw( MyPart *somSelf, Environment *ev, ODFacet* facet, ODShape* invalidShape) { MyPartData *somThis = MyPartGetData(somSelf); MyPartMethodDebug("MyPart","Draw"); SOM_TRY HPS hpsDraw; ODRect rect; TempODShape tempShape = facet->GetFrame(ev)->AcquireFrameShape(ev, kODNULL); tempShape->GetBoundingBox(ev, &rect); RECTL frameRect; rect.AsRECTL(frameRect); // Set up the drawing facet // Set up drawing environment CFocus f(ev, facet, invalidShape, &hpsDraw); // if we are not transparent, fill our background if (!_fTransparent) { GpiSetColor(hpsDraw, _fContentStruct.fColor); POINTL orig = {0, 0}; GpiMove(hpsDraw, &orig); POINTL pt&l = {frameRect.xRight, frameRect.yTop}; GpiBox(hpsDraw, DRO_FILL, &ptl, 0, 0); } // draw our foreground stuff GpiSetLineType(hpsDraw, LINETYPE_DASHDOUBLEDOT); GpiSetColor(hpsDraw, CLR_BLACK); for (int y = 0; y &< frameRect.yTop; y += YGRID) { ptl.y = y; ptl.x = 0; GpiMove(hpsDraw, &ptl); ptl.x = frameRect.xRight; ptl.y += frameRect.xRight; GpiLine(hpsDraw, &ptl); } for (int x = XGRID; x &< frameRect.xRight; x += XGRID) { ptl.x = x; ptl.y = 0; GpiMove(hpsDraw, &ptl); ptl.x += frameRect.yTop; ptl.y = frameRect.yTop; GpiLine(hpsDraw, &ptl); } SOM_CATCH_ALL SOM_ENDTRY } |
If the embed does not implement background transparency, it will still render its background; the net result will be additional flickering whenever the embed is redrawn. It is clear that this is not an ideal mechanism for such communication. A more appropriate route would be to include transparency as a standard Container Suite OSA SetData property and use OSA for negotiating the cooperative handling. This PartProperties protocol is proposed as an intermediate mechanism for enabling more sophisticated cooperation until the OSA handling is standardized.