home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc Development Framework / ODFDev / Clock / Sources / View.cpp < prev    next >
Encoding:
Text File  |  1996-09-17  |  28.6 KB  |  947 lines  |  [TEXT/CWIE]

  1. //========================================================================================
  2. //
  3. //    File:                View.cpp
  4. //    Release Version:    $ ODF 2 $
  5. //
  6. //    Copyright:    (c) 1993 - 1996 by Apple Computer, Inc., all rights reserved.
  7. //
  8. //========================================================================================
  9.  
  10. #ifndef VIEW_H
  11. #include "View.h"
  12. #endif
  13.  
  14. #ifndef PART_H
  15. #include "Part.h"
  16. #endif
  17.  
  18. #ifndef DEFINES_K
  19. #include "Defines.k"
  20. #endif
  21.  
  22. #ifndef CONTENT_H
  23. #include "Content.h"
  24. #endif
  25.  
  26. #ifndef FRAME_H
  27. #include "Frame.h"
  28. #endif
  29.  
  30. // ----- Part Layer -----
  31.  
  32. #ifndef FWUTIL_H
  33. #include "FWUtil.h"
  34. #endif
  35.  
  36. #ifndef FWITERS_H
  37. #include "FWIters.h"
  38. #endif
  39.  
  40. #ifndef FWCONTXT_H
  41. #include "FWContxt.h"
  42. #endif
  43.  
  44. #ifndef FWGROWBX_H
  45. #include "FWGrowBx.h"
  46. #endif
  47.  
  48. // ----- OS Layer -----
  49.  
  50. #ifndef FWTXTSHP_H
  51. #include "FWTxtShp.h"
  52. #endif
  53.  
  54. #ifndef FWRECSHP_H
  55. #include "FWRecShp.h"
  56. #endif
  57.  
  58. #ifndef FWLINSHP_H
  59. #include "FWLinShp.h"
  60. #endif
  61.  
  62. #ifndef FWOVLSHP_H
  63. #include "FWOvlShp.h"
  64. #endif
  65.  
  66. #ifndef FWODGEOM_H
  67. #include "FWODGeom.h"
  68. #endif
  69.  
  70. #ifndef FWTXTBOX_H
  71. #include "FWTxtBox.h"
  72. #endif
  73.  
  74. #ifndef FWCFMRES_H
  75. #include "FWCFMRes.h"
  76. #endif
  77.  
  78. #ifndef FWRESACC_H
  79. #include "FWResAcc.h"
  80. #endif
  81.  
  82. #ifndef FWRESTYP_H
  83. #include "FWResTyp.h"
  84. #endif
  85.  
  86. #ifndef FWFXMATH_H
  87. #include "FWFxMath.h"
  88. #endif
  89.  
  90. #ifndef FWSCRCON_H
  91. #include "FWScrCon.h"
  92. #endif
  93.  
  94. #ifndef FWBITMAP_H
  95. #include "FWBitmap.h"
  96. #endif
  97.  
  98. #ifndef FWBMPSHP_H
  99. #include "FWBmpShp.h"
  100. #endif
  101.  
  102. #ifndef FWARCSHP_H
  103. #include "FWArcShp.h"
  104. #endif
  105.  
  106. #ifndef FWGRUTIL_H
  107. #include "FWGrUtil.h"
  108. #endif
  109.  
  110. #ifndef FWRESTYP_H
  111. #include "FWResTyp.h"
  112. #endif
  113.  
  114. //========================================================================================
  115. // RunTime information
  116. //========================================================================================
  117.  
  118. #ifdef FW_BUILD_MAC
  119. #pragma segment odfclock
  120. #endif
  121.  
  122. //========================================================================================
  123. // Constants
  124. //========================================================================================
  125.  
  126. const FW_Fixed kClockRadius = FW_IntToFixed(1000);    // In logical units
  127. const FW_Fixed kFxPI = FW_DoubleToFixed(3.1415926);
  128. const FW_Fixed kUsedShapeInset = FW_DoubleToFixed(-0.003);
  129.  
  130. //========================================================================================
  131. // class CClockView
  132. //========================================================================================
  133.  
  134. FW_DEFINE_AUTO(CClockView)
  135. FW_DEFINE_CLASS_M1(CClockView, FW_CView)
  136.  
  137. //----------------------------------------------------------------------------------------
  138. // CClockView::CClockView
  139. //----------------------------------------------------------------------------------------
  140.  
  141. CClockView::CClockView(Environment* ev) :
  142.     FW_CView(ev),
  143.     fClockFrame(NULL),
  144.     fLastTime(FW_CTime::GetCurrentTime())
  145. {
  146.     SetResizeInvalidates(ev, true);
  147.  
  148.     FW_END_CONSTRUCTOR
  149. }
  150.  
  151. //----------------------------------------------------------------------------------------
  152. // CClockView::CClockView
  153. //----------------------------------------------------------------------------------------
  154.  
  155. CClockView::~CClockView()
  156. {
  157.     FW_START_DESTRUCTOR
  158. }
  159.  
  160. //----------------------------------------------------------------------------------------
  161. // CClockView::PostCreateViewFromStream
  162. //----------------------------------------------------------------------------------------
  163.  
  164. void CClockView::PostCreateViewFromStream(Environment *ev)
  165. {
  166.     FW_CView::PostCreateViewFromStream(ev);
  167.     
  168.     fClockFrame = (CClockFrame*)GetFrame(ev);
  169.     fLastTime += fClockFrame->GetClockContent(ev)->GetTimeOffset();
  170. }
  171.  
  172. //----------------------------------------------------------------------------------------
  173. // CClockView::UpdateClock
  174. //----------------------------------------------------------------------------------------
  175.  
  176. void CClockView::UpdateClock(Environment* ev, const FW_CTime& time)
  177. {
  178.     fLastTime = time;
  179.     
  180.     FW_CFrameFacetIterator facets(ev, this->GetFrame(ev));
  181.     for (ODFacet* clockFacet = facets.First(ev); facets.IsNotComplete(ev); clockFacet = facets.Next(ev))
  182.     {
  183.         Draw(ev, clockFacet, FW_CAcquiredODShape(GetFrame(ev)->AcquireUsedShape(ev, NULL)));
  184.     }
  185. }
  186.  
  187. //========================================================================================
  188. // class CAnalogView
  189. //========================================================================================
  190.  
  191. FW_DEFINE_AUTO(CAnalogView)
  192. FW_DEFINE_CLASS_M1(CAnalogView, CClockView)
  193.     
  194. const FW_ClassTypeConstant LAnalogView = FW_TYPE_CONSTANT('A','N','V','W');
  195. FW_REGISTER_ARCHIVABLE_CLASS(LAnalogView, CAnalogView, CAnalogView::Create, FW_CView::Read, CAnalogView::Destroy, FW_CView::Write)
  196.  
  197. //----------------------------------------------------------------------------------------
  198. // CAnalogView::CAnalogView
  199. //----------------------------------------------------------------------------------------
  200.  
  201. CAnalogView::CAnalogView(Environment* ev) :
  202.     CClockView(ev)
  203. {
  204.     FW_END_CONSTRUCTOR
  205. }
  206.  
  207. //----------------------------------------------------------------------------------------
  208. // CAnalogView::~CAnalogView
  209. //----------------------------------------------------------------------------------------
  210.  
  211. CAnalogView::~CAnalogView()
  212. {
  213.     FW_START_DESTRUCTOR
  214. }
  215.  
  216. //----------------------------------------------------------------------------------------
  217. //    FixedCosine
  218. //----------------------------------------------------------------------------------------
  219.  
  220. static FW_Fixed FixedCosine(FW_Fixed angleRadians)
  221. {
  222. #if (GENERATING68K)
  223.     FW_Fixed hackResult;
  224.     hackResult.fRep = ::Frac2Fix(FracCos(angleRadians.fRep));
  225.     return hackResult;
  226. #else
  227.     return FW_Cos(angleRadians);
  228. #endif
  229. }
  230.  
  231. //----------------------------------------------------------------------------------------
  232. //    FixedSine
  233. //----------------------------------------------------------------------------------------
  234.  
  235. static FW_Fixed FixedSine(FW_Fixed angleRadians)
  236. {
  237. #if (GENERATING68K)
  238.     FW_Fixed hackResult;
  239.     hackResult.fRep = ::Frac2Fix(FracSin(angleRadians.fRep));
  240.     return hackResult;
  241. #else
  242.     return FW_Sin(angleRadians);
  243. #endif
  244. }
  245.  
  246. //----------------------------------------------------------------------------------------
  247. // CAnalogView::GetTickColor
  248. //----------------------------------------------------------------------------------------
  249.  
  250. static void GetTickColor(const FW_CColor& backgroundColor, FW_CColor& color)
  251. {
  252.     color = backgroundColor;
  253.     
  254.     if(color.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  255.         color += 0x40; // make it brighter
  256.     else
  257.         color -= 0x40; // make it darker
  258. }
  259.  
  260. //----------------------------------------------------------------------------------------
  261. // CAnalogView::GetHoursColor - Get the color of the hours hand
  262. //----------------------------------------------------------------------------------------
  263.  
  264. static void GetHoursColor(const FW_CColor& backgroundColor, FW_CColor& color)
  265. {
  266.     color = backgroundColor;
  267.     
  268.     if(color.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  269.         color += 0x40; // make it brighter
  270.     else
  271.         color -= 0x40; // make it darker
  272. }
  273.  
  274. //----------------------------------------------------------------------------------------
  275. // CAnalogView::GetMinutesColor - Get the color of the minutes hand
  276. //----------------------------------------------------------------------------------------
  277.  
  278. static void GetMinutesColor(const FW_CColor& backgroundColor, FW_CColor& color)
  279. {
  280.     GetHoursColor(backgroundColor, color);
  281.     
  282.     if(backgroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  283.         color += 0x40; // make it brighter
  284.     else
  285.         color -= 0x40; // make it darker
  286. }
  287.  
  288. //----------------------------------------------------------------------------------------
  289. // CAnalogView::GetSecondsColor - Get the color of the seconds hand
  290. //----------------------------------------------------------------------------------------
  291.  
  292. static void GetSecondsColor(const FW_CColor& backgroundColor, FW_CColor& color)
  293. {
  294.     if(backgroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  295.         color = FW_kRGBWhite;
  296.     else
  297.         color = FW_kRGBBlack;
  298. }
  299.  
  300. //----------------------------------------------------------------------------------------
  301. // CAnalogView::DoUpdateClockFacet
  302. //----------------------------------------------------------------------------------------
  303.  
  304. void CAnalogView::DoUpdateClockFacet(Environment* ev, 
  305.                                     FW_CGraphicContext* gc, 
  306.                                     const FW_CRect& clockRect, 
  307.                                     const FW_CTime& newTime)
  308. {
  309. FW_UNUSED(ev);
  310. FW_UNUSED(clockRect); // this is not in clock coordinates
  311.  
  312.         FW_CRect clockBox(-kClockRadius, -kClockRadius, kClockRadius, kClockRadius);
  313.  
  314.         FW_CColor backGroundColor;
  315.         fClockFrame->GetBackgroundColor(&backGroundColor);
  316.  
  317.         // erase the background
  318.         FW_CRectShape::RenderRect(*gc, clockBox, FW_kFill, backGroundColor);
  319.  
  320.         // and draw it
  321.         this->DrawClockFace(ev, gc);
  322.         
  323.         this->DrawHourHand(ev, gc, FW_IntToFixed(newTime.GetHour()), FW_IntToFixed(newTime.GetMinute()));
  324.         this->DrawMinuteHand(ev, gc, FW_IntToFixed(newTime.GetMinute()));
  325.         this->DrawSecondHand(ev, gc, FW_IntToFixed(newTime.GetSecond()));
  326. }
  327.  
  328. //----------------------------------------------------------------------------------------
  329. // CAnalogView::Draw
  330. //----------------------------------------------------------------------------------------
  331.  
  332. void CAnalogView::Draw(Environment *ev, ODFacet* odFacet, ODShape* invalidShape)
  333. {
  334.     ODCanvas* canvas = odFacet->GetCanvas(ev);
  335.  
  336.     FW_CAcquiredODShape aqUsedShape(GetFrame(ev)->AcquireUsedShape(ev, NULL));
  337.     FW_CRect clockBox = FW_GetShapeBoundingBox(ev, aqUsedShape);
  338.  
  339.     FW_CMapping mapping(FW_kCustomConstrained);
  340.     FW_CPoint logicalExtent(FW_MultipliedByInt(kClockRadius, 2), FW_MultipliedByInt(kClockRadius, 2));
  341.  
  342.     FW_CAcquiredODTransform aqExternalTransform = odFacet->AcquireExternalTransform(ev, NULL);
  343.     
  344.     if( ! canvas->IsDynamic(ev) ) // we only draw offscreen when drawing to a dynamic canvas
  345.     {
  346.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  347.  
  348.         FW_CPoint deviceExtent(GetExtent(ev));
  349.  
  350.         mapping.SetExtents(ev, logicalExtent, deviceExtent);
  351.         
  352.         mapping.SetDeviceOrigin(ev, FW_Half(deviceExtent.x), FW_Half(deviceExtent.y));
  353.  
  354.         vc.SetMapping(mapping);
  355.  
  356.         DoUpdateClockFacet(ev, &vc, clockBox, fLastTime);
  357.     }
  358.     else
  359.     {
  360.         // The offscreen business for the analog view is different.
  361.         // the logical space (in which all drawing is performed) is
  362.         // fixed (-kClockRadius, -kClockRadius, kClockRadius, kClockRadius)
  363.         // while the physical (or device) space is the actual offscreen size,
  364.         // which is based on the used shape.
  365.  
  366.         FW_CPoint deviceExtent(clockBox.Width(), clockBox.Height());
  367.  
  368.         // create an offscreen bitmap
  369.         FW_CBitmap bits(FW_FixedToInt(deviceExtent.x), FW_FixedToInt(deviceExtent.y), 0, NULL);
  370.         
  371.         {
  372.             // create a graphics context so that we can draw _to_ the bitmap
  373.             FW_CBitmapContext vc(ev, bits);
  374.  
  375.             // set the logical and physical extents
  376.             mapping.SetExtents(ev, logicalExtent, deviceExtent);
  377.             
  378.             // set the device origin to the center
  379.             mapping.SetDeviceOrigin(ev, FW_Half(deviceExtent.x), FW_Half(deviceExtent.y));
  380.  
  381.             // install the mapping
  382.             vc.SetMapping(mapping);
  383.             
  384.             DoUpdateClockFacet(ev, &vc, clockBox, fLastTime);
  385.             
  386.             // ----- Set the Highlight -----
  387.             if (odFacet->GetHighlight(ev) == kODFullHighlight)
  388.             {
  389.                 FW_CRect clockRect(-kClockRadius, -kClockRadius, kClockRadius, kClockRadius);
  390.                 FW_CRectShape::RenderRect(vc, clockRect, FW_kFill, FW_CInk(FW_kSystemHilite));
  391.             }
  392.             // the bitmap context is destructed here, but the bits remain
  393.         }
  394.  
  395.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  396.             
  397.         // render the bitmap
  398.         FW_CBitmapShape::RenderBitmap(vc, bits, clockBox);
  399.     }
  400. }
  401.  
  402. //----------------------------------------------------------------------------------------
  403. // CAnalogView::DrawTicks
  404. //----------------------------------------------------------------------------------------
  405.  
  406. static void DrawTicks(FW_CGraphicContext* gc, FW_CBoundedShape& tickShape, 
  407.                      FW_Fixed xPos, FW_Fixed yPos, 
  408.                      FW_Fixed tickWidth)
  409. {
  410.     tickWidth = FW_DividedByInt(tickWidth, 2);
  411.     
  412.     tickShape.SetRectangle(FW_CRect(xPos - tickWidth, yPos - tickWidth, xPos + tickWidth, yPos + tickWidth));
  413.     tickShape.Render(*gc);
  414.  
  415.     tickShape.SetRectangle(FW_CRect( - xPos - tickWidth, yPos - tickWidth, - xPos + tickWidth, yPos + tickWidth));
  416.     tickShape.Render(*gc);
  417.  
  418.     tickShape.SetRectangle(FW_CRect(xPos - tickWidth, - yPos - tickWidth, xPos + tickWidth, - yPos + tickWidth));
  419.     tickShape.Render(*gc);
  420.  
  421.     tickShape.SetRectangle(FW_CRect( - xPos - tickWidth, - yPos - tickWidth, - xPos + tickWidth, - yPos + tickWidth));
  422.     tickShape.Render(*gc);
  423. }
  424.  
  425. //----------------------------------------------------------------------------------------
  426. // CAnalogView::DrawClockFace
  427. //----------------------------------------------------------------------------------------
  428.  
  429. void CAnalogView::DrawClockFace(Environment* ev, FW_CGraphicContext* gc)
  430. {
  431. FW_UNUSED(ev);
  432.     // ----- Erase under the clock -----
  433.     FW_CRect ovalRect(-kClockRadius, -kClockRadius, kClockRadius, kClockRadius);
  434.     
  435.     FW_CColor backGroundColor;
  436.     fClockFrame->GetBackgroundColor(&backGroundColor);
  437.  
  438.     FW_CColor tickColor;
  439.     GetTickColor(backGroundColor, tickColor);
  440.  
  441.     FW_CColor rimColor;
  442.     if(backGroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  443.         rimColor = FW_kRGBWhite;
  444.     else
  445.         rimColor = FW_kRGBBlack;
  446.         
  447.     FW_COvalShape::RenderOval(
  448.         *gc,
  449.         ovalRect,
  450.         FW_kFrame,
  451.         rimColor,
  452.         FW_CStyle(kClockRadius * FW_DoubleToFixed(0.02)));
  453.  
  454.     // ----- Render the "ODF" string -----
  455.     
  456.     FW_CString faceString = fClockFrame->GetClockContent(ev)->GetFaceString();
  457.     
  458.     FW_CTextShape::RenderText(
  459.         *gc,
  460.         faceString,
  461.         FW_CPoint(FW_IntToFixed(0), - FW_Half(kClockRadius)),
  462.         FW_CFont(FW_GetHelveticaFontName(), FW_kItalic, FW_DividedByInt(kClockRadius, 5)),
  463.         FW_kTextAlignHCenter | FW_kTextAlignBaseLine, FW_CInk(tickColor, FW_kRGBWhite, FW_kOr));
  464.     
  465.     
  466.     // ----- Render the ticks around the clock face -----
  467.  
  468.     // create a rectangle shape for the small ticks
  469.     FW_CRectShape rectShape;
  470.     rectShape.SetInk(FW_CInk(tickColor));
  471.  
  472.     // create an oval shape for the large ticks
  473.     FW_COvalShape ovalShape(ovalRect, FW_kFill);
  474.     ovalShape.SetInk(FW_CInk(tickColor));
  475.  
  476.     // Use the symmetry of the clock face to speed up the calculations for drawing the face
  477.     // We only need to calculate points for 45 degrees of the circle.  The remaining points can
  478.     // be inferred from these points
  479.     
  480.     short angle = 0;
  481.     short fiveMinute = 0;
  482.  
  483.     while (angle < 45)
  484.     {
  485.         // ----- convert angle to radians -----
  486.         FW_Fixed radians = FW_DividedByInt(kFxPI * FW_IntToFixed(90 - angle), 180);
  487.         FW_Fixed cosRadian = FixedCosine(radians);
  488.         FW_Fixed sinRadian = FixedSine(radians);
  489.         
  490.         FW_Fixed tickRadius = kClockRadius * FW_DoubleToFixed(0.9);
  491.         FW_Fixed xTick = tickRadius * cosRadian;
  492.         FW_Fixed yTick = tickRadius * sinRadian;
  493.  
  494.         if(fiveMinute == 0)
  495.         {
  496.             fiveMinute = 4;
  497.             
  498.             FW_Fixed tickWidth = FW_DividedByInt(kClockRadius, 15);
  499.             
  500.             DrawTicks(gc, ovalShape, xTick, yTick, tickWidth);
  501.             
  502.             DrawTicks(gc, ovalShape, yTick, xTick, tickWidth);
  503.         }
  504.         else
  505.         {
  506.             fiveMinute--;
  507.             
  508.             FW_Fixed tickWidth = FW_DividedByInt(kClockRadius, 30);
  509.             
  510.             DrawTicks(gc, rectShape, xTick, yTick, tickWidth);
  511.             
  512.             DrawTicks(gc, rectShape, yTick, xTick, tickWidth);
  513.         }
  514.         
  515.         angle += 6; // a tick occurs every six degrees
  516.     }
  517. }
  518.  
  519. //----------------------------------------------------------------------------------------
  520. //    CalcPoint
  521. //----------------------------------------------------------------------------------------
  522.  
  523. static FW_CPoint CalcPoint(FW_Fixed radius, FW_Fixed angle)
  524. {
  525.     return FW_CPoint(
  526.         radius * FixedSine(angle),
  527.         - radius * FixedCosine(angle));
  528. }
  529.  
  530. //----------------------------------------------------------------------------------------
  531. // ::DegreesToRadians
  532. //----------------------------------------------------------------------------------------
  533.  
  534. static inline FW_Fixed DegreesToRadians(FW_Fixed degrees)
  535. {
  536.     return FW_DividedByInt(degrees * kFxPI, 180);
  537. }
  538.  
  539. //----------------------------------------------------------------------------------------
  540. // CAnalogView::DrawHourHand
  541. //----------------------------------------------------------------------------------------
  542.  
  543. void CAnalogView::DrawHourHand(Environment* ev, 
  544.                                 FW_CGraphicContext* gc, 
  545.                                 FW_Fixed hour, 
  546.                                 FW_Fixed minute)
  547. {
  548. FW_UNUSED(ev);
  549.     FW_Fixed hourRadius = kClockRadius * FW_DoubleToFixed(0.5);    // Make the hour hand short
  550.  
  551.     if (hour > FW_IntToFixed(11))
  552.         hour -= FW_IntToFixed(12);
  553.     
  554.     // ----- Divide the clock face into degrees.  
  555.     // ----- Hour hand falls on every five minutes or every 30 degrees
  556.     FW_Fixed clockDegrees = FW_MultipliedByInt(hour, 30) + FW_Half(minute);
  557.     FW_Fixed radians = ::DegreesToRadians(clockDegrees);
  558.     
  559.     // calculate the point where the tip of the hand should end
  560.     FW_CPoint newPoint = ::CalcPoint(hourRadius, radians);
  561.  
  562.     const short arcDegrees = 14; // the hand is a filled arc this many degrees wide
  563.     // adjust angle so the base of the arc is centered
  564.     short angle = FW_FixedToInt(clockDegrees) - (arcDegrees / 2);
  565.     
  566.     // flip it to the other side
  567.     angle += 180;
  568.     
  569.     FW_CRect faceRect(-hourRadius, -hourRadius, hourRadius, hourRadius);
  570.  
  571.     // offset the reference rectangle so the center is where we want the vertex of the arc
  572.     faceRect.Offset(newPoint.x, newPoint.y);
  573.     
  574.  
  575.     FW_CColor backGroundColor;
  576.     fClockFrame->GetBackgroundColor(&backGroundColor);
  577.  
  578.     FW_CColor color;
  579.     GetHoursColor(backGroundColor, color);
  580.  
  581.     FW_CArcShape::RenderArc(*gc, faceRect,
  582.                             angle,
  583.                             arcDegrees,
  584.                             FW_kFill,
  585.                             color);
  586.  
  587. }
  588.  
  589. //----------------------------------------------------------------------------------------
  590. // CAnalogView::DrawMinuteHand
  591. //----------------------------------------------------------------------------------------
  592.  
  593. void CAnalogView::DrawMinuteHand(Environment* ev, 
  594.                                 FW_CGraphicContext* gc, 
  595.                                 FW_Fixed minute)
  596. {
  597. FW_UNUSED(ev);
  598.     FW_Fixed minuteRadius = kClockRadius * FW_DoubleToFixed(0.8);  // Make the minute hand longer than the hour hand
  599.     
  600.     // ----- Divide the clock face into degrees.  
  601.     // ----- (360 degrees divided by 60 minutes is 6 degrees per minute)
  602.     FW_Fixed clockDegrees = FW_MultipliedByInt(minute, 6);
  603.     FW_Fixed radians = ::DegreesToRadians(clockDegrees);
  604.  
  605.     FW_CPoint newPoint = ::CalcPoint(minuteRadius, radians);
  606.  
  607.     const short arcDegrees = 6; // the hand is a filled arc this many degrees wide
  608.     // adjust angle so the base of the arc is centered
  609.     short angle = FW_FixedToInt(clockDegrees) - (arcDegrees / 2);
  610.  
  611.     // flip it to the other side
  612.     angle += 180;
  613.     
  614.     FW_CRect faceRect(-minuteRadius, -minuteRadius, minuteRadius, minuteRadius);
  615.  
  616.     // offset the reference rectangle so the center is where we want the vertex of the arc
  617.     faceRect.Offset(newPoint.x, newPoint.y);
  618.  
  619.     FW_CColor backGroundColor;
  620.     fClockFrame->GetBackgroundColor(&backGroundColor);
  621.  
  622.     FW_CColor color;
  623.     GetMinutesColor(backGroundColor, color);
  624.  
  625.     FW_CArcShape::RenderArc(*gc, faceRect,
  626.                             angle,
  627.                             arcDegrees,
  628.                             FW_kFill,
  629.                             color);
  630.  
  631. }
  632.  
  633. //----------------------------------------------------------------------------------------
  634. // CAnalogView::DrawSecondHand
  635. //----------------------------------------------------------------------------------------
  636.  
  637. void CAnalogView::DrawSecondHand(Environment* ev, 
  638.                                 FW_CGraphicContext* gc, 
  639.                                 FW_Fixed second)
  640. {
  641. FW_UNUSED(ev);
  642.     FW_Fixed secondRadius = kClockRadius * FW_DoubleToFixed(0.9);    // A nice sweeping second hand
  643.  
  644.     // ----- Divide the clock face into degrees.  
  645.     // ----- (360 degrees divided by 60 seconds is 6 degrees per second)
  646.     FW_Fixed radians = ::DegreesToRadians(FW_MultipliedByInt(second, 6));
  647.     
  648.     FW_CPoint newPoint = ::CalcPoint(secondRadius, radians);
  649.  
  650.     FW_CLineShape lineShape(FW_kZeroPoint, newPoint);
  651.     FW_CColor backGroundColor;
  652.     fClockFrame->GetBackgroundColor(&backGroundColor);
  653.  
  654.     FW_CColor color;
  655.     GetSecondsColor(backGroundColor, color);
  656.  
  657.     lineShape.SetInk(FW_CInk(color));
  658.     lineShape.Render(*gc);
  659.     
  660.     // draw a "pin" in the center
  661.     FW_Fixed pinRadius = FW_DividedByInt(kClockRadius, 40);
  662.     FW_CRect pinRect(-pinRadius, -pinRadius, pinRadius, pinRadius);
  663.     FW_COvalShape::RenderOval(*gc, pinRect, FW_kFill, color);
  664. }
  665.  
  666. //----------------------------------------------------------------------------------------
  667. //    CAnalogView::CreateUsedShape
  668. //----------------------------------------------------------------------------------------
  669.  
  670. ODShape* CAnalogView::CreateUsedShape(Environment* ev, const FW_CRect& suggestedUsedRect)
  671. {    
  672.     FW_CRect rect(suggestedUsedRect);
  673.     FW_Fixed height = suggestedUsedRect.Height();
  674.     FW_Fixed width = suggestedUsedRect.Width();
  675.     
  676.     if (height < width)
  677.         rect.right = rect.left + height;
  678.     else
  679.         rect.bottom = rect.top + width;
  680.  
  681.     rect.PlaceInCenterOf(suggestedUsedRect);
  682.     
  683.     return ::FW_CreateOvalODShape(ev, rect);
  684. }
  685.  
  686. //----------------------------------------------------------------------------------------
  687. //    CAnalogView::Create
  688. //----------------------------------------------------------------------------------------
  689.  
  690. void* CAnalogView::Create(FW_CReadableStream& stream, FW_ClassTypeConstant type)
  691. {
  692. FW_UNUSED(stream);
  693. FW_UNUSED(type);
  694.     FW_SOMEnvironment ev;
  695.     return FW_NEW(CAnalogView, (ev));
  696. }
  697.  
  698. //----------------------------------------------------------------------------------------
  699. //    CDrawView::Destroy
  700. //----------------------------------------------------------------------------------------
  701.  
  702. void CAnalogView::Destroy(void* object, FW_ClassTypeConstant type)
  703. {
  704. FW_UNUSED(type);
  705.     CAnalogView* self = (CAnalogView*) object;
  706.     delete self;
  707. }
  708.  
  709. //========================================================================================
  710. // class CDigitalView
  711. //========================================================================================
  712.  
  713. FW_DEFINE_AUTO(CDigitalView)
  714. FW_DEFINE_CLASS_M1(CDigitalView, CClockView)
  715.     
  716. const FW_ClassTypeConstant LDigitalView = FW_TYPE_CONSTANT('D','G','V','W');
  717. FW_REGISTER_ARCHIVABLE_CLASS(LDigitalView, CDigitalView, CDigitalView::Create, FW_CView::Read, CDigitalView::Destroy, FW_CView::Write)
  718.  
  719.     
  720. //----------------------------------------------------------------------------------------
  721. // CDigitalView::CDigitalView
  722. //----------------------------------------------------------------------------------------
  723.  
  724. CDigitalView::CDigitalView(Environment* ev) :
  725.     CClockView(ev),
  726.     fFont(FW_kCourier12)
  727. {
  728.     FW_END_CONSTRUCTOR
  729. }
  730.  
  731. //----------------------------------------------------------------------------------------
  732. // CDigitalView::~CDigitalView
  733. //----------------------------------------------------------------------------------------
  734.  
  735. CDigitalView::~CDigitalView()
  736. {
  737.     FW_START_DESTRUCTOR
  738. }
  739.  
  740. //----------------------------------------------------------------------------------------
  741. //    CDigitalView::GetDigitalClockRect
  742. //----------------------------------------------------------------------------------------
  743.  
  744. FW_CRect CDigitalView::GetDigitalClockRect(Environment* ev, const FW_CRect& suggestedUsedRect)
  745. {
  746.     // ----- Calculate a default rectangle size for a digital clock
  747.     FW_CString32 digitalString;
  748.     {
  749.         FW_PSharedLibraryResourceFile resFile(ev);
  750.         ::FW_LoadStringByID(ev, resFile, kClockFaceStrings, FW_kMultiStringRes, kClockDigitalWidthString, digitalString);
  751.     }
  752.     
  753.     FW_CTextShape textShape(digitalString,
  754.                             FW_IntToFixed(0),
  755.                             FW_IntToFixed(0),
  756.                             fFont);
  757.  
  758.     FW_CScreenContext sc(ev); // we need a context for measuring the text
  759.  
  760.     short boxHeight = FW_FixedToInt(suggestedUsedRect.Height());
  761.     short boxWidth = FW_FixedToInt(suggestedUsedRect.Width());
  762.     short fontSize = boxHeight;
  763.     FW_CRect boundsRect;
  764.     
  765.     for(;;)
  766.     {
  767.         fFont.SetFontSize(FW_IntToFixed(fontSize));
  768.         FW_CFontMetrics metrics;
  769.         fFont.GetFontMetrics(sc, metrics);
  770.         short fontHeight = metrics.GetFontHeight();
  771.         if(fontHeight > boxHeight)
  772.         {
  773.             fontSize = ( (long) fontSize * boxHeight) / fontHeight;
  774.         }
  775.         else
  776.         {
  777.             textShape.GetBounds(sc, boundsRect);
  778.             short textWidth = FW_FixedToInt(boundsRect.Width());
  779.             if(boxWidth < textWidth)
  780.                 fontSize = ( (long) fontSize * boxWidth) / textWidth;
  781.             else
  782.             {
  783.                 break;
  784.             }
  785.         }
  786.         
  787.         if(fontSize == 0) // punt
  788.         {
  789.             fFont.SetFontSize(FW_IntToFixed(12));
  790.             textShape.GetBounds(sc, boundsRect);
  791.             break;
  792.         }
  793.     }
  794.     
  795.     boundsRect.PlaceInCenterOf(suggestedUsedRect);
  796.  
  797.     return boundsRect;
  798. }
  799.         
  800. //----------------------------------------------------------------------------------------
  801. // CDigitalView::DoUpdateClockFacet
  802. //----------------------------------------------------------------------------------------
  803.  
  804. void CDigitalView::DoUpdateClockFacet(Environment* ev, 
  805.                                     FW_CGraphicContext* gc, 
  806.                                     const FW_CRect& clockRect, 
  807.                                     const FW_CTime& newTime)
  808. {
  809. FW_UNUSED(ev);
  810.     FW_CString255 timeString;
  811.     newTime.GetTimeString(timeString, TRUE);
  812.         
  813.     FW_CColor backGroundColor;
  814.     fClockFrame->GetBackgroundColor(&backGroundColor);
  815.  
  816.     FW_CColor contrastColor;
  817.     if(backGroundColor.IsDarkerThan(FW_CColor(FW_kRGBGray)))
  818.         contrastColor = FW_kRGBWhite;
  819.     else
  820.         contrastColor = FW_kRGBBlack;
  821.  
  822.     FW_CRectShape::RenderRect(*gc, clockRect, FW_kFill, backGroundColor);
  823.     FW_CRectShape::RenderRect(*gc, clockRect, FW_kFrame, contrastColor);
  824.     
  825.     FW_CTextShape::RenderText(*gc, timeString, clockRect.Center(), fFont,
  826.         FW_kTextAlignHCenter | FW_kTextAlignVCenter,
  827.         FW_CInk(contrastColor, FW_kRGBWhite, FW_kOr));
  828. }
  829.  
  830. //----------------------------------------------------------------------------------------
  831. // CDigitalView::Draw
  832. //----------------------------------------------------------------------------------------
  833.  
  834. void CDigitalView::Draw(Environment* ev, ODFacet* odFacet, ODShape* invalidShape)
  835. {    
  836.     ODCanvas* canvas = odFacet->GetCanvas(ev);
  837.  
  838.     FW_CAcquiredODShape aqUsedShape(GetFrame(ev)->AcquireUsedShape(ev, NULL));
  839.     FW_CRect clockBox = FW_GetShapeBoundingBox(ev, aqUsedShape);
  840.     clockBox.Inset(kClockRadius * -kUsedShapeInset, kClockRadius * -kUsedShapeInset);
  841.  
  842.     if( ! canvas->IsDynamic(ev) ) // we only draw offscreen when drawing to a dynamic canvas
  843.     {
  844.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  845.  
  846.         DoUpdateClockFacet(ev, &vc, clockBox, fLastTime);
  847.     }
  848.     else
  849.     {
  850.         // Different from the analog view, the digital view's extent is
  851.         // the same as the offscreen device, but we have to fiddle with the origin
  852.         // because the offscreen has a default origin of (0,0)
  853.  
  854.         // create an offscreen bitmap
  855.         FW_CBitmap bits(FW_FixedToInt(clockBox.Width()), FW_FixedToInt(clockBox.Height()), 0, NULL);
  856.         
  857.         {
  858.             // create a graphics context so that we can draw _to_ the bitmap
  859.             FW_CBitmapContext vc(ev, bits);
  860.  
  861.             // because the mapping of the bitmap has its origin at 0, and the
  862.             // used shape bounds upon which DoUpdateClockFacet bases its mapping
  863.             // is probably not at 0, 0, we change the logical mapping so that the clock
  864.             // draws in the right spot.
  865.             FW_CMapping mapping;
  866.             vc.GetMapping(mapping);
  867.             mapping.SetLogicalOrigin(ev, clockBox.left, clockBox.top);
  868.             vc.SetMapping(mapping);
  869.  
  870.             // we don't need to prepare the offscreen since DoUpdateClockFacet
  871.             // will paint the whole image. Otherwise...
  872.             // FW_CRectShape::RenderRect(vc, box, FW_kFill, FW_kRGBWhite);
  873.             
  874.             DoUpdateClockFacet(ev, &vc, clockBox, fLastTime);
  875.             
  876.             // ----- Set the Highlight -----
  877.             if (odFacet->GetHighlight(ev) == kODFullHighlight)
  878.                 FW_CRectShape::RenderRect(vc, clockBox, FW_kFill, FW_CInk(FW_kSystemHilite));
  879.             
  880.             // the bitmap context is destructed here, but the bits remain
  881.         }
  882.  
  883.         FW_CViewContext vc(ev, this, odFacet, invalidShape);
  884.         
  885.         // render the bitmap
  886.         FW_CBitmapShape::RenderBitmap(vc, bits, clockBox);
  887.     }
  888. }
  889.  
  890. //----------------------------------------------------------------------------------------
  891. //    CDigitalView::CreateUsedShape
  892. //----------------------------------------------------------------------------------------
  893.  
  894. ODShape* CDigitalView::CreateUsedShape(Environment* ev, const FW_CRect& suggestedUsedRect)
  895. {    
  896.     FW_CRect rect = GetDigitalClockRect(ev, suggestedUsedRect);
  897.  
  898.     rect.Inset(kClockRadius * kUsedShapeInset, kClockRadius * kUsedShapeInset);
  899.  
  900.     // Below shows two equivalent methods for specifying the used shape, the first
  901.     // as a region, and the second as an ODPolygon with one contour.
  902.     
  903.     // The first method is not likely to fail, so the second method will probably never
  904.     // get used. It's here as an example. [BRP]
  905.  
  906.     const short kCorners = 4;
  907.     FW_CPoint rectCorners[kCorners] = {
  908.         FW_CPoint(rect.left, rect.top), 
  909.         FW_CPoint(rect.right, rect.top), 
  910.         FW_CPoint(rect.right, rect.bottom), 
  911.         FW_CPoint(rect.left, rect.bottom)
  912.     };
  913.     
  914.     ODShape *shape = ::FW_CreatePolygonODShape(ev, kCorners, rectCorners);
  915.  
  916.     if(shape == NULL)
  917.         shape = ::FW_NewODShape(ev, rect);
  918.         
  919.     return shape;
  920. }
  921.  
  922. //----------------------------------------------------------------------------------------
  923. //    CDigitalView::Create
  924. //----------------------------------------------------------------------------------------
  925.  
  926. void* CDigitalView::Create(FW_CReadableStream& stream, FW_ClassTypeConstant type)
  927. {
  928. FW_UNUSED(stream);
  929. FW_UNUSED(type);
  930.  
  931.     FW_SOMEnvironment ev;
  932.     return FW_NEW(CDigitalView, (ev));
  933. }
  934.  
  935. //----------------------------------------------------------------------------------------
  936. //    CDigitalView::Destroy
  937. //----------------------------------------------------------------------------------------
  938.  
  939. void CDigitalView::Destroy(void* object, FW_ClassTypeConstant type)
  940. {
  941. FW_UNUSED(type);
  942.  
  943.     CDigitalView* self = (CDigitalView*) object;
  944.     delete self;
  945. }
  946.  
  947.