Title Banner

Previous Book Contents Book Index Next

Inside Macintosh: OpenDoc Cookbook /
Chapter 2 - SamplePart Tutorial


Drawing the Part

Every part editor must implement its Draw method so it can display the visible portion of its content on demand, in response to the Draw call. Most part editors perform drawing of their content synchronously; that is, they allow OpenDoc to call the Draw method of their part editor object (ODPart subclass). OpenDoc calls the Draw method whenever portions of the document are marked invalid, as when the user scrolls a part's content into view. However, part editors can also draw asynchronously by calling their own Draw method. For example, a part that represents a clock would need to update and redraw its display every second.

To display its content, a part must have at least one frame, and it may have more than one frame, even in a single window. Part editors can display their content in different frames simultaneously, and they can display them differently in the same frame at different times.

During drawing, the part editor is responsible for examining the frame and displaying the correct information in the frame, properly transformed and clipped. If additional information is needed to perform rendering properly, the part editor may store it in the part info field of the frame or the facet.

The proper way to render a part on a particular display device may also vary depending on whether the device is static or dynamic. A part editor can use the isDynamic flag of the canvas object to determine the nature of the interaction style and draw its part accordingly. For example, it may draw scroll bars on a dynamic canvas but omit them for a static one.

The basic steps to perform in drawing are as follows:

  1. Prepare the platform graphics environment for drawing.
  2. Get the view type, the presentation if required, and any other information needed to determine the proper display method, such as selection state, highlight state, and the state of the stationery flag.
  3. Render the content appropriately.
  4. Restore the old graphics environment.

In SamplePart, these steps are accomplished by the Draw method and three subroutine methods to handle the four standard view types: frame, large icon, small icon, and thumbnail. In addition, SamplePart has methods that prepare for drawing and handle various other situations affecting imaging behavior. The following sections discuss these methods.

The Draw Method

OpenDoc calls the part object's Draw method whenever a facet of a part's display frame intersects the invalidated portion of an OpenDoc window. Parts may call their own Draw method whenever their content needs to be rendered onto a canvas.

The SamplePart object's implementation of the Draw method performs the following actions:

  1. Focuses the Mac OS drawing environment.
    The SamplePart object's Draw method focuses the Mac OS QuickDraw port, origin, and clip shape for drawing in the facet passed into the Draw method. SamplePart accomplishes this by instantiating a stack-based object (here named initiateDrawing) of class CFocus, which is defined in the OpenDoc utility file FocusLib.cpp. The CFocus constructor saves the old port, origin, and clip shape and sets the new ones properly. At the end of the Draw method, when control passes out of its scope, the CFocus object is automatically deleted, and its destructor restores the port, origin, and clip shape previously in force.

  2. Gets the view type of the frame to which the current facet belongs.
    The method gets a pointer to the frame from the facet, a pointer to which is passed in from OpenDoc with the Draw call. The frame is queried for its view type.

  3. Draws the part's content appropriately for the view type.
    SamplePart has a separate method for each view type that can draw its content properly, and it branches to the appropriate one.

Listing 2-15 shows the implementation of the Draw method.

Listing 2-15 Draw method

void SamplePart::Draw( Environment*   ev,
                       ODFacet*       facet,
                       ODShape*       invalidShape )
{
    SOM_Trace("SamplePart","Draw");

   CFocus initiateDrawing(ev, facet, invalidShape);
   
   ODTypeToken view = facet->GetFrame(ev)->GetViewType(ev);
   
   if ( view == gGlobals->fLargeIconView || view == gGlobals->fSmallIconView )
      this->DrawIconView(ev, facet);
   else if ( view == gGlobals->fThumbnailView )
      this->DrawThumbnailView(ev, facet);
   else
      this->DrawFrameView(ev, facet);
}

The DrawIconView Method

The SamplePart object's internal DrawIconView method draws an appropriate version of the frame's icon.

The DrawIconView method performs the following actions:

  1. Sets the icon transform type.
    The method checks the facet's highlight state. If the facet is highlighted, the method will display the selected version of the icon. If a part window exists, it will display the version of the icon indicating that it is also open.

  2. Draws the icon.
    The method sets the size of the rectangle in which to display the icon correctly and calls the Mac OS Toolbox routine PlotIconID to draw the correct version of the icon according to its icon transform type. Large icons are drawn in a 32-by-32-pixel rectangle; small icons are drawn in a 16-by-16-pixel rectangle.

Listing 2-16 shows the implementation of the DrawIconView method.

Listing 2-16 DrawIconView method

void SamplePart::DrawIconView( Environment*  ev,
                               ODFacet*      facet )
{
    SOM_Trace("SamplePart","DrawIconView");

   Rect               iconRect;
   IconTransformType  transformType = ttNone;
   CFrameInfo*        frameInfo;
   ODFrame*           frame; 
   ODTypeToken        viewType;
   
   frame = facet->GetFrame(ev);
   viewType= frame->GetViewType(ev);
   frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
   
   if ( facet->GetHighlight(ev) == kODFullHighlight )
      transformType = ttSelected;
   
   if ( frameInfo->HasPartWindow() &&       frameInfo->GetPartWindow()->IsShown(ev) )
      transformType |= ttOpen;
   
   if ( viewType == gGlobals->fLargeIconView )
      SetRect(&iconRect, 0, 0, kODLargeIconSize, kODLargeIconSize);
   else // ( viewType == gGlobals->fSmallIconView )
      SetRect(&iconRect, 0, 0, kODSmallIconSize, kODSmallIconSize);

   CUsingLibraryResources res;
   PlotIconID(&iconRect, atAbsoluteCenter, transformType, kBaseResourceID);
}

The DrawThumbnailView Method

Normally, a thumbnail view of a frame is a 64-by-64-pixel representation of its actual content. However, SamplePart has no intrinsic content, so its DrawThumbnailView method simply displays a 'PICT' resource, a handle to which was previously stored in the fThumbnail field of the part's global variable structure. The same strategy is appropriate for parts that have been newly created from stationery and have no content yet. When the user has added content a "real" thumbnail can be created.

The DrawThumbnailView method performs the following actions:

  1. Sets the bounding rectangle of the thumbnail.
    The method retrieves the bounding rectangle of the thumbnail picture resource by dereferencing its handle and sets the drawing offset accordingly.

  2. Draws the picture.
    The method calls the QuickDraw routine DrawPicture to draw the picture resource at the proper position.

Listing 2-17 shows the implementation of the DrawThumbnailView method.

Listing 2-17 DrawThumbnailView method

void SamplePart::DrawThumbnailView( Environment*ev,
                           ODFacet*/*facet*/ )
{
    SOM_Trace("SamplePart","DrawThumbnailView");

   LoadThumbnail(ev, &gGlobals->fThumbnail);
   
   Rect bounds = (**(PicHandle) gGlobals->fThumbnail).picFrame;
   
   OffsetRect(&bounds, -bounds.left, -bounds.top);
   DrawPicture((PicHandle) gGlobals->fThumbnail, &bounds);
}

The DrawFrameView Method

The implementation of the SamplePart object's internal DrawFrameView method (called by the part's Draw method) renders the full content view of the part when the view type is frame. SamplePart has no intrinsic content; the frame view simply draws two text strings with stylistic variations.

The DrawFrameView method performs the following actions:

  1. Gets the facet's frame and canvas, the frame shape, and the QuickDraw region of the frame area.
    The method uses the facet passed as a parameter to get the frame. If the frame has a source frame, the method uses the source frame instead. The method gets a reference to the facet's canvas, which represents its underlying platform-specific drawing system (QuickDraw), so that the frame shape is returned in the correct coordinate system. The method then gets the shape and QuickDraw region of the frame.

  2. Sets the font characteristics.
    The method calculates the height of the frame and sets the font size to 80% of the frame height. Then it sets the font to be the default application font for the current script system and sets its variation to be bold and condensed.

  3. Gets the text string to be drawn.
    Before acquiring the string resource to draw, you must set up the resource chain so the resources contained in your dynamic library are available. This is handled in the DrawFrameView method by the OpenDoc utility routine BeginUsingLibraryResources, which is defined in the file UseRsrcM.cpp. At this point the method saves the QuickDraw pen state and resets it to normal, acquires the individual string from the resource, and moves the pen to an appropriate baseline position in preparation for drawing the text.

  4. Draws the text.
    The method calls the QuickDraw routine DrawString to render a text string acquired from its string resource onto the screen, using the font characteristics calculated previously. If the facet's highlight state is kODFullHighlight, indicating that the part is selected, the method fills in the background of the drawing port with the highlight color. The method then draws another text string acquired from its string resource, this time at the fixed point size of 24 points, centered in the frame, and in color reversed from its background.

  5. Restores the resource chain and port characteristics.
    The method calls EndUsingLibraryResources to restore the resource chain as configured prior to calling BeginUsingLibraryResources. You must call the ending routine if you have called the beginning routine, so you must not throw an exception between the two calls. If an exception is likely, therefore, you should save it and throw it after calling EndUsingLibraryResources. Last, the method restores the QuickDraw graphics port and resets its text font, size, and variation.

Listing 2-18 shows the implementation of the DrawFrameView method.

Listing 2-18 DrawFrameView method

void SamplePart::DrawFrameView( Environment*  ev,
                                ODFacet*      facet )
{
    SOM_Trace("SamplePart","DrawFrameView");

   ODFrame*      frame;
   ODUShort      frameHeight = 0;
   ODUShort      frameWidth = 0;
   RgnHandle     frameRgn;
   FontInfo      finfo;
   Str63         defaultString;
   CFrameInfo*   frameInfo;
   GrafPtr       port;


   GetPort(&port);
   EraseRect(&port->portRect);
   
   frameInfo = (CFrameInfo*) facet->GetFrame(ev)->GetPartInfo(ev);
   if ( frameInfo->HasSourceFrame() )
      frame = frameInfo->GetSourceFrame(ev);
   else
      frame = facet->GetFrame(ev);

   ODCanvas* biasCanvas = facet->GetCanvas(ev);

   TempODShape frameShape = frame->AcquireFrameShape(ev, biasCanvas);
   frameRgn = frameShape->GetQDRegion(ev);
   frameHeight = (**frameRgn).rgnBBox.bottom - (**frameRgn).rgnBBox.top;
   frameWidth = (**frameRgn).rgnBBox.right - (**frameRgn).rgnBBox.left;

   ODUShort size = port->txSize;
   ODUShort font = port->txFont;
   Style face = port->txFace;

   TextSize((ODUShort)(frameHeight * 0.8));
   TextFont(1);
   TextFace(bold + condense);
      
   GetFontInfo(&finfo);
   
   ODSLong rfRef;
   rfRef = BeginUsingLibraryResources();
   {
      PenState penState;
      GetPenState(&penState);
      
      PenNormal();
      GetIndString(defaultString, kMenuStringResID, kDefaultContent1ID);
      MoveTo((frameWidth / 2) - (StringWidth(defaultString) / 2),
               frameHeight - (finfo.descent - 2));
      DrawString(defaultString);
      
      if ( facet->GetHighlight(ev) == kODFullHighlight )
      {
         UInt8 mode = LMGetHiliteMode();
         BitClr(&mode,pHiliteBit);
         LMSetHiliteMode(mode);
         InvertRect(&port->portRect);
      }
      
      TextMode(srcXor);
      TextSize(24);
      TextFace(bold + extend);

      GetIndString(defaultString, kMenuStringResID, kDefaultContent2ID);
      MoveTo((frameWidth / 2) - (StringWidth(defaultString) / 2),
               (frameHeight / 2) + 6);
      DrawString(defaultString);
      
      SetPenState(&penState);
   }
   EndUsingLibraryResources(rfRef);

   // Restore port chararcteristics.
   SetPort(port);
   port->txSize = size;
   port->txFont = font;
   port->txFace = face;
}

The ViewTypeChanged Method

OpenDoc calls a part's ViewTypeChanged method when the view type of one of the part's display frames has been modified, such as when the user changes the view type in the Part Info dialog box. In general, the ViewTypeChanged method should set up the facilities needed for a part to display itself with a particular view type.

The SamplePart object's implementation of the ViewTypeChanged method performs the following actions:

  1. Gets the view type of the frame.
    For this purpose, the ODFrame class provides a GetViewType method, which returns a tokenized string representing the view type.

  2. If thumbnail is the view type, prepares the thumbnail view.
    In this case, ViewTypeChanged calls the SamplePart object's internal method GenerateThumbnail. The GenerateThumbnail method creates a 64-by-64-pixel representation of the current display frame. In SamplePart, this method calls the SamplePart utility method LoadThumbnail, which simply returns a handle to a preexisting 'PICT' resource. The method puts a pointer to the thumbnail into the global variables structure. If the GenerateThumbnail method is unable to load the resource for some reason, it defaults to the regular frame view and throws the error returned by the Resource Manager as an exception (or, if there is no Resource Manager error, the method throws the resNotFound error).

  3. Changes the frame's used shape to match the new view type.
    The method calls the SamplePart object's internal method CalcNewUsedShape to calculate the appropriate used shape for the new view type. If the view type is frame view, the CalcNewUsedShape method intentionally returns a null used shape, which resets the used shape to exactly the frame shape. Otherwise, the CalcNewUsedShape method returns a used shape equal to the appropriate icon or thumbnail view.

  4. Invalidates the frame.
    The ViewTypeChanged method invalidates the frame, calls the frame's ChangeUsedShape method with the new used shape, and then invalidates the frame again.

Listing 2-19 shows the implementation of the ViewTypeChanged method, Listing 2-20 shows the GenerateThumbnail method, Listing 2-21 shows the LoadThumbnail method, and Listing 2-22 shows the CalcNewUsedShape method.

Listing 2-19 ViewTypeChanged method

void SamplePart::ViewTypeChanged( Environment*  ev,
                                  ODFrame*      frame )
{
   SOM_Trace("SamplePart","ViewTypeChanged");

   ODTypeTokenview = frame->GetViewType(ev);
   
   if ( view == gGlobals->fThumbnailView )
      this->GenerateThumbnail(ev, frame);
         
   TempODShape newUsedShape = this->CalcNewUsedShape(ev, frame);

   frame->Invalidate(ev, kODNULL, kODNULL);
   frame->ChangeUsedShape(ev, newUsedShape, kODNULL);
   frame->Invalidate(ev, kODNULL, kODNULL);
}
Listing 2-20 GenerateThumbnail method
void SamplePart::GenerateThumbnail( Environment*  ev,
                                    ODFrame*      frame )
{
   SOM_Trace("SamplePart","GenerateThumbnail");
   
   LoadThumbnail(ev, &gGlobals->fThumbnail);

   if ( gGlobals->fThumbnail == kODNULL )
   {
      frame->ChangeViewType(ev, gGlobals->fFrameView);
      THROW_IF_ERROR((ODError)ResError());
      THROW(resNotFound);
   }
}
Listing 2-21 LoadThumbnail method
void LoadThumbnail(Environment* ev, Handle* thumbnail)
{
   if ( *thumbnail ) return;
   
   ODSLong rfRef;
   rfRef = BeginUsingLibraryResources();
   {
      *thumbnail = (Handle) GetPicture(kThumbnailPicture);
   }
   EndUsingLibraryResources(rfRef);
}
Listing 2-22 CalcNewUsedShape method
ODShape* SamplePart::CalcNewUsedShape( Environment*   ev,
                                       ODFrame*       frame )
{
   SOM_Trace("SamplePart","CalcNewUsedShape");

   ODShape* usedShape = kODNULL;   ODVolatile(usedShape);
   RgnHandle usedRgn;              ODVolatile(usedRgn);
   
   ODTypeToken view = frame->GetViewType(ev);

   if ( view == gGlobals->fLargeIconView ||
         view == gGlobals->fSmallIconView ||
         view == gGlobals->fThumbnailView )
   {
      TRY
         Rect bounds;
         usedRgn = ODNewRgn();

         if ( view == gGlobals->fLargeIconView || view == gGlobals->fSmallIconView )
         {
            CUsingLibraryResources res;

            SetRect(&bounds, 0, 0,
                  (view == gGlobals->fLargeIconView) ? 
                                 kODLargeIconSize : kODSmallIconSize,
                  (view == gGlobals->fLargeIconView) ? 
                                 kODLargeIconSize : kODSmallIconSize);

            THROW_IF_ERROR( IconIDToRgn(usedRgn, &bounds, 
                                    atAbsoluteCenter, kBaseResourceID) );
         }
         else if ( view == gGlobals->fThumbnailView )
         {  
            bounds = (**(PicHandle)gGlobals->fThumbnail).picFrame;
            RectRgn(usedRgn,&bounds);
         }
         usedShape = frame->CreateShape(ev);
         usedShape->SetQDRegion(ev, usedRgn);
                     
      CATCH_ALL
         ODSafeReleaseObject(usedShape);
         ODDisposeHandle((ODHandle)usedRgn);
         usedShape = kODNULL;
      ENDTRY
   }
   return usedShape;
}

The GeometryChanged Method

OpenDoc calls the GeometryChanged method when the external transform or clip shape of a facet belonging to the part's display frame changes. The only action of the SamplePart object's implementation of the method is to invalidate the clip shape of the facet, causing it to be redrawn.

Listing 2-23 shows the implementation of the GeometryChanged method.

Listing 2-23 GeometryChanged method

void SamplePart::GeometryChanged( Environment*   ev,
                                  ODFacet*       facet,
                                  ODBoolean      clipShapeChanged,
                                  ODBoolean      /* externalTransformChanged */ )
{
   SOM_Trace("SamplePart","GeometryChanged");

   if ( clipShapeChanged )
      facet->Invalidate(ev, kODNULL, kODNULL);
}

The HighlightChanged Method

OpenDoc calls a part's HighlightChanged method when the highlight state of one of its display frame's facets changes. The method is responsible for redrawing the facet's content with highlighting that is consistent with that of the containing part in which this part is embedded.

The SamplePart object's implementation of the HighlightChanged method gets a reference to the facet's frame, then (if its view type is not a frame view) simply invalidates the frame, causing its content to be redrawn. If the frame's view type is a frame view, the method does nothing.

Listing 2-24 shows the implementation of the HighlightChanged method.

Listing 2-24 HighlightChanged method

void SamplePart::HighlightChanged(Environment* ev, ODFacet* facet)
{
   ODFrame* frame = facet->GetFrame(ev);
   
   if ( frame->GetViewType(ev) != gGlobals->fFrameView )
      frame->Invalidate(ev, kODNULL, kODNULL);
}

The FacetAdded Method

OpenDoc calls the FacetAdded method when the containing part (or OpenDoc) adds a facet to one of the part's display frames. The part's basic responsibility in response to the FacetAdded method call is to prepare to draw the content visible in the new facet.

The SamplePart object's implementation of FacetAdded retrieves the facet's frame and the frame's info object. If the facet's frame is the root frame of a window, the method marks the frame for activation whenever the window is selected.

Finally, the method handles the possibility of the frame having a hidden part window. If the frame had become invisible previously, it would have hidden any part window it had. Therefore, the method checks to see if this is the first facet added to the frame, indicating that it is just becoming visible; if so, and if the frame has a part window, the method shows the part window.

Listing 2-25 shows the implementation of the FacetAdded method.

Listing 2-25 FacetAdded method

void SamplePart::FacetAdded( Environment*   ev,
                             ODFacet*       facet )
{
   SOM_Trace("SamplePart","FacetAdded");

   ODFrame* frame = facet->GetFrame(ev);
   CFrameInfo* frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);
      
   if ( frame->IsRoot(ev) )
   {
      frameInfo->SetActiveFacet(facet);
      frameInfo->SetFrameReactivate(kODTrue);
   }
   if ( (CountFramesFacets(ev, frame) == 1) )
   {
      TempODWindow window = frameInfo->AcquirePartWindow(ev);
      if ( window ) window->Show(ev);
   }
}

The FacetRemoved Method

OpenDoc calls a part's FacetRemoved method when the containing part or OpenDoc removes a facet from one of this part's display frames.

The SamplePart object's implementation of FacetRemoved retrieves the facet's frame, and, if the frame indicates that this is the active facet, the method marks that indication false. Finally, if the facet being removed is the last facet belonging to its frame, and if its containing frame is null, the method hides the frame's part window, if it has one.

Listing 2-26 shows the implementation of the FacetRemoved method.

Listing 2-26 FacetRemoved method

void SamplePart::FacetRemoved( Environment*   ev,
                               ODFacet*       facet )
{
   SOM_Trace("SamplePart","FacetRemoved");

   ODFrame* frame = facet->GetFrame(ev);
   TempODFramecontainingFrame = frame->AcquireContainingFrame(ev);
   CFrameInfo*frameInfo = (CFrameInfo*) frame->GetPartInfo(ev);

   if ( ODObjectsAreEqual(ev, frameInfo->GetActiveFacet(), facet) )
      frameInfo->SetActiveFacet(kODNULL);

   if ( (CountFramesFacets(ev, frame) == 0) &&         (containingFrame == kODNULL) )
   {
      TempODWindow window = frameInfo->AcquirePartWindow(ev);
      if ( window ) window->Hide(ev);
   }
}

Previous Book Contents Book Index Next

© Apple Computer, Inc.
16 JUL 1996




Navigation graphic, see text links

Main | Page One | What's New | Apple Computer, Inc. | Find It | Contact Us | Help