home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Sample Controls / Label / CLabelControl.cp next >
Encoding:
Text File  |  1996-12-20  |  55.2 KB  |  1,551 lines  |  [TEXT/CWIE]

  1. #include "ocheaders.h"
  2. #include "BDDISPIDs.h"
  3. #include "CBaseControl.h"
  4. #include "CErrorControl.h"
  5. #include "CLabelControl.h"
  6. #include "BDConsts.h"
  7. #include "Colors.h"
  8. #include "FnAssert.h"
  9. #include "dispatch.h"
  10. #include <LArray.h>
  11. #include "CConnectionPoint.h"
  12. #include "CCPContainer.h"
  13. #include "CError.h"
  14. #include "CLabelError.h"
  15. #include "BDUtils.h"
  16. #include "rotate.h"
  17. #include <ctype.h>
  18. #include <math.h>
  19. #include <stdio.h>
  20.  
  21. ///////////////////////////////////////////////////////////////////////////////
  22. //
  23. //  CLabelControl::CLabelControl
  24. //
  25. //    Constructor
  26. //
  27.  
  28. CLabelControl::CLabelControl(void) : CBaseControl()
  29. {
  30.     const Rect nullRect = { 0, 0, 0, 0 };
  31.     
  32. // protected:
  33.     mMainOffscreenGWorld     = NULL;
  34.     mPixOffscreenPixMap     = NULL;
  35.     mRectOffscreenBounds     = nullRect;
  36.  
  37. // user spec'd properties:
  38.     mAngle                    = 0;
  39.     mAlignment                = 4;
  40.     mBackStyle                = 1;
  41.     mBackColor                = RGB_GRAY;
  42.     strcpy(mCaption, "Default String");
  43.     strcpy(mFontName, "Times");
  44.     mFontBold                = 0;
  45.     mFontItalic            = 0;
  46.     mFontUnderline            = 0;
  47.     mFontStrikeout            = 0;
  48.     mFontSize                = 18;
  49.     mForeColor                = RGB_BLACK;
  50.     mMode                    = 0;
  51.     mRotateBy                = 0; // don't rotate when clicked, by default
  52.  
  53.        strcpy((char*) mID[1], "Label");
  54.        mID[0] = strlen("Label");
  55.  
  56.     CCPContainer*        containerObj = nil;
  57.  
  58.     // Create the new connection point container object
  59.     containerObj = new CCPContainer(NUM_CONNECTIONS);
  60.     
  61.     // Snag the interface pointer
  62.     if (containerObj)
  63.         containerObj->QueryInterface(IID_IConnectionPointContainer, &mCPContainerP);
  64.     
  65.     // if we have a container object, allocate the connection points
  66.     if (mCPContainerP)
  67.         // We support 1 connection point
  68.         containerObj->AddConnectionPoint(IID_IDoMenuEvents);
  69. }
  70.  
  71. ///////////////////////////////////////////////////////////////////////////////
  72. //
  73. //  CLabelControl::~CLabelControl
  74. //
  75. //    Destructor
  76. //
  77.  
  78. CLabelControl::~CLabelControl(void)
  79. {    
  80.     // clean up offscreen world
  81.     if (mMainOffscreenGWorld != NULL)
  82.     {
  83.         ::DisposeGWorld(mMainOffscreenGWorld);
  84.         mMainOffscreenGWorld = NULL;
  85.         mPixOffscreenPixMap = NULL;
  86.     }
  87. }
  88.  
  89.  
  90. ///////////////////////////////////////////////////////////////////////////////
  91. //
  92. //  CLabelControl::IUnknown::QueryInterface
  93. //
  94. //  Returns a pointer to the specified interface on a component to which a
  95. //  client currently holds an interface pointer.
  96. //
  97.  
  98. STDMETHODIMP
  99. CLabelControl::QueryInterface(REFIID inRefID, void** outObj)
  100. {
  101.     HRESULT resultCode = ResultFromScode(E_NOINTERFACE);    // init it cause I'm anal
  102.     
  103.     if (inRefID == IID_IBindStatusCallback) 
  104.     {
  105.         resultCode = CBaseBindStatusCallback::QueryInterface(inRefID, outObj);
  106.     }
  107.     
  108.     else if (inRefID == IID_IDoMenuEvents) // an outgoing interface
  109.     {
  110.         *outObj = (void*) (IDoMenuEvents*) this;
  111.         AddRef();
  112.         
  113.         resultCode = ResultFromScode(S_OK);
  114.     }
  115.     else
  116.     {
  117.         resultCode = CBaseControl::QueryInterface(inRefID, outObj);
  118.     }
  119.     return resultCode;
  120. }
  121.  
  122.  
  123. ///////////////////////////////////////////////////////////////////////////////
  124. //
  125. //  CLabelControl::IControl::Draw
  126. //
  127. //    Context            the drawing context for ActiveX - includes the port
  128. //
  129. //    Draw the label object.
  130. //
  131.  
  132. STDMETHODIMP
  133. CLabelControl::Draw(THIS_ DrawContext* Context)
  134. {
  135.     if (Context->DrawAspect != DVASPECT_CONTENT)
  136.         return ResultFromScode(DV_E_DVASPECT);
  137.         
  138.     this->PrepareOffscreenWorld(Context);    // get the main offscreen world setup and ready
  139.  
  140.     // get the offscreen bitmap
  141.     BitMap* offscreenBitMap = this->GetOffscreenBitMap();
  142.     
  143.     // set the user selectable font properties
  144.     this->SetFontParams(mMainOffscreenGWorld);
  145.  
  146.     // draw the text in the label
  147.     this->RenderLabelControl();
  148.     
  149.     // Move it
  150.     this->MoveOnScreen(offscreenBitMap, Context);
  151.     
  152.     // unlock, and unload
  153.     this->ReleaseOffscreenBitMap(offscreenBitMap);
  154.  
  155.     return ResultFromScode(S_OK);
  156. }
  157.  
  158. ///////////////////////////////////////////////////////////////////////////////
  159. //
  160. //  CLabelControl::IControl::DoMouse
  161. //
  162.  
  163. STDMETHODIMP CLabelControl::DoMouse(THIS_ MouseEventType inMouseET, PlatformEvent* inEvent)
  164. {
  165.     switch (inMouseET) 
  166.     {
  167.         case MouseDown:
  168.         {
  169.             //    If the user has set a rotateby parameter, then rotate when we click.
  170.             if ( mRotateBy != 0 )
  171.             {
  172.                 // adjust the rotation angle -- we only handle (0 <= mRotateBy <= 360)
  173.                 mAngle += mRotateBy;
  174.                 mAngle %= fullRevolution;    // Limit it to less than a revolution
  175.                 
  176.                 // get the current drawing environment
  177.                 DrawContext context = {(PortType) 0};
  178.                 mContainerSiteP->AcquireContext(mActiveContext->GetContextID(), &context);    
  179.                 
  180.                 // draw
  181.                 Draw(&context);
  182.             
  183.                 // reset the drawing environment
  184.                 mContainerSiteP->ReleaseContext(&context);
  185.             }
  186.             break;
  187.         }
  188.                 
  189.         case MouseEnter:
  190.         {
  191.             FireEvent(IID_IDoMenuEvents, DISPID_POPUP, inEvent);
  192.             break;
  193.         }
  194.  
  195.         case MouseUp:
  196.             //    We don't do anything for a mouse up event, since we handle 
  197.             //    it with the mouse down.
  198.             break;
  199.     }
  200.  
  201.     return ResultFromScode(S_OK);
  202. }
  203.  
  204. ///////////////////////////////////////////////////////////////////////////////
  205. //
  206. //  CLabelControl::IPersistPropertyBag::Load
  207. //
  208. //    PropBag            container that holds all the properties passed in
  209. //    ErrorLog        ptr to an error log
  210. //
  211. //    Sets the data members that represent the user definable properties from the URL.
  212. //
  213.  
  214. STDMETHODIMP CLabelControl::Load(IPropertyBag* PropBag, IErrorLog* ErrorLog)
  215. {
  216.     SCODE theSCode = LoadTextState(PropBag, ErrorLog);
  217.     return ResultFromScode(theSCode);
  218. }
  219.  
  220. ///////////////////////////////////////////////////////////////////////////////
  221. //
  222. // CLabelControl::IPersistPropertyBag::LoadTextState
  223. //
  224. //    pPropertyBag    container that holds all the properties passed in
  225. //    pErrorLog        ptr to an error log
  226. //
  227. //    Sets the data members that represent the user definable properties from the container stream.
  228. //
  229.  
  230. STDMETHODIMP CLabelControl::LoadTextState(IPropertyBag *pPropertyBag,IErrorLog *pErrorLog)
  231. {
  232.     const long    maxLength = 256;
  233.     char        propertyString[MAX_PROPERTY_STRING_LENGTH];
  234.  
  235.     // try to load in each property.  if we can't get it, then leave
  236.     // things at the default.
  237.     
  238.     // The rotation angle
  239.     if (::LoadPropertyString(pPropertyBag, "angle", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  240.         mAngle = atoi(propertyString);
  241.         mAngle %= fullRevolution;    // limit to: 0 <= mAngle <= 360
  242.  
  243.     // The caption alignment
  244.     if (::LoadPropertyString(pPropertyBag, "alignment", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  245.         mAlignment = atoi(propertyString);
  246.  
  247.     // The backgroung property
  248.     if (::LoadPropertyString(pPropertyBag, "backstyle", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  249.         mBackStyle = atoi(propertyString);
  250.  
  251.     // The background color
  252.     if (::LoadPropertyString(pPropertyBag, "backcolor", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  253.         this->LoadColor(&mBackColor, propertyString);
  254.     
  255.     // The caption string
  256.     if (::LoadPropertyString(pPropertyBag, "caption", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  257.         strcpy(mCaption, propertyString);
  258.  
  259.     // The fontname string
  260.     if (::LoadPropertyString(pPropertyBag, "fontname", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  261.         strcpy(mFontName, propertyString);
  262.  
  263.     // Is the caption font bold style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
  264.     if (::LoadPropertyString(pPropertyBag, "fontbold", propertyString, maxLength, pErrorLog))
  265.         mFontBold = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
  266.  
  267.     // Is the caption font italic style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
  268.     if (::LoadPropertyString(pPropertyBag, "fontitalic", propertyString, maxLength, pErrorLog))
  269.         mFontItalic = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
  270.  
  271.     // Is the caption font underline style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
  272.     if (::LoadPropertyString(pPropertyBag, "fontunderline", propertyString, maxLength, pErrorLog))
  273.         mFontUnderline = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
  274.  
  275.     // Is the caption font strikeout style? -- 1, y, or Y means "YES" (anything else, i.e. 0, n, or N means "NO")
  276.     if (::LoadPropertyString(pPropertyBag, "fontstrikeout", propertyString, maxLength, pErrorLog))
  277.         mFontStrikeout = ((propertyString[0] == '1') || (propertyString[0] == 'y')|| (propertyString[0] == 'Y'));
  278.  
  279.     // The font size
  280.     if (::LoadPropertyString(pPropertyBag, "fontsize", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  281.         mFontSize = atoi(propertyString);
  282.  
  283.     // The foreground color
  284.     if (::LoadPropertyString(pPropertyBag, "forecolor", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  285.         this->LoadColor(&mForeColor, propertyString);
  286.  
  287.     // The text rendering mode
  288.     if (::LoadPropertyString(pPropertyBag, "mode", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  289.         mMode = atoi(propertyString);
  290.  
  291.     // Rotate on click mode.
  292.     if (::LoadPropertyString(pPropertyBag, "rotateby", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  293.         mRotateBy = atoi(propertyString);
  294.     
  295.     // The object name of the label
  296.     if (::LoadPropertyString(pPropertyBag, "sourceobjecttag_", propertyString, MAX_PROPERTY_STRING_LENGTH, pErrorLog))
  297.     {        
  298.         strcpy((char*)(&mID[1]), propertyString);
  299.         mID[0] = strlen(propertyString);
  300.     }
  301.  
  302.     return ResultFromScode(S_OK);
  303. }  
  304.  
  305. ///////////////////////////////////////////////////////////////////////////////
  306. //
  307. //  CLabelControl::FireEvent
  308. //
  309.  
  310. STDMETHODIMP
  311. CLabelControl::FireEvent(REFIID RefID, long EventID, PlatformEvent* Event)
  312. {
  313.     IEnumConnectionPoints*    enumCP;
  314.     IEnumConnections*        enumC;
  315.     CONNECTDATA                connectData;
  316.     IUnknown*                eventTarget;
  317.     IConnectionPoint*        connectionPoint;
  318.     
  319.     // Get an enumerator for the connection points
  320.     if (SUCCEEDED(mCPContainerP->EnumConnectionPoints(&enumCP)))
  321.     {
  322.         // Loop through all the connection points for this control
  323.         while (enumCP->Next(1, &connectionPoint, nil) == NOERROR)
  324.         {
  325.             // Get all the connections for this connection point
  326.             if (SUCCEEDED(connectionPoint->EnumConnections(&enumC)))
  327.             {
  328.                 // Loop through all the connections for this connection point    
  329.                 while (enumC->Next(1, &connectData, nil) == NOERROR)
  330.                 {
  331.                     // Get the interface implementation for this connection
  332.                     // if successful, fire the event
  333.                     if (SUCCEEDED(connectData.pUnk->QueryInterface(RefID, (void**) &eventTarget)))
  334.                         FireOneEvent(RefID, EventID, eventTarget, Event);
  335.                 }
  336.                 
  337.                 // Release the enumerator
  338.                 enumC->Release();
  339.             }
  340.         }
  341.         
  342.         enumCP->Release();
  343.     }
  344.     
  345.     return ResultFromScode(S_OK);
  346. }
  347.  
  348. ///////////////////////////////////////////////////////////////////////////////
  349. //
  350. //  CLabelControl::CEventSender::FireOneEvent
  351. //
  352.  
  353. STDMETHODIMP
  354. CLabelControl::FireOneEvent(REFIID /*RefID*/, long EventID, IUnknown* EventTarget, PlatformEvent* Event)
  355. {
  356.     IDoMenuEvents*        popTarget = (IDoMenuEvents*) EventTarget;
  357.     IUnknown*                    unk;
  358.     
  359.     this->QueryInterface(IID_IUnknown, (void**) &unk);
  360.     
  361.     switch (EventID)
  362.     {
  363.         case DISPID_POPUP:
  364.             popTarget->Popup(unk, Event);
  365.             break;
  366.     }
  367.     
  368.     return ResultFromScode(S_OK);
  369. }
  370.  
  371. ///////////////////////////////////////////////////////////////////////////////
  372. //
  373. // CLabelControl::LoadColor
  374. //
  375. //
  376. //    theColor        an RGBColor that defines the color tuple exactly (i.e., not indexed)
  377. //    colorString        a string with a color in it repesented in ASCII
  378. //
  379. //    Sets the color data member based on the value of the user definable property.
  380. //
  381.  
  382. Boolean CLabelControl::LoadColor(RGBColor * theColor, char * colorString)
  383. {
  384.     Boolean didSet = true;
  385.     
  386.     for (short i = 0; i < strlen(colorString); i++)
  387.         tolower(colorString[i]);
  388.     
  389.     if (streq(colorString, "black"))
  390.         *theColor = RGB_BLACK;
  391.     else if (streq(colorString, "white"))
  392.         *theColor = RGB_WHITE;
  393.     else if (streq(colorString, "red"))
  394.         *theColor = RGB_RED;
  395.     else if (streq(colorString, "green"))
  396.         *theColor = RGB_GREEN;
  397.     else if (streq(colorString, "blue"))
  398.         *theColor = RGB_BLUE;
  399.     else if (streq(colorString, "cyan"))
  400.         *theColor = RGB_CYAN;
  401.     else if (streq(colorString, "magenta"))
  402.         *theColor = RGB_MAGENTA;
  403.     else if (streq(colorString, "yellow"))
  404.         *theColor = RGB_YELLOW;
  405.     else
  406.     {
  407.         didSet = false;    // reverse Boolean logic -- false unless we succeed
  408.         
  409.         // We're expecting a 32-bit hex RGB value in the format #00bbggrr.  
  410.         //
  411.         // NOTE: To be compatible with Windows, if the high-order bit is set,
  412.         // the low-order byte is supposed to be treated as a system color index.
  413.         // We'll punt on this for now.
  414.         
  415.         Boolean punt = false;
  416.         
  417.         // sanity check on string format.  #bbggrr, #0bbggrr, or #00bbggrr.
  418.         int len = strlen(colorString);
  419.         if ((colorString[0] == '#') && (len >= 7) && (len <= 9))
  420.         {
  421.             // if len == 9, the input may be trying to set the high-order
  422.             // bit.  Check for this and punt if so.
  423.             if (len == 9)
  424.             {
  425.                 char * sHi = "0x??";
  426.                 short iHi = 0;
  427.                 
  428.                 strncat(sHi, colorString, 2);
  429.                 sscanf(sHi, "%hx", &iHi);
  430.                 
  431.                 punt = iHi & 0x40;
  432.             }
  433.                 
  434.             if (! punt) // if we're NOT setting hi-order bit...
  435.             {
  436.                 const int mostSignificantBits = 8;     // amount to shift to move a byte
  437.                 const int numComponents = 3;        // number of array components - one ea for R,G & B
  438.                 const int redPos = 2;                // array position for component
  439.                 const int greenPos = 1;                // array position for component
  440.                 const int bluePos = 0;                // array position for component
  441.  
  442.                 unsigned short desiredColorArray[numComponents]; // a COLORREF, as an array
  443.                 
  444.                 for (short i = 0, pos = len-2; i < numComponents; i++, pos -= 2)
  445.                 {
  446.                     char * sColor = "0x??";
  447.                     short aShort = 0;
  448.                     strncpy(&sColor[2], &colorString[pos], 2);
  449.                     sscanf(sColor, "%hx", &aShort);
  450.                     ASSERT((aShort <= 0xff), "Range check error!");
  451.                     desiredColorArray[i] = aShort << mostSignificantBits;// move the color component value into the ms bits
  452.                 }
  453.                 
  454.                 // return the color that we went to all that trouble to set
  455.                 theColor->red = (unsigned short) desiredColorArray[redPos];
  456.                 theColor->green = (unsigned short) desiredColorArray[greenPos];
  457.                 theColor->blue = (unsigned short) desiredColorArray[bluePos];
  458.  
  459.                 // success, so tell the world
  460.                 didSet = true;
  461.             }
  462.         } 
  463.     }
  464.         
  465.     return didSet;
  466. }
  467.  
  468. ///////////////////////////////////////////////////////////////////////////////
  469. //
  470. //  CLabelControl::PrepareOffscreenWorld
  471. //
  472. //    Context            the drawing context for ActiveX - includes the port
  473. //
  474. //  Creates the offscreen gworld that is the current display.  
  475. //
  476.  
  477. void CLabelControl::PrepareOffscreenWorld(const DrawContext* Context)
  478. {
  479.     try
  480.     {
  481.         // get the current drawing environment
  482.         
  483.         // If there's already an offscreen GWorld, get rid of it.  
  484.         if (mMainOffscreenGWorld != nil)
  485.         {
  486.             DisposeGWorld(mMainOffscreenGWorld);
  487.             mMainOffscreenGWorld = nil;
  488.         }
  489.  
  490.         // Define the size of the GWorld's bounding boxes -- same size as onscreen
  491.         short rightBounds = Context->Location.right - Context->Location.left;
  492.         short bottomBounds = Context->Location.bottom - Context->Location.top;
  493.         SetRect(&mRectOffscreenBounds, 0, 0, rightBounds, bottomBounds);
  494.  
  495.         // Allocate a new GWorld for the offscreen drawing and store its PixMap.
  496.         GDHandle currDevice = ::GetGDevice();
  497.         short targetDepth = (**(**currDevice).gdPMap).pixelSize;    // how many pixels deep are we?
  498.         QDErr err = ::NewGWorld(&mMainOffscreenGWorld, targetDepth, &mRectOffscreenBounds, 0, nil, 0);
  499.  
  500.         if (!err)
  501.         {
  502.             mPixOffscreenPixMap = ::GetGWorldPixMap(mMainOffscreenGWorld);
  503.                         
  504.             // we should now have a gworld
  505.             if (mMainOffscreenGWorld == nil || mPixOffscreenPixMap == nil)
  506.                 throw CLabelError(CONTROL_GWORLD_ALLOCATION_ERROR, this);
  507.             
  508.             // Clear the image
  509.             this->ClearDisplay();
  510.         }
  511.     }
  512.     
  513.     // error handling
  514.     catch (CLabelError &labelError)
  515.     {
  516.         labelError.HandleError();
  517.     }
  518. }
  519.  
  520. ///////////////////////////////////////////////////////////////////////////////
  521. //
  522. //  CLabelControl::ClearDisplay
  523. //
  524. //    No params        no parameters are passed or returned
  525. //
  526. //    Erases the main offscreen world to the background color passed in the properties
  527. //
  528.  
  529. void CLabelControl::ClearDisplay(void)
  530. {
  531.     // Store the current port and device before switching to the offscreen world.
  532.     CGrafPtr    currentPort;
  533.     GDHandle    currentDevice;
  534.     ::GetGWorld(¤tPort, ¤tDevice);
  535.  
  536.     // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  537.     ::SetGWorld(mMainOffscreenGWorld, nil);
  538.     
  539.     // Set the background color before we erase
  540.     ::RGBBackColor(&mBackColor);
  541.  
  542.     // Erase the PixMap's bounding box in the GWorld.
  543.     ::EraseRect(&mRectOffscreenBounds);
  544.  
  545.     // restore the original port and device         
  546.     ::SetGWorld(currentPort, currentDevice);
  547. }
  548.  
  549. ///////////////////////////////////////////////////////////////////////////////
  550. //
  551. //  CLabelControl::MoveOnScreen
  552. //
  553. //    offscreenBitMap    a PixMap or BitMap pointer
  554. //    Context            the drawing context for ActiveX - includes the port
  555. //
  556. //    Computes the size of the rect needed to hold the rotated rect.
  557. //
  558.  
  559. void CLabelControl::MoveOnScreen(const BitMap* /*offscreenBitMap*/, const DrawContext* Context)
  560. {
  561.     // Set the colors
  562.     ::RGBForeColor(&RGB_BLACK);
  563.     ::RGBBackColor(&RGB_WHITE);
  564.     
  565.     // actually move the bits
  566.     ::CopyBits((BitMap*)*mPixOffscreenPixMap, &(Context->Port->portBits), &mRectOffscreenBounds, &Context->Location, srcCopy, NULL);
  567. }
  568.  
  569. ///////////////////////////////////////////////////////////////////////////////
  570. //
  571. //  CLabelControl::GetOffscreenBitMap
  572. //
  573. //    returns BitMap    a PixMap or BitMap pointer
  574. //
  575. //    Returns a BitMap/PixMap that is part of the offscreen world - ready to draw into.
  576. //
  577.  
  578. BitMap * CLabelControl::GetOffscreenBitMap(void)
  579. {
  580.     // sanity check
  581.     ASSERT((mPixOffscreenPixMap != NULL), "NULL offscreen pixmap!");
  582.     
  583.     // Lock n load the pixMap handle 'till we're done with it
  584.     ::LockPixels(mPixOffscreenPixMap);
  585.     
  586.     // lock handle so it does not float on us
  587.     ::HLock((Handle)mPixOffscreenPixMap);
  588.     
  589.     // Note the bitmap, now that we've locked n loaded    
  590.     BitMap* offscreenBitMap = (BitMap *)(*mPixOffscreenPixMap);
  591.     ASSERT((offscreenBitMap != NULL), "NULL offscreen bitmap!");
  592.     
  593.     return offscreenBitMap;
  594. }
  595.  
  596. ///////////////////////////////////////////////////////////////////////////////
  597. //
  598. //  CLabelControl::ReleaseOffscreenBitMap
  599. //
  600. //    offscreenBitMap    an offscreenBitMap that we are through with and would like to purge.
  601. //
  602. //    Unlocks handles and pixels in preparation for disposing of a GWorld
  603. //
  604.  
  605. void CLabelControl::ReleaseOffscreenBitMap(BitMap * offscreenBitMap)
  606. {
  607.     
  608.     // we are done with this handle so let it float
  609.     ::HUnlock((Handle)mPixOffscreenPixMap);
  610.     
  611.     // Release the pixMap handle
  612.     ::UnlockPixels(mPixOffscreenPixMap);
  613.     
  614.     // Set this to NULL just as a safety factor
  615.     offscreenBitMap = NULL;
  616. }
  617.  
  618.  
  619.  
  620. ///////////////////////////////////////////////////////////////////////////////
  621. //
  622. //  P  R  I  V  A  T  E      D  R  A  W  I  N  G      S  U  P  P  O  R  T
  623. //
  624. ///////////////////////////////////////////////////////////////////////////////
  625.  
  626.  
  627. ///////////////////////////////////////////////////////////////////////////////
  628. //
  629. //    CLabelControl::SetFontParams
  630. //
  631. //    theGWorld        a GWorld that we want to set up the font parameters in
  632. //
  633. //    This routine sets the GWorld font params up based on the properties that were passed.
  634. //
  635.  
  636.  
  637. void CLabelControl::SetFontParams(GWorldPtr theGWorld)
  638. {
  639.     // switch to the offscreen GWorld to set up the font stuff
  640.     CGrafPtr    currentPort;
  641.     GDHandle    currentDevice;
  642.     ::GetGWorld(¤tPort, ¤tDevice);
  643.     
  644.     ::SetGWorld(theGWorld, NULL);
  645.  
  646.     // set the font info up from what was passed into us
  647.     if (mFontBold) TextFace(bold);
  648.     if (mFontItalic) TextFace(italic);
  649.     if (mFontUnderline) TextFace(underline);
  650.  
  651.     // convert fontname to a pascal string
  652.     Str255 fontName;
  653.     strcpy((char *)fontName, mFontName);
  654.     c2pstr((char *)fontName);
  655.  
  656.     short familyID = 0;
  657.     ::GetFNum(fontName, &familyID);
  658.     if (familyID == 0)
  659.         ::TextFont(applFont);
  660.     else
  661.         ::TextFont(familyID);
  662.     ::TextSize(this->mFontSize);
  663.     ::TextMode(srcCopy);
  664.     
  665.     ::GetFontInfo(&mFontInfo);    // save off the current font info
  666.     
  667.     // Set the port and device back to the original        
  668.     ::SetGWorld(currentPort, currentDevice);
  669. }
  670.  
  671. ///////////////////////////////////////////////////////////////////////////////
  672. //
  673. //    CLabelControl::GetTextSize
  674. //
  675. //    aString            a string of the pascal type
  676. //    textSize        the size of a Rect expressed as the bottom-right corner
  677. //
  678. //    Computes the size of a rect needed to hold aString (assumes that the GWorld 
  679. //    has been init'd to the correct font and styles).
  680. //
  681.  
  682. void CLabelControl::GetTextSize(const StringPtr aString, Point * textSize)
  683. {
  684.     // Store the current port and device before switching to the offscreen world.
  685.     CGrafPtr    currentPort;
  686.     GDHandle    currentDevice;
  687.     ::GetGWorld(¤tPort, ¤tDevice);
  688.  
  689.     // Switch to the offscreen GWorld so that the string width is accurate
  690.     ::SetGWorld(mMainOffscreenGWorld, NULL);
  691.  
  692.     textSize->v = mFontInfo.ascent + mFontInfo.descent;     // compute height
  693.     
  694.     textSize->h = StringWidth(aString);                         // compute width
  695.  
  696.     // restore the saved off port
  697.     ::SetGWorld(currentPort, currentDevice);
  698. }
  699.  
  700. ///////////////////////////////////////////////////////////////////////////////
  701. //
  702. //    CLabelControl::RenderLabelControl
  703. //
  704. //    No params        no parameters are passed or returned
  705. //
  706. //    Actually does the drawing of the label. It creates the content GWorld. Then
  707. //    rotates it as needed. Then blits it into the offscreen PixMap (data member).
  708. //
  709.  
  710. void CLabelControl::RenderLabelControl(void)
  711. {
  712.     Point contentSize = {0, 0};            // nil going in -- comes back to us with the size of the bitmap
  713.     GWorldPtr contentGWorld = nil;        // init to nil to satisfy the compiler
  714.     GWorldPtr scratchGWorld = nil;        // ditto
  715.     short rotationAngle;
  716.     CGrafPtr    currentPort;            // use for saving off curr GWorld
  717.     GDHandle    currentDevice;
  718.     
  719.     // build a GWorld just big enough for the string and then draw draw string into it
  720.     QDErr theErr = CreateContent(&contentSize, contentGWorld);
  721.     PixMapHandle contentPixMap = ::GetGWorldPixMap(contentGWorld);
  722.  
  723.     if(mAngle > quarterRevolution)    // 360 < angle < 90
  724.     {
  725.         short preRotate = mAngle / quarterRevolution;    // how many times must we rotate by 90?
  726.         rotationAngle = mAngle % quarterRevolution;    // reduce angle - e.g., how much more do we rotate?
  727.         
  728.         switch(preRotate)    // number of times to rotate by 90 degrees (i.e., 1 for 2nd quad, 2 for 3rd and 3 for 4th)
  729.         {    
  730.             case 1:
  731.             {
  732.                 GWorldPtr preRotateGWorld = nil;        // init to nil to satisfy the compiler
  733.                 theErr = Rotate90(contentGWorld, preRotateGWorld);
  734.                 ASSERT(theErr == noErr, "failed to pre-rotate by 90");
  735.  
  736.                 // blow away previous content and recreate it
  737.                 ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
  738.                 ::DisposeGWorld(contentGWorld);    // we have rotated so now we can blow it away
  739.                 contentGWorld = nil;    // just to make double sure
  740.  
  741.                 // Recreate content GWorld:
  742.                 Rect preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds;    // how many pixels deep are we?
  743.                 try
  744.                 {
  745.                     theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
  746.                     ASSERT(theErr == noErr, "failed to recreate content GWorld");
  747.                     if(theErr != noErr)
  748.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  749.                 
  750.                     // prepare to copy the pre-rotated version into the new content
  751.                     PixMapHandle preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
  752.                     ::LockPixels(preRotatePixMap);
  753.                     ::HLock((Handle)preRotatePixMap);
  754.  
  755.                     contentPixMap = ::GetGWorldPixMap(contentGWorld);
  756.                     ::LockPixels(contentPixMap);
  757.                     ::HLock((Handle)contentPixMap);
  758.  
  759.                     ::GetGWorld(¤tPort, ¤tDevice);
  760.  
  761.                     ::SetGWorld(contentGWorld, nil);            // Switch to the scratch GWorld    
  762.  
  763.                     ::RGBForeColor(&RGB_BLACK);
  764.                     ::RGBBackColor(&RGB_WHITE);
  765.                                                                 // now copy back into the (new) content
  766.                     ::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap), 
  767.                                 &(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
  768.  
  769.                     ::SetGWorld(currentPort, currentDevice);    // restore GWorld
  770.  
  771.                                                                 // reset the content size:
  772.                     contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
  773.                     contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
  774.  
  775.                     ::DisposeGWorld(preRotateGWorld);
  776.  
  777.                     // rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd 
  778.                     // by the rotation routine but we must dispose of it when completed drawing
  779.                     GWorldPtr rotatedGWorld = nil;    // init to nil to satisfy the compiler
  780.                     theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)rotationAngle), mBackColor, rotatedGWorld);
  781.                     ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
  782.                     if(theErr != noErr)
  783.                         throw CLabelError(ROTATION_FAILED_ERROR, this);
  784.  
  785.                     // now compute the alignment our content GWorld within the label GWorld
  786.                     PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
  787.                     Rect contentBounds = (**rotatedPixMap).bounds;    // get a copy of the rotated rect
  788.                     ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
  789.  
  790.                     // Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
  791.                     // in the 256 color depth. Then we can copy the entire 256 color into the final 
  792.                     // GWorld at the target bitmap color depth.
  793.                     theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
  794.                     ASSERT(theErr == noErr, "failed to alloc scratch GWorrld");
  795.                     if(theErr != noErr)
  796.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  797.                 
  798.                     PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
  799.                     ::LockPixels(scratchPixMap);
  800.                     ::HLock((Handle)scratchPixMap);
  801.  
  802.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  803.  
  804.                     ::SetGWorld(scratchGWorld, nil);            // Set the port and device back to the original  
  805.  
  806.                     ::RGBBackColor(&mBackColor);                // Set the background color before we erase        
  807.                     ::EraseRect(&(**scratchPixMap).bounds);     // Erase the PixMap's bounding box in the GWorld.              
  808.  
  809.                     ::RGBForeColor(&RGB_BLACK);
  810.                     ::RGBBackColor(&RGB_WHITE);
  811.  
  812.                     ::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap), 
  813.                                 &(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
  814.  
  815.                     ::DisposeGWorld(rotatedGWorld);                // cleanup the rotation artifacts    
  816.  
  817.                     ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  818.                     
  819.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  820.  
  821.                     ::SetGWorld(mMainOffscreenGWorld, nil);        // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  822.                                                                 // copy the content into the label        
  823.                     ::RGBBackColor(&mBackColor);                // Set the background color before we erase        
  824.                     ::EraseRect(&(**mPixOffscreenPixMap).bounds); // Erase the PixMap's bounding box in the GWorld.              
  825.  
  826.                     ::RGBForeColor(&RGB_BLACK);
  827.                     ::RGBBackColor(&RGB_WHITE);
  828.  
  829.                     ::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  830.                                 &(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
  831.  
  832.                     ::SetGWorld(currentPort, currentDevice);    // restore the port and device back to the original
  833.  
  834.                     ::DisposeGWorld(scratchGWorld);                // cleanup the scratch artifacts    
  835.                     ::DisposeGWorld(contentGWorld);
  836.                 }// try
  837.                 
  838.                 // error handling
  839.                 catch (CLabelError &labelError)
  840.                 {
  841.                     labelError.HandleError();
  842.                 }
  843.                 break;
  844.             }// case 1
  845.             
  846.             case 2:
  847.             {
  848.                 try
  849.                 {
  850.                     GWorldPtr preRotateGWorld = nil;        // init to nil to satisfy the compiler
  851.                     theErr = Rotate90(contentGWorld, preRotateGWorld);
  852.                     ASSERT(theErr == noErr, "failed to pre-rotate by 180");
  853.  
  854.                     // blow away previous content and recreate it
  855.                     ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
  856.                     ::DisposeGWorld(contentGWorld);
  857.                     contentGWorld = nil;    // just to make double sure
  858.  
  859.                     // Recreate content GWorld:
  860.                     Rect preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds;    // how many pixels deep are we?
  861.  
  862.                     theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
  863.                     ASSERT(theErr == noErr, "failed to recreate content GWorld");
  864.                     if(theErr != noErr)
  865.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  866.                 
  867.                     // prepare to copy the pre-rotated version into the new content
  868.                     PixMapHandle preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
  869.                     ::LockPixels(preRotatePixMap);
  870.                     ::HLock((Handle)preRotatePixMap);
  871.  
  872.                     contentPixMap = ::GetGWorldPixMap(contentGWorld);
  873.                     ::LockPixels(contentPixMap);
  874.                     ::HLock((Handle)contentPixMap);
  875.  
  876.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  877.  
  878.                     ::SetGWorld(contentGWorld, nil);            // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  879.  
  880.                     ::RGBForeColor(&RGB_BLACK);                    // set the first and last pallette colors before we copy
  881.                     ::RGBBackColor(&RGB_WHITE);
  882.  
  883.                     // now copy back into the (new) content
  884.                     ::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap), 
  885.                                 &(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
  886.  
  887.                     ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  888.  
  889.                     // reset the content size:
  890.                     contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
  891.                     contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
  892.  
  893.                     ::DisposeGWorld(preRotateGWorld);
  894.                     preRotateGWorld = nil;        // re-init to just to make sure
  895.                     theErr = Rotate90(contentGWorld, preRotateGWorld);
  896.                     ASSERT(theErr == noErr, "failed to pre-rotate by 180");
  897.  
  898.                     // now we copy it back again:
  899.                     // blow away previous content and recreate it
  900.                     ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
  901.                     ::DisposeGWorld(contentGWorld);
  902.                     contentGWorld = nil;    // just to make double sure
  903.  
  904.                     // Recreate content GWorld:
  905.                     preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds;    // how many pixels deep are we?
  906.  
  907.                     theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
  908.                     ASSERT(theErr == noErr, "failed to recreate content GWorld");
  909.                     if(theErr != noErr)
  910.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  911.                 
  912.                     // prepare to copy the pre-rotated version into the new content
  913.                     preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
  914.                     ::LockPixels(preRotatePixMap);
  915.                     ::HLock((Handle)preRotatePixMap);
  916.  
  917.                     contentPixMap = ::GetGWorldPixMap(contentGWorld);
  918.                     ::LockPixels(contentPixMap);
  919.                     ::HLock((Handle)contentPixMap);
  920.  
  921.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  922.  
  923.                     ::SetGWorld(contentGWorld, nil);            // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  924.  
  925.                     ::RGBForeColor(&RGB_BLACK);                    // set the first and last pallette colors before we copy
  926.                     ::RGBBackColor(&RGB_WHITE);
  927.  
  928.                     // now copy back into the (new) content
  929.                     ::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap), 
  930.                                 &(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
  931.  
  932.                     ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  933.  
  934.                     // reset the content size:
  935.                     contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
  936.                     contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
  937.  
  938.                     ::DisposeGWorld(preRotateGWorld);
  939.                     preRotateGWorld = nil;        // re-init to just to make sure
  940.  
  941.                     // rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd 
  942.                     // by the rotation routine but we must dispose of it when completed drawing
  943.                     GWorldPtr rotatedGWorld = nil;    // init to nil to satisfy the compiler
  944.                     theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)rotationAngle), mBackColor, rotatedGWorld);
  945.                     ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
  946.                     if(theErr != noErr)
  947.                         throw CLabelError(ROTATION_FAILED_ERROR, this);
  948.  
  949.                     // now compute the alignment our content GWorld within the label GWorld
  950.                     PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
  951.                     Rect contentBounds = (**rotatedPixMap).bounds;    // get a copy of the rotated rect
  952.                     ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
  953.  
  954.                     // Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
  955.                     // in the 256 color depth. Then we can copy the entire 256 color into the final 
  956.                     // GWorld at the target bitmap color depth.
  957.                     theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
  958.                     ASSERT(theErr == noErr, "failed to alloc scratch GWorrld");
  959.                     if(theErr != noErr)
  960.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  961.                 
  962.                     PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
  963.                     ::LockPixels(scratchPixMap);
  964.                     ::HLock((Handle)scratchPixMap);
  965.  
  966.                     // Store the current port and device before switching to the offscreen world.
  967.                     ::GetGWorld(¤tPort, ¤tDevice);
  968.  
  969.                     ::SetGWorld(scratchGWorld, nil);    // Switch to the scratch GWorld and lock the offscreen buffer in memory.        
  970.                     ::RGBBackColor(&mBackColor);                // Set the background color before we erase        
  971.  
  972.                     ::EraseRect(&(**scratchPixMap).bounds);         // Erase the PixMap's bounding box in the GWorld.              
  973.  
  974.                     ::RGBForeColor(&RGB_BLACK);
  975.                     ::RGBBackColor(&RGB_WHITE);
  976.  
  977.                     ::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap), 
  978.                                 &(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
  979.  
  980.                     ::SetGWorld(currentPort, currentDevice);    // Set the port and device back to the original  
  981.  
  982.                     ::DisposeGWorld(rotatedGWorld);            // cleanup the rotation artifacts    
  983.  
  984.                     
  985.                     ::SetGWorld(mMainOffscreenGWorld, nil);    // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  986.                                                             // copy the content into the label        
  987.                     ::RGBForeColor(&RGB_BLACK);
  988.                     ::RGBBackColor(&RGB_WHITE);
  989.  
  990.                     ::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  991.                                 &(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
  992.  
  993.                     ::SetGWorld(currentPort, currentDevice);    // Set the port and device back to the original
  994.  
  995.                     ::DisposeGWorld(scratchGWorld);            // cleanup the scratch artifacts    
  996.                     ::DisposeGWorld(contentGWorld);
  997.                 }// try
  998.                 
  999.                 // error handling
  1000.                 catch (CLabelError &labelError)
  1001.                 {
  1002.                     labelError.HandleError();
  1003.                 }
  1004.  
  1005.                 break;
  1006.             }// case 2
  1007.                 
  1008.             case 3:
  1009.             {
  1010.                 try
  1011.                 {
  1012.                     GWorldPtr preRotateGWorld = nil;        // init to nil to satisfy the compiler
  1013.                     theErr = Rotate90(contentGWorld, preRotateGWorld);    // rotate for 90
  1014.                     ASSERT(theErr == noErr, "failed to pre-rotate by 180");
  1015.  
  1016.                     // blow away previous content and recreate it
  1017.                     ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
  1018.                     ::DisposeGWorld(contentGWorld);
  1019.                     contentGWorld = nil;    // just to make double sure
  1020.  
  1021.                     // Recreate content GWorld:
  1022.                     Rect preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds;    // how many pixels deep are we?
  1023.  
  1024.                     theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
  1025.                     ASSERT(theErr == noErr, "failed to recreate content GWorld");
  1026.                     if(theErr != noErr)
  1027.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1028.                 
  1029.                     // prepare to copy the pre-rotated version into the new content
  1030.                     PixMapHandle preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
  1031.                     ::LockPixels(preRotatePixMap);
  1032.                     ::HLock((Handle)preRotatePixMap);
  1033.  
  1034.                     contentPixMap = ::GetGWorldPixMap(contentGWorld);
  1035.                     ::LockPixels(contentPixMap);
  1036.                     ::HLock((Handle)contentPixMap);
  1037.  
  1038.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  1039.  
  1040.                     ::SetGWorld(contentGWorld, nil);            // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  1041.  
  1042.                     ::RGBForeColor(&RGB_BLACK);                    // set the first and last pallette colors before we copy
  1043.                     ::RGBBackColor(&RGB_WHITE);
  1044.  
  1045.                     // now copy back into the (new) content
  1046.                     ::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap), 
  1047.                                 &(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
  1048.  
  1049.                     ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  1050.  
  1051.                     // reset the content size:
  1052.                     contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
  1053.                     contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
  1054.  
  1055.                     ::DisposeGWorld(preRotateGWorld);
  1056.                     preRotateGWorld = nil;        // re-init to just to make sure
  1057.                     theErr = Rotate90(contentGWorld, preRotateGWorld);    // rotate 2nd time for 180
  1058.                     ASSERT(theErr == noErr, "failed to pre-rotate by 180");
  1059.  
  1060.                     // now we copy it back again:
  1061.                     // blow away previous content and recreate it
  1062.                     ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
  1063.                     ::DisposeGWorld(contentGWorld);
  1064.                     contentGWorld = nil;    // just to make double sure
  1065.  
  1066.                     // Recreate content GWorld:
  1067.                     preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds;    // how many pixels deep are we?
  1068.  
  1069.                     theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
  1070.                     ASSERT(theErr == noErr, "failed to recreate content GWorld");
  1071.                     if(theErr != noErr)
  1072.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1073.                 
  1074.                     // prepare to copy the pre-rotated version into the new content
  1075.                     preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
  1076.                     ::LockPixels(preRotatePixMap);
  1077.                     ::HLock((Handle)preRotatePixMap);
  1078.  
  1079.                     contentPixMap = ::GetGWorldPixMap(contentGWorld);
  1080.                     ::LockPixels(contentPixMap);
  1081.                     ::HLock((Handle)contentPixMap);
  1082.  
  1083.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  1084.  
  1085.                     ::SetGWorld(contentGWorld, nil);            // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  1086.  
  1087.                     ::RGBForeColor(&RGB_BLACK);                    // set the first and last pallette colors before we copy
  1088.                     ::RGBBackColor(&RGB_WHITE);
  1089.  
  1090.                     // now copy back into the (new) content
  1091.                     ::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap), 
  1092.                                 &(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
  1093.  
  1094.                     ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  1095.  
  1096.                     // reset the content size:
  1097.                     contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
  1098.                     contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
  1099.  
  1100.                     ::DisposeGWorld(preRotateGWorld);
  1101.                     preRotateGWorld = nil;        // re-init to just to make sure
  1102.                     theErr = Rotate90(contentGWorld, preRotateGWorld);    // rotate third time for 270
  1103.                     ASSERT(theErr == noErr, "failed to pre-rotate by 270");
  1104.  
  1105.                     // now we copy it back again:
  1106.                     // blow away previous content and recreate it
  1107.                     ReleaseOffscreenBitMap((BitMap*)&contentPixMap);
  1108.                     ::DisposeGWorld(contentGWorld);
  1109.                     contentGWorld = nil;    // just to make double sure
  1110.  
  1111.                     // Recreate content GWorld:
  1112.                     preRotateRect = (**((CGrafPort)*preRotateGWorld).portPixMap).bounds;    // how many pixels deep are we?
  1113.  
  1114.                     theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth, &preRotateRect, 0, nil, 0);
  1115.                     ASSERT(theErr == noErr, "failed to recreate content GWorld");
  1116.                     if(theErr != noErr)
  1117.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1118.                 
  1119.                     // prepare to copy the pre-rotated version into the new content
  1120.                     preRotatePixMap = ::GetGWorldPixMap(preRotateGWorld);
  1121.                     ::LockPixels(preRotatePixMap);
  1122.                     ::HLock((Handle)preRotatePixMap);
  1123.  
  1124.                     contentPixMap = ::GetGWorldPixMap(contentGWorld);
  1125.                     ::LockPixels(contentPixMap);
  1126.                     ::HLock((Handle)contentPixMap);
  1127.  
  1128.                     ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  1129.  
  1130.                     ::SetGWorld(contentGWorld, nil);            // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  1131.  
  1132.                     ::RGBForeColor(&RGB_BLACK);                    // set the first and last pallette colors before we copy
  1133.                     ::RGBBackColor(&RGB_WHITE);
  1134.  
  1135.                     // now copy back into the (new) content
  1136.                     ::CopyBits((BitMap *)(*preRotatePixMap), (BitMap *)(*contentPixMap), 
  1137.                                 &(**preRotatePixMap).bounds, &(**contentPixMap).bounds, srcCopy, NULL);
  1138.  
  1139.                     ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  1140.  
  1141.                     // reset the content size:
  1142.                     contentSize.v = (**contentPixMap).bounds.bottom - (**contentPixMap).bounds.top;
  1143.                     contentSize.h = (**contentPixMap).bounds.right - (**contentPixMap).bounds.left;
  1144.  
  1145.                     ::DisposeGWorld(preRotateGWorld);
  1146.                     preRotateGWorld = nil;        // re-init to just to make sure
  1147.  
  1148.                     // rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd 
  1149.                     // by the rotation routine but we must dispose of it when completed drawing
  1150.                     GWorldPtr rotatedGWorld = nil;    // init to nil to satisfy the compiler
  1151.                     theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)rotationAngle), mBackColor, rotatedGWorld);
  1152.                     ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
  1153.                     if(theErr != noErr)
  1154.                         throw CLabelError(ROTATION_FAILED_ERROR, this);
  1155.  
  1156.                     // now compute the alignment our content GWorld within the label GWorld
  1157.                     PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
  1158.                     Rect contentBounds = (**rotatedPixMap).bounds;    // get a copy of the rotated rect
  1159.                     ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
  1160.  
  1161.                     // Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
  1162.                     // in the 256 color depth. Then we can copy the entire 256 color into the final 
  1163.                     // GWorld at the target bitmap color depth.
  1164.                     theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
  1165.                     ASSERT(theErr == noErr, "failed to alloc scratch GWorrld");
  1166.                     if(theErr != noErr)
  1167.                         throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1168.                 
  1169.                     PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
  1170.                     ::LockPixels(scratchPixMap);
  1171.                     ::HLock((Handle)scratchPixMap);
  1172.  
  1173.                     // Store the current port and device before switching to the offscreen world.
  1174.                     ::GetGWorld(¤tPort, ¤tDevice);
  1175.  
  1176.                     ::SetGWorld(scratchGWorld, nil);        // Switch to the scratch GWorld        
  1177.                     ::RGBBackColor(&mBackColor);            // Set the background color before we erase        
  1178.  
  1179.                     ::EraseRect(&(**scratchPixMap).bounds); // Erase the PixMap's bounding box in the GWorld.              
  1180.  
  1181.                     ::RGBForeColor(&RGB_BLACK);                // set the first and last pallette colors before we copy
  1182.                     ::RGBBackColor(&RGB_WHITE);
  1183.  
  1184.                     ::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap), 
  1185.                                 &(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
  1186.  
  1187.                     ::SetGWorld(currentPort, currentDevice);// restore to the previous GWorld
  1188.  
  1189.                     ::DisposeGWorld(rotatedGWorld);            // cleanup the rotation artifacts    
  1190.  
  1191.                     ::SetGWorld(mMainOffscreenGWorld, nil);    // Switch to the offscreen GWorld.
  1192.  
  1193.                     ::RGBForeColor(&RGB_BLACK);                // set the first and last pallette colors before we copy
  1194.                     ::RGBBackColor(&RGB_WHITE);
  1195.  
  1196.                     ::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  1197.                                 &(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
  1198.  
  1199.                     ::SetGWorld(currentPort, currentDevice);// Set the port and device back to the original
  1200.  
  1201.                     ::DisposeGWorld(scratchGWorld);            // cleanup the scratch artifacts    
  1202.                     ::DisposeGWorld(contentGWorld);
  1203.                 }// try
  1204.                 
  1205.                 // error handling
  1206.                 catch (CLabelError &labelError)
  1207.                 {
  1208.                     labelError.HandleError();
  1209.                 }
  1210.  
  1211.                 break;
  1212.             }// case 3
  1213.         }// switch(preRotate)
  1214.     }// if (mAngle > quarterRevolution)    
  1215.     else
  1216.     {
  1217.         rotationAngle = mAngle;    // did not have to pre-rotate so set to parameter
  1218.     }// else
  1219.  
  1220.     
  1221.     if(mAngle != 0 && mAngle <= quarterRevolution)    // 0 < angle <= 90
  1222.     {
  1223.         try
  1224.         {
  1225.             // rotate the content GWorld and return a rotatedGWorld (the new GWorld is alloc'd 
  1226.             // by the rotation routine but we must dispose of it when completed drawing
  1227.             GWorldPtr rotatedGWorld = nil;    // init to nil to satisfy the compiler
  1228.             theErr = rotate(contentPixMap, contentSize.v, contentSize.h, DegressToRadians((double)mAngle), mBackColor, rotatedGWorld);
  1229.             ASSERT(theErr == noErr, "rotate() failed - probably low mem!");
  1230.             if(theErr != noErr)
  1231.                 throw CLabelError(ROTATION_FAILED_ERROR, this);
  1232.  
  1233.             // now compute the alignment our content GWorld within the label GWorld
  1234.             PixMapHandle rotatedPixMap = ::GetGWorldPixMap(rotatedGWorld);
  1235.             Rect contentBounds = (**rotatedPixMap).bounds;    // get a copy of the rotated rect
  1236.             ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
  1237.  
  1238.             // Create a scratch pixmap at 8 bits deep so that we can assemble the entire thing
  1239.             // in the 256 color depth. Then we can copy the entire 256 color into the final 
  1240.             // GWorld at the target bitmap color depth.
  1241.             theErr = ::NewGWorld(&scratchGWorld, twoHundredFiftySixColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
  1242.             ASSERT(theErr == noErr, "failed to alloc scratch GWorld");
  1243.             if(theErr != noErr)
  1244.                 throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1245.  
  1246.             PixMapHandle scratchPixMap = ::GetGWorldPixMap(scratchGWorld);
  1247.             ::LockPixels(scratchPixMap);
  1248.             ::HLock((Handle)scratchPixMap);
  1249.  
  1250.             ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  1251.             
  1252.             ::SetGWorld(scratchGWorld, nil);        // Switch to the offscreen GWorld
  1253.             ::RGBBackColor(&mBackColor);            // Set the background color before we erase
  1254.  
  1255.             ::EraseRect(&((**scratchPixMap).bounds));    // Erase the PixMap's bounding box in the GWorld.
  1256.             
  1257.             ::RGBForeColor(&RGB_BLACK);            // set the first and last pallette colors before we copy
  1258.             ::RGBBackColor(&RGB_WHITE);
  1259.         
  1260.             // copy the content into a 256 color version of the label        
  1261.             ::CopyBits((BitMap *)(*rotatedPixMap), (BitMap *)(*scratchPixMap), 
  1262.                         &(**rotatedPixMap).bounds, &contentBounds, srcCopy, NULL);
  1263.  
  1264.             ::DisposeGWorld(rotatedGWorld);                    // cleanup the rotation artifacts    
  1265.  
  1266.             ::SetGWorld(mMainOffscreenGWorld, nil);    // Switch to the offscreen GWorld
  1267.             
  1268.             ::RGBBackColor(&mBackColor);            // Set the background color before we erase
  1269.             ::EraseRect(&mRectOffscreenBounds);    // Erase the PixMap's bounding box in the GWorld.
  1270.             
  1271.             ::RGBForeColor(&RGB_BLACK);            // set the first and last palette colors before we copy
  1272.             ::RGBBackColor(&RGB_WHITE);
  1273.         
  1274.             ::CopyBits((BitMap *)(*scratchPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  1275.                         &(**scratchPixMap).bounds, &mRectOffscreenBounds, srcCopy, NULL);
  1276.             ::DisposeGWorld(scratchGWorld);                    // cleanup the scratch artifacts    
  1277.  
  1278.             ::SetGWorld(currentPort, currentDevice);    // Set the port and device back to the original
  1279.         }// try
  1280.         
  1281.         // error handling
  1282.         catch (CLabelError &labelError)
  1283.         {
  1284.             labelError.HandleError();
  1285.         }
  1286.  
  1287.     }// if(mAngle != 0 && mAngle <= 90)
  1288.     else if(mAngle == 0)    // angle == 0 so we are not rotating -- just copy content into label
  1289.     {
  1290.         // now compute the alignment our content GWorld within the label GWorld
  1291.         Rect contentBounds = (**contentPixMap).bounds;    // get a copy of the content rect
  1292.         ComputeAlignmentRect(contentBounds, mRectOffscreenBounds);
  1293.  
  1294.         ::GetGWorld(¤tPort, ¤tDevice);    // Store the current port and device before switching to the offscreen world.
  1295.  
  1296.         ::SetGWorld(mMainOffscreenGWorld, nil);            // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  1297.  
  1298.         // Set the colors before we do a copybits so
  1299.         ::RGBForeColor(&RGB_BLACK);
  1300.         ::RGBBackColor(&RGB_WHITE);
  1301.     
  1302.         ::CopyBits((BitMap *)(*contentPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  1303.                     &(**contentPixMap).bounds, &contentBounds, srcCopy, NULL);    
  1304.  
  1305.         ::SetGWorld(currentPort, currentDevice);    // restore to the previous GWorld
  1306.     }// else if(rotationAngle == 0)
  1307.  
  1308. //    DisposeGWorld(contentGWorld);
  1309. }
  1310.  
  1311. ///////////////////////////////////////////////////////////////////////////////
  1312. //
  1313. //    CLabelControl::ComputeAlignmentRect
  1314. //
  1315. //    theContentRect    the rect that is to be aligned
  1316. //    theLabelRect    the parent rect (i.e., this one is stationary -- the other one moves)
  1317. //
  1318. //    This routine aligns the contentRect inside theLabelRect. It does so 
  1319. //    according to the parameter passed as mAlignment by the OCX. This operation 
  1320. //    takes place on a rect that has been prepared and is ready to blit into 
  1321. //    place (i.e., it is rotated already).
  1322. //
  1323.  
  1324. void CLabelControl::ComputeAlignmentRect(Rect &theContentRect, const Rect theLabelRect)
  1325. {
  1326.     // compute the differences between rect sizes:
  1327.     short deltaVert = (theLabelRect.bottom - theLabelRect.top) - theContentRect.bottom;
  1328.     short deltaHoriz = (theLabelRect.right - theLabelRect.left) - theContentRect.right;
  1329.     
  1330.     // now compute the amount that we will offset the content rect by:
  1331.     Point offsetAmount;
  1332.     
  1333.     // do the horizontal coord first
  1334.     switch(mAlignment)
  1335.     {
  1336.         case 0: 
  1337.         case 3:
  1338.         case 6:    // left aligned
  1339.             offsetAmount.h = 0;                    // don't offset horiz
  1340.             break;
  1341.         
  1342.         case 1:
  1343.         case 4:
  1344.         case 7:    // centered horizontally
  1345.             offsetAmount.h = deltaHoriz / 2;    // offset by half the difference horiz
  1346.             break;
  1347.             
  1348.         case 2:
  1349.         case 5:
  1350.         case 8:    // right aligned
  1351.             offsetAmount.h = deltaHoriz;        // offset by the entire horiz difference
  1352.             break;
  1353.             
  1354.         default:
  1355.             throw CLabelError(CONTROL_BAD_ALIGNMENT_PARAM_ERROR, this);
  1356.     }
  1357.     
  1358.     // now the vertical coord
  1359.     switch(mAlignment)
  1360.     {
  1361.         case 0:
  1362.         case 1:
  1363.         case 2:    // top aligned
  1364.             offsetAmount.v = 0;                    // don't offset vert
  1365.             break;
  1366.         
  1367.         case 3:
  1368.         case 4:
  1369.         case 5:    // centered vertically
  1370.             offsetAmount.v = deltaVert / 2;        // offset by half the difference vert
  1371.             break;
  1372.             
  1373.         case 6:
  1374.         case 7:
  1375.         case 8:    // bottom aligned
  1376.             offsetAmount.v = deltaVert;            // offset by the entire vert difference
  1377.             break;
  1378.             
  1379.         default:
  1380.             throw CLabelError(CONTROL_BAD_ALIGNMENT_PARAM_ERROR, this);
  1381.     }
  1382.     
  1383.     // now we can just offset the content rect:
  1384.     OffsetRect(&theContentRect, offsetAmount.h, offsetAmount.v);
  1385. }
  1386.  
  1387.  
  1388. ///////////////////////////////////////////////////////////////////////////////
  1389. //
  1390. //    CLabelControl::RotatedSize
  1391. //
  1392. //    angDegrees        the CCW angle in degrees from the horizontal
  1393. //    origSize        the size of a Rect expressed as the bottom-right corner
  1394. //
  1395. //    Computes the size of the rect needed to hold the rotated rect.
  1396. //
  1397.  
  1398. Point CLabelControl::RotatedSize(const double angDegrees, const Point origSize)
  1399. {
  1400.     double angRadians = DegressToRadians(angDegrees);
  1401.     double compAngRadians = DegressToRadians(quarterRevolution - angDegrees);
  1402.     Point rotSize = {     sin(angRadians) * origSize.h + cos(angRadians) * origSize.v,             // v = sin(ø)w + cos(ø)h
  1403.                         sin(compAngRadians) * origSize.h + cos(compAngRadians) * origSize.v };    // h same with angle = 90 - ø
  1404.     return rotSize;
  1405. }
  1406.  
  1407.  
  1408. ///////////////////////////////////////////////////////////////////////////////
  1409. //
  1410. //    CLabelControl::CreateContent
  1411. //
  1412. //    textSize        the size of a Rect that holds the caption expressed as 
  1413. //                    the bottom-right corner
  1414. //    contentGWorld    a GWorld containing the caption (not rotated)
  1415. //
  1416. //    Given a nil pointer to a GWorld return a GWorld setup with all the right params
  1417. //    and with the caption string drawn in it.
  1418. //
  1419.  
  1420.     QDErr CLabelControl::CreateContent(Point* textSize, GWorldPtr &contentGWorld)
  1421.     {
  1422.         QDErr theErr;
  1423.         try
  1424.         {
  1425.             // Get the size of the Caption
  1426.             Point sizeCaption  = {0, 0};                    // nil point cause I'm anal
  1427.             Str255 strCaption;                                // temp holder for our string - we store it as a c-string
  1428.             strcpy((char *)strCaption, mCaption);
  1429.             c2pstr((char *)strCaption);
  1430.             this->GetTextSize(strCaption, &sizeCaption);    // get the smallest rect to hold our text
  1431.             textSize->h = sizeCaption.h;                    // pass this back so we can use it later
  1432.             textSize->v = sizeCaption.v;
  1433.  
  1434.             const Rect contentBounds = { 0, 0, textSize->v, textSize->h };
  1435.             // alloc the output GWorld and save results of failure
  1436.             theErr = ::NewGWorld(&contentGWorld, twoHundredFiftySixColorsDepth , &contentBounds, 0, nil, 0);
  1437.             ASSERT(theErr == noErr, "alloc contentGWorld failed!");
  1438.             if(theErr != noErr)
  1439.                 throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1440.  
  1441.             this->SetFontParams(contentGWorld);
  1442.  
  1443.             // Store the current port and device before switching to the offscreen world.
  1444.             CGrafPtr    currentPort;
  1445.             GDHandle    currentDevice;
  1446.             ::GetGWorld(¤tPort, ¤tDevice);
  1447.  
  1448.             // Switch to the offscreen GWorld and lock the offscreen buffer in memory.
  1449.             ::SetGWorld(contentGWorld, NULL);
  1450.             PixMapHandle contentPixMap = GetGWorldPixMap(contentGWorld);
  1451.             ::LockPixels(contentPixMap);
  1452.  
  1453.             // Draw the text into the offsceen world...
  1454.             // generate Pascal strings for drawing
  1455.             Str255 captionStringToDraw;
  1456.             strcpy((char *)captionStringToDraw, mCaption);
  1457.             c2pstr((char *)captionStringToDraw);
  1458.  
  1459.             // Set colors
  1460.             ::RGBBackColor(&mBackColor);
  1461.             ::RGBForeColor(&mForeColor);
  1462.  
  1463.             ::MoveTo(0, mFontInfo.ascent);
  1464.             ::DrawString(captionStringToDraw);
  1465.  
  1466.             // After drawing the string, unlock the offscreen buffer in memory
  1467.             //  and set the port and device back to the original        
  1468.             ::UnlockPixels(contentPixMap);
  1469.             ::SetGWorld(currentPort, currentDevice);
  1470.         }// try
  1471.  
  1472.         // error handling
  1473.         catch (CLabelError &labelError)
  1474.         {
  1475.             labelError.HandleError();
  1476.         }
  1477.             
  1478.         return theErr;    // pass back the error condition -- should only fail if low mem
  1479.     }
  1480.  
  1481. ///////////////////////////////////////////////////////////////////////////////
  1482. //
  1483. //    CLabelControl::Rotate90
  1484. //
  1485. //    inputGWorld        a GWorld containing a bitmap to be rotated by 90 degrees CCW
  1486. //    rotate90GWorld    a GWorld containing the results of the 90 degree rotation
  1487. //
  1488. //    Provided an inputGworld this routine rotates it 90 degrees CCW. A pointer 
  1489. //    to an output GWorld is provided as input. This GWorld is alloc'd by the
  1490. //    rotation routine and returned.
  1491. //
  1492.  
  1493.     QDErr CLabelControl::Rotate90(const GWorldPtr inputGWorld, GWorldPtr &rotate90GWorld)
  1494.     {
  1495.         try
  1496.         {
  1497.             // create the output buffer (--> rotateGWorld):
  1498.             PixMapHandle inputPixMap = GetGWorldPixMap(inputGWorld);        // get the PixMap from our unrotated GWorld
  1499.             ::LockPixels(inputPixMap);
  1500.             const Rect inputBounds = (**inputPixMap).bounds;                // get the bounds of the unrotated GWorld
  1501.             // create bounds of rotated GWorld by swapping right and bottom coords for rect @ 0,0
  1502.             const Rect rotate90Bounds = { 0, 0, inputBounds.right, inputBounds.bottom };
  1503.             QDErr theErr = ::NewGWorld(&rotate90GWorld, twoHundredFiftySixColorsDepth , &rotate90Bounds, 0, nil, 0);
  1504.             ASSERT(theErr == noErr, "alloc rotate90GWorld failed!");
  1505.             if(theErr != noErr)
  1506.                 throw CLabelError(GWORLD_ALLOCATION_ERROR, this);
  1507.  
  1508.             if (theErr != noErr) return theErr;                                // bail and return the err code
  1509.             PixMapHandle rotate90PixMap = GetGWorldPixMap(rotate90GWorld);    // get the PixMap from our rotated GWorld
  1510.             ::LockPixels(rotate90PixMap);
  1511.             ::HLock((Handle)rotate90PixMap);
  1512.  
  1513.             // now get the rowbytes for each bitmap since we need these to compute our addresses
  1514.             short inputRowBytes = GetRowBytes(inputPixMap);
  1515.             short rotate90RowBytes = GetRowBytes(rotate90PixMap);
  1516.             
  1517.             // walk each pixel in the source PixMap and copy it to the rotated PixMap
  1518.             Ptr inputPixMapBaseAddr = GetPixBaseAddr(inputPixMap);            // always get the addr via this function
  1519.             Ptr rotate90PixMapBaseAddr = GetPixBaseAddr(rotate90PixMap);    // always get the addr via this function
  1520.  
  1521.             unsigned char * src;    // pointer to the begin of a src row (in scanline order)
  1522.             unsigned char * dst;    // pointer to the begin of a dst row (in scanline order)
  1523.             unsigned char * curColumn = nil;
  1524.             
  1525.             for (int row = 0; row < inputBounds.bottom; row++)                // do all the rows
  1526.             {
  1527.                 src = (unsigned char *)(inputPixMapBaseAddr + (inputRowBytes * row));    // init to begininning of current row
  1528.                 curColumn = (unsigned char *)(rotate90PixMapBaseAddr + row);// init to point to first pixel of first row
  1529.  
  1530.                 for (int column = 0; column < inputBounds.right; column++)// do all the columns
  1531.                 {
  1532.                     dst = curColumn + ((inputBounds.right - column - 1) * rotate90RowBytes);
  1533.                     *dst = src[column];
  1534.                 }// columns        
  1535.             }// rows
  1536.             
  1537.             ::UnlockPixels(inputPixMap);
  1538.         }// try
  1539.  
  1540.         // error handling
  1541.         catch (CLabelError &labelError)
  1542.         {
  1543.             labelError.HandleError();
  1544.         }
  1545.             
  1546.         return noErr;
  1547.     }
  1548.  
  1549. // end-of-file ////////////////////////////////////////////////////////////////
  1550.  
  1551.