home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 January / macformat-020.iso / Shareware City / Developers / apps.to.go / pbClock / Window.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-09  |  59.9 KB  |  1,984 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* •••• Except for a couple of places (marked by ••••), this is the only file that
  20. **        changed to turn AppWannabe into pbClock.  Since the changes here are relatively
  21. **        extensive, I didn't mark them with ••••, although they are commented. */
  22.  
  23.  
  24.  
  25. /*****************************************************************************/
  26.  
  27.  
  28.  
  29. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  30. #include "App.defs.h"        /* Get various application definitions.            */
  31. #include "App.protos.h"        /* Get the prototypes for application.            */
  32.  
  33. #ifndef __ERRORS__
  34. #include <Errors.h>
  35. #endif
  36.  
  37. #ifndef __FONTS__
  38. #include <Fonts.h>
  39. #endif
  40.  
  41. #ifndef __LOWMEM__
  42. #include <LowMem.h>
  43. #endif
  44.  
  45. #ifndef __RESOURCES__
  46. #include <Resources.h>
  47. #endif
  48.  
  49. #ifndef __TOOLUTILS__
  50. #include <ToolUtils.h>
  51. #endif
  52.  
  53. #ifndef __UTILITIES__
  54. #include "Utilities.h"
  55. #endif
  56.  
  57.  
  58.  
  59. /*****************************************************************************/
  60.  
  61.  
  62.  
  63. static void        ContentCommon(WindowPtr window, EventRecord *event, short cnum, short action,
  64.                               RGBColor *oldc, RGBColor *newc);
  65. static void        FetchClockValues(FileRecHndl frHndl, short field);
  66. static Boolean    TrackArrowProc(ControlHandle ctl, short part, EventRecord *event);
  67. static Boolean    TrackRadioProc(ControlHandle ctl, short part, EventRecord *event);
  68. static void        SwitchClocks(FileRecHndl frHndl, EventRecord *event, short cnum);
  69. static void        HiliteTabAndReturn(FileRecHndl frHndl);
  70. static Boolean    DigitsOnly(TEHandle teHndl, EventRecord *event, short *handled);
  71. static OSErr    BackLayerProc(LayerObj theLayer, short message);
  72. static OSErr    WorkLayerProc(LayerObj theLayer, short message);
  73. static short    MyGetRadioButtonChoice(WindowPtr window, short famNum);
  74. static short    MyGetButtonVariant(ControlHandle ctl, Boolean *stop);
  75.  
  76. static Point    gArcLocs[60];        /* Used to cache data for speed up. */
  77.  
  78. static LayerObj    gBackLayer[2], gWorkLayer;
  79.     /* These are globals so that they can be created just once.  This speeds up
  80.     ** clock redraws after the first one.  In the DrawClocks function, first
  81.     ** a windowLayer is created that is the size/location of the left clock.
  82.     ** Then, if gWorkLayer hasn't been created yet, it is created as a below-layer
  83.     ** for windowLayer.  I force a depth of 1 for gWorkLayer since the clocks are
  84.     ** only b/w.
  85.     **
  86.     ** Since gWorkLayer and gBackLayer are used as a pair, when gWorkLayer is created,
  87.     ** so is gBackLayer.  If they were already created when DrawClocks is called, then
  88.     ** they are inserted into the windowLayer "chain" just below windowLayer.
  89.     ** Once they are created and attached, they are used, and then just windowLayer
  90.     ** is disposed of, leaving gWorkLayer and gBackLayer around for the next usage.
  91.     **
  92.     ** The only other trick with these guys is that the AppsToGo editor can be used
  93.     ** on this application while it is running.  It is possible that the left and right
  94.     ** clock controls (which are just data controls, and used for their size/position)
  95.     ** are changed.  This would mean that the cached layers could be wrong.  Therefore
  96.     ** in the OpenApplication function (which is called at startup of the application
  97.     ** and at editor restart time), we need to get rid of them if they exist.  By
  98.     ** getting rid of them, the next time that DrawClocks is called, it will create
  99.     ** them again, based on the new size of the clock controls. */
  100.     
  101. typedef struct {
  102.     long    drawTime;
  103.     short    numMoves, timeControl, numRemaining, numTimeControls;
  104. } DrawClocksInfo;
  105.     /* This is the info that the layer procs need.  We put it into a structure
  106.     ** because when we create a layer, we only get to pass in a single refcon
  107.     ** value.  The refcon value will point to one of these structures. */
  108.  
  109.  
  110.  
  111. /*****************************************************************************/
  112.  
  113.  
  114.  
  115. Boolean        gNoDefaultDocument = false;
  116.                     /* Set to true if app should boot with no default document. */
  117.                     /* This tells DTS.Lib..framework what you want. */
  118.  
  119. OSType        gAppWindowType = kDocFileType;    /* Main document type. */
  120. long        gAppWindowAttr = kwAppWindow;    /* Main window attributes. */
  121.  
  122. short        gMinVersion    = kMinVersion;    /* Minimum document version app can support. */
  123. short        gMaxVersion    = kMaxVersion;    /* Maximum document version app can support. */
  124.                                             /* More informing DTS.Lib..framework. */
  125.  
  126. extern long            gTimer;
  127.  
  128. extern short        gPrintPage;                /* Non-zero means we are printing. */
  129.                                             /* DTS.Lib..framework global. */
  130.  
  131. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  132. extern CursPtr        gCursorPtr;                /* to know about these things. */
  133.                                             /* Above are DTS.Lib..framework globals. */
  134.  
  135. extern GetButtonVariantProcPtr        gGetButtonVariant;
  136.  
  137.  
  138.  
  139. /*****************************************************************************/
  140. /*****************************************************************************/
  141.  
  142.  
  143.  
  144. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  145.  
  146. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  147. ** You are passed an empty region.  You are supposed to add any custom frame
  148. ** parts that this document uses.  Typically there are no frame portions, as
  149. ** they are accounted for in other ways.  The scrollbars and grow icon will
  150. ** automatically be contributed to the calculation of the frame region.
  151. ** If you use sidebars, these are also added in automatically.  This is only
  152. ** used if the frame region is more complicated than can automatically be
  153. ** handled.  So, almost always, you will simply leave the region empty. */
  154.  
  155. #pragma segment TheDoc
  156. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  157. {
  158. #ifndef __MWERKS__
  159. #pragma unused (frHndl, window, rgn)
  160. #endif
  161. }
  162.  
  163.  
  164.  
  165. /*****************************************************************************/
  166.  
  167.  
  168.  
  169. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  170.  
  171. /* This is called (by DoContentClick()) when a mouse-down event occurs in the content of
  172. ** a window.  Other applications might want to call FindControl, TEClick, etc., to
  173. ** further process the click. */
  174.  
  175. #pragma segment TheDoc
  176. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  177. {
  178. #ifndef __MWERKS__
  179. #pragma unused (firstClick)
  180. #endif
  181.  
  182.     WindowPtr        oldPort;
  183.     FileRecHndl        frHndl;
  184.     TreeObjHndl        root;
  185.     ControlHandle    ctl;
  186.     short            action, cnum, lr;
  187.     RGBColor        oldc, newc;
  188.  
  189.     GetPort(&oldPort);
  190.     SetPort(window);
  191.     if (gQDVersion) {
  192.         GetBackColor(&oldc);
  193.         newc.red = newc.green = newc.blue = 0xFFFF;
  194.         RGBBackColor(&newc);
  195.     }    /* The window color is non-white, but the control area containing TEControls
  196.         ** is white.  Therefore we have to set the backColor to white before doing
  197.         ** teControl operations, or else we will get some weird results. */
  198.  
  199.     frHndl = (FileRecHndl)GetWRefCon(window);
  200.     root   = (*frHndl)->d.doc.root;
  201.  
  202.     cnum = IsCtlEvent(window, event, &ctl, &action);
  203.  
  204.     switch (cnum) {
  205.  
  206.         case kLeftClockRadio:
  207.         case kRightClockRadio:
  208.         case kBothClocksRadio:
  209.         case kSetClocks:
  210.         case kResetClocks:
  211.         case kStartClocks:
  212.         case kPauseClocks:
  213.         case kTabButton:
  214.         case kReturnButton:
  215.             ContentCommon(window, event, cnum, action, &oldc, &newc);
  216.             break;
  217.  
  218.         default:
  219.             if (cnum) {
  220.                 FetchClockValues(frHndl, 0);
  221.                     /* For any other control click, fetch the values.  FetchClockValues also
  222.                     ** properly closes fields, which is really why we are doing this. */
  223.  
  224.                 if ((cnum >= kTimeControl1Hours) && (cnum < kTimeControlEndRange)) {
  225.                     mDerefRoot(root)->timeRemaining[0] = mDerefRoot(root)->timeControl[0];
  226.                     mDerefRoot(root)->timeRemaining[1] = mDerefRoot(root)->timeControl[3];
  227.                     lr = MyGetRadioButtonChoice(window, 0);
  228.                     DrawClock(frHndl, (lr & 0x01));
  229.                     if (lr == 2) DrawClock(frHndl, 1);
  230.                 }
  231.             }
  232.             break;
  233.     }
  234.  
  235.     if (gQDVersion)
  236.         RGBBackColor(&oldc);
  237.  
  238.     SetPort(oldPort);
  239.     return;
  240. }
  241.  
  242. static void    ContentCommon(WindowPtr window, EventRecord *event, short cnum, short action,
  243.                           RGBColor *oldc, RGBColor *newc)
  244. {
  245.     ControlHandle    ctl, cc;
  246.     FileRecHndl        frHndl;
  247.     TreeObjHndl        root;
  248.     short            rs, nm, i;
  249.     Str15            pstr;
  250.     long            diff, timer, t1, t2;
  251.     TEHandle        te;
  252.  
  253.     frHndl = (FileRecHndl)GetWRefCon(window);
  254.     root   = (*frHndl)->d.doc.root;
  255.  
  256.     switch (cnum) {
  257.  
  258.         case kLeftClockRadio:
  259.         case kRightClockRadio:
  260.         case kBothClocksRadio:
  261.             InitContent(frHndl, window);
  262.             if (MyGetRadioButtonChoice(window, 0) == 2) {        /* If "both" radio button selected... */
  263.                 FetchClockValues(frHndl, 0);
  264.                 mDerefRoot(root)->timeRemaining[0] = mDerefRoot(root)->timeControl[0];
  265.                 mDerefRoot(root)->timeRemaining[1] = mDerefRoot(root)->timeControl[3];
  266.                     /* If both clocks being set, propagate clock setting. */
  267.             }
  268.             DrawClocks(frHndl);
  269.             break;
  270.  
  271.         case kSetClocks:
  272.             RGBBackColor(oldc);
  273.             DisplayControlSet(window, 'pbc2', kwHideAll);        /* Hide the run clocks controls. */
  274.             RGBBackColor(newc);
  275.             DisplayControlSet(window, 'pbc3', kwShowAll);        /* Show the set clocks controls. */
  276.  
  277.             CNum2Ctl(window, kStartClocksText, &cc);    /* The title of kStartClocksText is used to        */
  278.             pcpy(pstr, (*cc)->contrlTitle);                /* determine if the clocks are running.  Therefore */
  279.             CNum2Ctl(window, kPauseClocks, &cc);        /* flag the clocks as stopped when setting them.   */
  280.             SetStyledCTitle(cc, pstr);
  281.  
  282.             CTEWindActivate(window, true);                /* Reactivate last-active TEControl.         */
  283.             te = CTEFindActive(window);                    /* Show selection range of active TEControl. */
  284.             if (te) CTESetSelect(0, 999, te);
  285.  
  286.             mDerefRoot(root)->timeRemaining[0] = mDerefRoot(root)->timeControl[0];
  287.             mDerefRoot(root)->timeRemaining[1] = mDerefRoot(root)->timeControl[3];
  288.                 /* The timeRemaining fields are what is drawn in the clocks, so reset their
  289.                 ** values so that we can display the time that the user is entering. */
  290.  
  291.             mDerefRoot(root)->rightStart = -1;            /* Nobody has started the clocks yet flag. */
  292.             mDerefRoot(root)->numMoves[0] = 0;
  293.             mDerefRoot(root)->numMoves[1] = 0;
  294.             DrawClocks(frHndl);                            /* Show the user the reset values. */
  295.             break;
  296.  
  297.         case kResetClocks:
  298.             mDerefRoot(root)->timeRemaining[0] = mDerefRoot(root)->timeControl[0];
  299.             mDerefRoot(root)->timeRemaining[1] = mDerefRoot(root)->timeControl[3];
  300.             mDerefRoot(root)->rightStart = -1;            /* Nobody has started the clocks yet flag. */
  301.             mDerefRoot(root)->numMoves[0] = 0;
  302.             mDerefRoot(root)->numMoves[1] = 0;
  303.             (*frHndl)->d.doc.timer = event->when;        /* Sync up the ticks. */
  304.             CNum2Ctl(window, kPauseClocks, &ctl);
  305.             (*ctl)->contrlValue = 0;                    /* Reset balloon help for control. */
  306.             CNum2Ctl(window, kStopClocksText, &cc);        /* Put clocks in non-pause play mode. */
  307.             pcpy(pstr, (*cc)->contrlTitle);
  308.             SetStyledCTitle(ctl, pstr);
  309.             DrawClocks(frHndl);                            /* Show the user the reset values. */
  310.             HiliteTabAndReturn(frHndl);                    /* Adjust the rest of the controls. */
  311.             break;
  312.  
  313.         case kStartClocks:
  314.             FetchClockValues(frHndl, 0);
  315.                 /* Get the control values and place them into the document.
  316.                 ** This also closes any open field. */
  317.             mDerefRoot(root)->timeRemaining[0] = mDerefRoot(root)->timeControl[0];
  318.             mDerefRoot(root)->timeRemaining[1] = mDerefRoot(root)->timeControl[3];
  319.             mDerefRoot(root)->rightStart = -1;            /* Nobody has started the clocks yet flag. */
  320.             mDerefRoot(root)->numMoves[0] = 0;
  321.             mDerefRoot(root)->numMoves[1] = 0;
  322.             DrawClocks(frHndl);                            /* Show the user the reset values. */
  323.             CNum2Ctl(window, kStopClocksText, &ctl);
  324.             pcpy(pstr, (*ctl)->contrlTitle);
  325.             CNum2Ctl(window, kPauseClocks, &ctl);
  326.             (*ctl)->contrlValue = 0;
  327.             SetStyledCTitle(ctl, pstr);
  328.             CNum2Ctl(window, kNoClockButtonClick, &ctl);
  329.             (*ctl)->contrlVis = 0;                        /* Prevent an erase when the kwHideAll is done. */
  330.             HiliteTabAndReturn(frHndl);
  331.             RGBBackColor(oldc);
  332.             DisplayControlSet(window, 'pbc3', kwHideAll);
  333.             RGBBackColor(newc);
  334.             DisplayControlSet(window, 'pbc2', kwShowAll);
  335.             (*frHndl)->d.doc.timer = event->when;        /* Sync up the ticks. */
  336.             break;
  337.  
  338.         case kPauseClocks:
  339.             CNum2Ctl(window, kPauseClocks, &ctl);
  340.             if (ClocksPaused(window)) {
  341.                 (*frHndl)->d.doc.timer = event->when;    /* If currently paused, start 'em up again. */
  342.                 (*ctl)->contrlValue = 0;
  343.                 CNum2Ctl(window, kStopClocksText, &cc);
  344.             }
  345.             else {
  346.                 ContentCommon(window, event, kUpdateClock, action, oldc, newc);
  347.                     /* This is just a cute way to call some common code. */
  348.                 (*ctl)->contrlValue = 2;                /* Clocks stopped has different balloon help. */
  349.                 CNum2Ctl(window, kStartClocksText, &cc);
  350.             }
  351.             pcpy(pstr, (*cc)->contrlTitle);
  352.             SetStyledCTitle(ctl, pstr);
  353.             HiliteTabAndReturn(frHndl);
  354.             break;
  355.  
  356.         case kTabButton:
  357.         case kReturnButton:
  358.             ContentCommon(window, event, kUpdateClock, action, oldc, newc);
  359.                 /* This is just a cute way to call some common code. */
  360.             SwitchClocks(frHndl, event, cnum);
  361.             HiliteTabAndReturn(frHndl);
  362.             break;
  363.  
  364.         case kUpdateClock:        /* No such control.  It's a common utility message. */
  365.             timer = (*frHndl)->d.doc.timer;
  366.             (*frHndl)->d.doc.timer = event->when;
  367.             if (timer) {
  368.                 rs = mDerefRoot(root)->rightStart;
  369.                 nm = mDerefRoot(root)->numMoves[0] + mDerefRoot(root)->numMoves[1];
  370.                 if (rs > -1) {
  371.                     i  = nm + rs;
  372.                     i &= 0x01;
  373.                     diff = event->when - timer;
  374.                     if (diff < 0) diff = 0;
  375.                     t1 = mDerefRoot(root)->timeRemaining[i];
  376.                     t2 = t1 - diff;
  377.                     if (t2 < 0) t2 = 0;
  378.                     mDerefRoot(root)->timeRemaining[i] = t2;
  379.                     if ((t1 / 60) != (t2 / 60)) DrawClock(frHndl, i);
  380.                 }
  381.             }
  382.             break;
  383.     }
  384. }
  385.  
  386. static void    HiliteTabAndReturn(FileRecHndl frHndl)
  387. {
  388.     WindowPtr        window;
  389.     TreeObjHndl        root;
  390.     short            t, r, rs, nm, lcRunning, rcRunning, pauseHilite, btn;
  391.     ControlHandle    ctl;
  392.     long            t1, t2;
  393.  
  394.     window = (*frHndl)->fileState.window;
  395.     root   = (*frHndl)->d.doc.root;
  396.  
  397.     pauseHilite = 0;
  398.     lcRunning = rcRunning = 0;
  399.     if (ClocksPaused(window)) {
  400.         CNum2Ctl(window, kNoClockButtonClick, &ctl);
  401.         (*ctl)->contrlVis = 255;
  402.             /* This masks out the buttons on the top of the clock.  When this control
  403.             ** is visible, clicks wil go to it, instead of the buttons, which are
  404.             ** behind this control.  This control does nothing with the click.
  405.             ** Therefore the clock buttons are effectively disabled. */    
  406.         t = r = 255;        /* tab and return to be disabled. */
  407.     }
  408.     else {
  409.         CNum2Ctl(window, kNoClockButtonClick, &ctl);
  410.         (*ctl)->contrlVis = 0;        /* Enable clicks on clock buttons. */
  411.         t = r = 0;                    /* Assume tab and return as enabled. */
  412.         rs = mDerefRoot(root)->rightStart;
  413.         if (rs > -1) {
  414.             nm = mDerefRoot(root)->numMoves[0] + mDerefRoot(root)->numMoves[1];
  415.             if (!((nm + rs) & 0x01)) {
  416.                 r         = 255;        /* return disabled.    */
  417.                 lcRunning = 1;            /* left clock running. */
  418.             }
  419.             else {
  420.                 rcRunning = 1;            /* tab disabled.         */
  421.                 t         = 255;        /* right clock disabled. */
  422.             }
  423.         }
  424.         else pauseHilite = 255;            /* pause button grayed out. */
  425.  
  426.         if (!(t1 = mDerefRoot(root)->timeRemaining[0])) t = 255;    /* If no time on left, gray out tab. */
  427.         if (!(t2 = mDerefRoot(root)->timeRemaining[1])) r = 255;    /* If no time on right, gray out return. */
  428.         if (!(t1 | t2)) pauseHilite = 255;        /* If nobody has moved yet, gray out pause clock button. */
  429.     }
  430.  
  431.     CNum2Ctl(window, kTabButton, &ctl);
  432.     if ((*ctl)->contrlHilite != t) {        /* If control changed... */
  433.         UseControlStyle(ctl);
  434.         HiliteControl(ctl, t);
  435.         UseControlStyle(nil);
  436.     }
  437.     if (!t) {                                /* Do context-sensitive balloon help for tab. */
  438.         if (!r)
  439.             (*ctl)->contrlValue = 0;
  440.         else
  441.             (*ctl)->contrlValue = 2;
  442.     }
  443.     else {
  444.         if (!r)
  445.             (*ctl)->contrlValue = 3;
  446.         else
  447.             (*ctl)->contrlValue = 4;
  448.     }
  449.  
  450.     CNum2Ctl(window, kLeftClock, &ctl);
  451.     (*ctl)->contrlValue = lcRunning;        /* Context sensistve balloon help value for left clock face. */
  452.  
  453.     CNum2Ctl(window, kReturnButton, &ctl);
  454.     if ((*ctl)->contrlHilite != r) {        /* If control changed... */
  455.         UseControlStyle(ctl);
  456.         HiliteControl(ctl, r);
  457.         UseControlStyle(nil);
  458.     }
  459.     if (!r) {                                /* Do context-sensitive balloon help for return. */
  460.         if (!t)
  461.             (*ctl)->contrlValue = 0;
  462.         else
  463.             (*ctl)->contrlValue = 2;
  464.     }
  465.     else {
  466.         if (!t)
  467.             (*ctl)->contrlValue = 3;
  468.         else
  469.             (*ctl)->contrlValue = 4;
  470.     }
  471.  
  472.     CNum2Ctl(window, kRightClock, &ctl);
  473.     (*ctl)->contrlValue = rcRunning;        /* Context sensistve balloon help value for right clock face. */
  474.  
  475.     CNum2Ctl(window, kPauseClocks, &ctl);
  476.     if ((*ctl)->contrlHilite != pauseHilite) {        /* If control changed... */
  477.         (*ctl)->contrlHilite = pauseHilite;
  478.         DoDraw1Control(ctl, false);
  479.     }
  480.  
  481.     btn = 2;
  482.     if ((lcRunning) && (!rcRunning)) btn = 0;
  483.     if ((!lcRunning) && (rcRunning)) btn = 1;
  484.     CNum2Ctl(window, kLeftClock + 2, &ctl);            /* Left clock button. */
  485.     if ((*ctl)->contrlValue != btn) {
  486.         (*ctl)->contrlValue  = btn;
  487.         DoDraw1Control(ctl, false);
  488.     }
  489.  
  490.     if (btn < 2) btn ^= 0x01;
  491.     CNum2Ctl(window, kRightClock + 2, &ctl);        /* Right clock button. */
  492.     if ((*ctl)->contrlValue != btn) {
  493.         (*ctl)->contrlValue  = btn;
  494.         DoDraw1Control(ctl, false);
  495.     }
  496. }
  497.  
  498. short    ClocksPaused(WindowPtr window)
  499. {
  500.     ControlHandle    ctl;
  501.     Str63            p0, p1;
  502.  
  503.     CNum2Ctl(window, kPauseClocks, &ctl);
  504.     pcpy(p0, (*ctl)->contrlTitle);
  505.     CNum2Ctl(window, kStopClocksText, &ctl);
  506.     pcpy(p1, (*ctl)->contrlTitle);
  507.     return(pcmp(p0, p1));        /* If button DOESN'T compare to stopText, then they are stopped. */
  508. }
  509.  
  510. static void    FetchClockValues(FileRecHndl frHndl, short field)
  511. {
  512.     WindowPtr        window;
  513.     TreeObjHndl        root;
  514.     ControlHandle    ctl, ch, cm;
  515.     short            lr1, lr2, i, cn1, cn2;
  516.     Str15            pstr, ph, pm;
  517.     long            h, m, tc1, tc2, tc3;
  518.  
  519.     /* I didn't spend much time typing the below code, or any other part of this sample for
  520.     ** that matter.  I realize that this could probably be done much better/smaller, but
  521.     ** I was coding against a clock, so cut me some slack. */
  522.  
  523.     window = (*frHndl)->fileState.window;
  524.     root   = (*frHndl)->d.doc.root;
  525.  
  526.     lr1 = MyGetRadioButtonChoice(window, 0);
  527.     if (lr1 < 2) lr1 = lr2 = (3 * lr1);
  528.     else {
  529.         lr1 = 0;
  530.         lr2 = 3;
  531.     }        /* Get the left/right clock offsets.  This is based on which control is selected
  532.             ** in the family of radio buttons. (We only have one family -- 0.) */
  533.  
  534.     for (i = 0; i < 3; ++i) {
  535.         cn1 = kTimeControl1Hours +   i * (kTimeControl2Hours   - kTimeControl1Hours);
  536.         cn2 = kTimeControl1Minutes + i * (kTimeControl2Minutes - kTimeControl1Minutes);
  537.         if ((!field) || (field == cn1) || (field == cn2)) {
  538.             CNum2Ctl(window, cn1, &ch);
  539.             CNum2Ctl(window, cn2, &cm);
  540.                 /* Use the delta as an offset.  This is correct, based on the ctlID's I chose. */
  541.                 /* The controls gotten are the hours/minutes control for timeControl i, for whichever
  542.                 ** clock is currently chosen. */
  543.             CTEGetPStr(ch, ph);
  544.             h = p2dec(ph, nil);
  545.             CTEGetPStr(cm,  pm);
  546.             m = p2dec(pm, nil);
  547.             if (m > 59) {
  548.                 ++h;
  549.                 if (h > 99) h = 0;
  550.                 m -= 60;
  551.                 ph[0] = pm[0] = 0;
  552.             }
  553.             if (ph[0] < 2) {
  554.                 pcpypaddec(ph, '0', 2, 2, h);
  555.                 CTEPutPStr(ch, ph);
  556.                 CTESetSelect(0, 2, (TEHandle)GetControlReference(ch));
  557.             }
  558.             if (pm[0] < 2) {
  559.                 pcpypaddec(pm, '0', 2, 2, m);
  560.                 CTEPutPStr(cm, pm);
  561.                 CTESetSelect(0, 2, (TEHandle)GetControlReference(cm));
  562.             }
  563.             mDerefRoot(root)->timeControl[lr1 + i] = (60 * h + m) * 60 * 60;
  564.             mDerefRoot(root)->timeControl[lr2 + i] = (60 * h + m) * 60 * 60;
  565.                 /* Munge the hours and minutes values into a single long tick value. */
  566.         }
  567.     }
  568.  
  569.     if ((!field) || ((field >= kTC1NumMoves) && (field <= kTC3NumMoves))) {
  570.  
  571.         CNum2Ctl(window, kTC1NumMoves, &ctl);
  572.         CTEGetPStr(ctl, pstr);
  573.         tc1 = p2dec(pstr, nil);                        /* TimeControl 1 value. */
  574.  
  575.         CNum2Ctl(window, kTC2NumMoves, &ctl);
  576.         CTEGetPStr(ctl, pstr);
  577.         tc2 = p2dec(pstr, nil);                        /* TimeControl 2 value. */
  578.  
  579.         CNum2Ctl(window, kTC3NumMoves, &ctl);
  580.         CTEGetPStr(ctl, pstr);
  581.         tc3 = p2dec(pstr, nil);                        /* TimeControl 3 value. */
  582.  
  583.         if (!tc1) {
  584.             tc2 = 0;                                /* If no timeControl 1, then no timeControl 2. */
  585.             CNum2Ctl(window, kTC1NumMoves, &ctl);
  586.             UseControlStyle(ctl);
  587.             CTEPutPStr(ctl, "\p");                    /* Change a potential 0 into a nullField. */
  588.             UseControlStyle(nil);
  589.         }
  590.         if (!tc2) {                                    /* Same logic as above. */
  591.             tc3 = 0;
  592.             CNum2Ctl(window, kTC2NumMoves, &ctl);
  593.             UseControlStyle(ctl);
  594.             CTEPutPStr(ctl, "\p");
  595.             UseControlStyle(nil);
  596.         }
  597.         if (!tc3) {                                    /* Same logic as above. */
  598.             CNum2Ctl(window, kTC3NumMoves, &ctl);
  599.             UseControlStyle(ctl);
  600.             CTEPutPStr(ctl, "\p");
  601.             UseControlStyle(nil);
  602.         }
  603.  
  604.         mDerefRoot(root)->numMovesInTimeControl[0] = tc1;    /* Save 'em. */
  605.         mDerefRoot(root)->numMovesInTimeControl[1] = tc2;
  606.         mDerefRoot(root)->numMovesInTimeControl[2] = tc3;
  607.     }
  608. }
  609.  
  610. static Boolean    TrackArrowProc(ControlHandle ctl, short part, EventRecord *event)
  611. {
  612. #ifndef __MWERKS__
  613. #pragma unused (part, event)
  614. #endif
  615.  
  616.     WindowPtr        window, oldPort;
  617.     short            cnum, i, hrAdjust;
  618.     ControlHandle    cc, ccc;
  619.     Str15            pstr, ppp;
  620.     Point            pt;
  621.     Rect            crct;
  622.     long            dt, tc;
  623.  
  624.     /* This be one of the workhorses of this sample.  The arrow controls have a TrackProc
  625.     ** set for them in InitContent.  When the user clicks on an arrow, we end up here.
  626.     ** It is this code's responsibility to track the control, and to update the hour/minute
  627.     ** fields accordingly. */
  628.  
  629.     dt = TickCount() + LMGetDoubleTime();
  630.         /* We don't want to start zooming right away.  This is our delay before repeat. */
  631.  
  632.     window = (*ctl)->contrlOwner;        /* We have to find out who we are talking about. */
  633.     cnum = Ctl2CNum(ctl);
  634.     crct = (*ctl)->contrlRect;            /* That's enough info on where we are. */
  635.  
  636.     GetPort(&oldPort);
  637.     SetPort(window);
  638.  
  639.     CNum2Ctl(window, (cnum & 0xFFFC), &cc);
  640.         /* Note that I carefully selected the ctlID's so that I could do this kind of stuff.
  641.         ** The arrow's ctlID relates it to the TEControl that it adjusts. cc is that field. */
  642.     UseControlStyle(cc);
  643.     CTEGetPStr(cc, pstr);        /* We now have the currelt text from the related TEControl. */
  644.  
  645.     tc = 0x7FFFFFFFL;            /* Use a ridiculous initial value. */
  646.     do {
  647.         if (tc > dt) {            /* First time through, duh... */
  648.             GetMouse(&pt);
  649.             if (PtInRect(pt, &crct)) {        /* Is user actually still on the arrow? */
  650.                 HiliteControl(ctl, 1);
  651.                 hrAdjust = 0;                /* Assume that we won't adjust the hour field. */
  652.                 i = p2dec(pstr, nil);
  653.                 (cnum & 0x01) ? ++i : --i;    /* Up arrow or down arrow hit -- the bits tell us. */
  654.                 if (cnum & 0x04) {            /* If minute field... */
  655.                     if (i < 0) {
  656.                         i = 59;
  657.                         hrAdjust = -1;        /* Roll-over, so we will adjust the hour field, as well. */
  658.                     }
  659.                     if (i > 59) {
  660.                         i = 0;
  661.                         hrAdjust = 1;        /* Roll-over, so we will adjust the hour field, as well. */
  662.                     }
  663.                 }
  664.                 else {
  665.                     if (i < 0)  i = 99;
  666.                     if (i > 99) i = 0;
  667.                 }
  668.                 pcpypaddec(pstr, '0', 2, 2, i);
  669.                 CTEPutPStr(cc, pstr);
  670.                 CTESetSelect(0, 2, (TEHandle)GetControlReference(cc));
  671.                 if (hrAdjust) {                /* If there was a minute roll-over... */
  672.                     i = (cnum ^ 0x04);
  673.                     CNum2Ctl(window, (i & 0xFFFC), &ccc);
  674.                     CTEGetPStr(ccc, ppp);
  675.                     i = p2dec(ppp, nil) + hrAdjust;
  676.                     if (i < 0)  i = 99;
  677.                     if (i > 99) i = 0;
  678.                     pcpypaddec(ppp, '0', 2, 2, i);
  679.                     CTEPutPStr(ccc, ppp);
  680.                     CTESetSelect(0, 2, (TEHandle)GetControlReference(ccc));
  681.                 }
  682.                 SetWindowDirty(window);        /* There was a change, so flag it. */
  683.             }
  684.             else HiliteControl(ctl, 0);
  685.             dt += 6;                        /* Repeat rate is 1/10 of a second. */
  686.         }
  687.         tc = TickCount();
  688.     } while (StillDown());
  689.  
  690.     HiliteControl(ctl, 0);
  691.  
  692.     UseControlStyle(nil);
  693.     SetPort(oldPort);
  694.     return(true);
  695. }
  696.  
  697. static Boolean    TrackRadioProc(ControlHandle ctl, short part, EventRecord *event)
  698. {
  699. #ifndef __MWERKS__
  700. #pragma unused (part)
  701. #endif
  702.  
  703.     WindowPtr                window;
  704.     FileRecHndl                frHndl;
  705.     TreeObjHndl                root;
  706.     static ControlActionUPP    cupp;
  707.  
  708.     window = (*ctl)->contrlOwner;        /* We have to find out who we are talking about. */
  709.     frHndl = (FileRecHndl)GetWRefCon(window);
  710.     root   = (*frHndl)->d.doc.root;
  711.  
  712.     FetchClockValues(frHndl, 0);        /* We're here BEFORE the radio buttons switch. */
  713.     mDerefRoot(root)->timeRemaining[0] = mDerefRoot(root)->timeControl[0];
  714.     mDerefRoot(root)->timeRemaining[1] = mDerefRoot(root)->timeControl[3];
  715.         /* Move clock values where DrawClock can display them. */
  716.  
  717.     if (event->what != mouseDown) return(true);        /* Keypresses "tracked". */
  718.  
  719.     if (!cupp) cupp = (ControlActionUPP)(-1);
  720.     return(TrackControl(ctl, event->where, cupp));
  721. }
  722.  
  723. Boolean    DigitsOnly(TEHandle te, EventRecord *event, short *handled)
  724. {
  725. #ifndef __MWERKS__
  726. #pragma unused (te, handled)
  727. #endif
  728.  
  729.     char    key;
  730.     short    i;
  731.     Boolean    validChar;
  732.  
  733.     key = event->message   & charCodeMask;                /* Cut & paste from some other app I had. */
  734.     i   = event->modifiers & keyCodeMask;
  735.     if (i & cmdKey)                    return(false);
  736.     if (i & optionKey)                return(false);
  737.     if (key == 8)                    return(false);
  738.     if (key == 9)                    return(false);
  739.     if ((key >= 28) && (key <= 31)) return(false);
  740.  
  741.     validChar = false;
  742.     if ((key >= '0') && (key <= '9')) validChar = true;
  743.  
  744.     if (validChar) return(false);
  745.  
  746.     return(true);
  747. }
  748.  
  749.  
  750.  
  751. /*****************************************************************************/
  752.  
  753.  
  754.  
  755. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  756.  
  757. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  758. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  759. **
  760. ** 1) If you handle the key, return(true).  This completes the key handling.
  761. ** 2) If you don't handle the key, you return false.  However, there are two
  762. **    situations for not handling the key:
  763. **      a) You want someone else to.
  764. **      b) You want nobody else to look at the key.
  765. **    This is what the boolean passThrough is for.  If you wish the next window
  766. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  767. **    is already initialized to false, which is the common case, so you only have
  768. **    to worry about setting it true.
  769. **
  770. ** If you have a window that never processes keys and always passes them through,
  771. ** just set the contentKeyProc to nil.  This will indicate to the application
  772. ** framework that all keys should be passed through this window.  DTS.Draw has
  773. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  774. ** to document windows. */
  775.  
  776. #pragma segment TheDoc
  777. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  778. {
  779. #ifndef __MWERKS__
  780. #pragma unused (passThrough)
  781. #endif
  782.  
  783.     WindowPtr        oldPort;
  784.     FileRecHndl        frHndl;
  785.     TreeObjHndl        root;
  786.     short            cnum, action, i;
  787.     long            lcc, rcc;
  788.     ControlHandle    ctl;
  789.     TEHandle        te1, te2;
  790.     RGBColor        oldc, newc;
  791.  
  792.     GetPort(&oldPort);
  793.     SetPort(window);
  794.     if (gQDVersion) {
  795.         GetBackColor(&oldc);
  796.         newc.red = newc.green = newc.blue = 0xFFFF;
  797.         RGBBackColor(&newc);
  798.     }
  799.  
  800.     frHndl = (FileRecHndl)GetWRefCon(window);
  801.     root   = (*frHndl)->d.doc.root;
  802.  
  803.     /* This is a bit different than the usual AppsToGo ContentKey function.  Since this
  804.     ** app is actually modal (clocks running or not), we need to determine if it is
  805.     ** correct to call IsCtlEvent or not.  If the clocks are running, then the tab
  806.     ** key is used as a clock button.  If we call IsCtlEvent, it will be used to
  807.     ** advance to the next TEControl.  Therefore we need to check if the clocks are
  808.     ** running.  We do this by checking the reset control.  If it is inactive, then
  809.     ** the user clicked on it to start the clock mode. */
  810.  
  811.     te1  = CTEFindActive(window);
  812.     cnum = IsCtlEvent(window, event, nil, &action);
  813.  
  814.     if (cnum == kCRAdvance) {        /* Use the c/r key to advance to next field vertically. */
  815.         ctl = CTEViewFromTE(te1);
  816.         i   = Ctl2CNum(ctl) + 40;
  817.         if (i == 320) i = 204;
  818.         if (i == 324) i = 300;
  819.         if (i == 420) i = 200;
  820.         CNum2Ctl(window, i, &ctl);
  821.         CTEActivate(true, (TEHandle)GetControlReference(ctl));
  822.         CTESetSelect(0, 3, (TEHandle)GetControlReference(ctl));
  823.     }
  824.  
  825.     te2 = CTEFindActive(window);
  826.  
  827.     if (te1 != te2) {        /* Keypress changed field, so close the old one. */
  828.         ctl = CTEViewFromTE(te1);
  829.         FetchClockValues(frHndl, Ctl2CNum(ctl));        /* Specifically fetch the old field. */
  830.         lcc = mDerefRoot(root)->timeRemaining[0] - mDerefRoot(root)->timeControl[0];
  831.         rcc = mDerefRoot(root)->timeRemaining[1] - mDerefRoot(root)->timeControl[3];
  832.         mDerefRoot(root)->timeRemaining[0] -= lcc;
  833.         mDerefRoot(root)->timeRemaining[1] -= rcc;
  834.         if (lcc) DrawClock(frHndl, 0);        /* Draw the left clock if it needs updating. */
  835.         if (rcc) DrawClock(frHndl, 1);        /* Draw the right clock if it needs updating. */
  836.     }
  837.  
  838.     if (action == 2) SetWindowDirty(window);
  839.         /* If a TEControl was changed, dirty the document. */
  840.  
  841.     switch (cnum) {
  842.         case kLeftClockRadio:
  843.         case kRightClockRadio:
  844.         case kBothClocksRadio:
  845.         case kSetClocks:
  846.         case kResetClocks:
  847.         case kStartClocks:
  848.         case kPauseClocks:
  849.         case kTabButton:
  850.         case kReturnButton:
  851.             ContentCommon(window, event, cnum, action, &oldc, &newc);
  852.             break;
  853.     }
  854.  
  855.     if (gQDVersion)
  856.         RGBBackColor(&oldc);
  857.  
  858.     SetPort(oldPort);
  859.     return(true);
  860. }
  861.  
  862. static void    SwitchClocks(FileRecHndl frHndl, EventRecord *event, short cnum)
  863. {
  864.     TreeObjHndl        root;
  865.     short            i, rs, nm, nm1, nm2, nm3;
  866.     long            tc;
  867.  
  868.     root = (*frHndl)->d.doc.root;
  869.  
  870.     rs = mDerefRoot(root)->rightStart;
  871.     nm = mDerefRoot(root)->numMoves[0] + mDerefRoot(root)->numMoves[1];
  872.     if (rs < 0) {                                /* If clocks haven't been started yet. */
  873.         (*frHndl)->d.doc.timer = event->when;
  874.         if (cnum == kTabButton)
  875.             mDerefRoot(root)->rightStart = 1;    /* Start the right clock. */
  876.         if (cnum == kReturnButton)
  877.             mDerefRoot(root)->rightStart = 0;    /* Start the left clock. */
  878.         DrawClocks(frHndl);
  879.         return;
  880.     }
  881.  
  882.     nm1 = mDerefRoot(root)->numMovesInTimeControl[0];    /* Get the timeControl landmarks. */
  883.     nm2 = mDerefRoot(root)->numMovesInTimeControl[1];
  884.     nm3 = mDerefRoot(root)->numMovesInTimeControl[2];
  885.     i   = nm + rs;                        /* Determine which clock is running. */
  886.  
  887.     if (cnum == kTabButton) {            /* If tab was pressed... */
  888.  
  889.         if (!(i & 0x01)) {                /* If tab should have been pressed... */
  890.  
  891.             for (nm = ++mDerefRoot(root)->numMoves[0];;) {        /* for is for break control purposes. */
  892.  
  893.                 if (!nm1) break;
  894.                 tc = mDerefRoot(root)->timeControl[1];
  895.                 if (!tc) break;
  896.                 if (nm == nm1) {
  897.                     mDerefRoot(root)->timeRemaining[0] += tc;
  898.                     break;
  899.                 }
  900.  
  901.                 if (!nm2) break;
  902.                 tc = mDerefRoot(root)->timeControl[2];
  903.                 if (!tc) break;
  904.                 nm1 += nm2;
  905.                 if (nm == nm1) {
  906.                     mDerefRoot(root)->timeRemaining[0] += tc;
  907.                     break;
  908.                 }
  909.  
  910.                 if (!nm3) break;
  911.                 for (;nm1 += nm3 ;) {
  912.                     if (nm < nm1) break;
  913.                     if (nm == nm1) {
  914.                         mDerefRoot(root)->timeRemaining[0] += tc;
  915.                         break;
  916.                     }
  917.                 }
  918.  
  919.                 break;
  920.             }
  921.         }
  922.  
  923.         DrawClock(frHndl, 0);
  924.         DrawClock(frHndl, 1);
  925.     }
  926.  
  927.     if (cnum == kReturnButton) {        /* If return was pressed... */
  928.  
  929.         if (i & 0x01) {                    /* If return should have been pressed... */
  930.  
  931.             for (nm = ++mDerefRoot(root)->numMoves[1];;) {        /* for is for break control purposes. */
  932.  
  933.                 if (!nm1) break;
  934.                 tc = mDerefRoot(root)->timeControl[1 + 3];
  935.                 if (!tc) break;
  936.                 if (nm == nm1) {
  937.                     mDerefRoot(root)->timeRemaining[1] += tc;
  938.                     break;
  939.                 }
  940.  
  941.                 if (!nm2) break;
  942.                 tc = mDerefRoot(root)->timeControl[2 + 3];
  943.                 if (!tc) break;
  944.                 nm1 += nm2;
  945.                 if (nm == nm1) {
  946.                     mDerefRoot(root)->timeRemaining[1] += tc;
  947.                     break;
  948.                 }
  949.  
  950.                 if (!nm3) break;
  951.                 for (;nm1 += nm3 ;) {
  952.                     if (nm < nm1) break;
  953.                     if (nm == nm1) {
  954.                         mDerefRoot(root)->timeRemaining[1] += tc;
  955.                         break;
  956.                     }
  957.                 }
  958.  
  959.                 break;
  960.             }
  961.         }
  962.  
  963.         DrawClock(frHndl, 1);
  964.         DrawClock(frHndl, 0);
  965.     }
  966. }
  967.  
  968.  
  969.  
  970. /*****************************************************************************/
  971.  
  972.  
  973.  
  974. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  975.  
  976. /* Draw application specific content (Called by DoDrawFrame).
  977. **
  978. ** If your application has any custom frame areas, or if it uses sidebars,
  979. ** this is the function that you would put the frame drawing code.  The
  980. ** document scrollbars and grow icon drawing is handled by DTS.framework.
  981. ** Just do the sidebar and custom areas here. */
  982.  
  983. #pragma segment TheDoc
  984. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  985. {
  986.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  987.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  988.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  989.  
  990.     BeginFrame(window);
  991.     DoDrawControls(window, activate);
  992.     EndFrame(window);
  993. }
  994.  
  995.  
  996.  
  997. /*****************************************************************************/
  998.  
  999.  
  1000.  
  1001. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1002.  
  1003. /* Frees up any application-specific memory in the document.  This is called by
  1004. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  1005. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  1006. **
  1007. ** The document may have a bunch of handles off the main handle of the document.
  1008. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  1009. ** the ram for the main handle of the document, so release everything else
  1010. ** here, or you will have a memory leak.
  1011. **
  1012. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  1013. ** hierarchical document (see TreeObj package). */
  1014.  
  1015. #pragma segment TheDoc
  1016. OSErr    FreeDocument(FileRecHndl frHndl)
  1017. {
  1018.     return(DefaultFreeDocument(frHndl));
  1019. }
  1020.  
  1021.  
  1022.  
  1023. /*****************************************************************************/
  1024.  
  1025.  
  1026.  
  1027. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1028.  
  1029. /* Any additional window disposal tasks can be handled here. */
  1030.  
  1031. #pragma segment TheDoc
  1032. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  1033. {
  1034. #ifndef __MWERKS__
  1035. #pragma unused (window)
  1036. #endif
  1037.  
  1038.     WindowPtr    ww;
  1039.     FileRecHndl    ff;
  1040.  
  1041.     if ((*frHndl)->fileState.sfType == kDocFileType) {
  1042.         for (ww = nil; (ww = GetNextWindow(ww, 0)) != nil;) {
  1043.             ff = (FileRecHndl)GetWRefCon(ww);
  1044.             if ((*ff)->fileState.sfType == kViewHierFileType) {
  1045.                 if ((*frHndl)->d.doc.root == (*ff)->d.doc.root) {
  1046.                     DisposeOneWindow(ww, kClose);
  1047.                     ww = nil;
  1048.                 }
  1049.             }
  1050.         }
  1051.     }
  1052.  
  1053.     return(noErr);
  1054. }
  1055.  
  1056.  
  1057.  
  1058. /*****************************************************************************/
  1059.  
  1060.  
  1061.  
  1062. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1063.  
  1064. /* Image the document into the current port.
  1065. **
  1066. ** The only thing tricky about this function is that it needs to key off of
  1067. ** the global variable gPrintPage.  gPrintPage is the current page that is
  1068. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  1069. **
  1070. ** For when printing:
  1071. **
  1072. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  1073. ** the page there are no more pages, you should set gPrintPage to 0.  This
  1074. ** indicates to the print loop that the end of the document has been reached.
  1075. ** Even if the user indicated in the job dialog to print more pages, setting
  1076. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  1077. ** because the print loop can't know when printing is done.  The imaging procedure
  1078. ** is the logical one to state when everything has been imaged. */
  1079.  
  1080. #pragma segment TheDoc
  1081. OSErr    ImageDocument(FileRecHndl frHndl)
  1082. {
  1083.     WindowPtr    window;
  1084.     RGBColor    oldc, newc;
  1085.  
  1086.     window = (*frHndl)->fileState.window;
  1087.  
  1088.     if (gQDVersion) {
  1089.         GetBackColor(&oldc);
  1090.         newc.red = newc.green = newc.blue = 0xFFFF;
  1091.         RGBBackColor(&newc);
  1092.     }
  1093.  
  1094.     DrawClocks(frHndl);
  1095.     DoDrawControls(window, false);
  1096.         /* It's either a clock or a control, and we don't support printing, so that's it. */
  1097.  
  1098.     if (gQDVersion)
  1099.         RGBBackColor(&oldc);
  1100.  
  1101.     gPrintPage = 0;
  1102.     return(noErr);
  1103. }
  1104.  
  1105. void    DrawClocks(FileRecHndl frHndl)
  1106. {
  1107.     DrawClock(frHndl, 0);
  1108.     DrawClock(frHndl, 1);
  1109.     HiliteTabAndReturn(frHndl);
  1110. }
  1111.  
  1112. void    DrawClock(FileRecHndl frHndl, short clock)
  1113. {
  1114.     WindowPtr        window;
  1115.     TreeObjHndl        root;
  1116.     ControlHandle    ctl;
  1117.     Rect            rct;
  1118.     RgnHandle        oldClip, newClip;
  1119.     LayerObj        windowLayer;
  1120.     DrawClocksInfo    dcInfo;
  1121.     short            activeClock, rs, lr;
  1122.     RGBColor        oldc, newc;
  1123.  
  1124.     window = (*frHndl)->fileState.window;
  1125.     root   = (*frHndl)->d.doc.root;
  1126.  
  1127.     SetPort(window);
  1128.  
  1129.     CNum2Ctl(window, clock + kLeftClock, &ctl);
  1130.     rct = (*ctl)->contrlRect;
  1131.  
  1132.     NewLayer(&windowLayer, nil, nil, window, 0, 0);
  1133.     (*windowLayer)->dstRect = rct;
  1134.  
  1135.     if (!gWorkLayer) {            /* If we don't have offscreen layers yet, make 'em. */
  1136.         NewLayer(&gWorkLayer, windowLayer, WorkLayerProc, nil, 8, 0);
  1137.         NewLayer(&gBackLayer[0], gWorkLayer, BackLayerProc, nil, 1, 0);
  1138.         NewLayer(&gBackLayer[1], gWorkLayer, BackLayerProc, nil, 1, 1);
  1139.         DetachLayer(gWorkLayer);
  1140.         DetachLayer(gBackLayer[0]);
  1141.         DetachLayer(gBackLayer[1]);
  1142.     }        /* Note that on an AppsToGo editor restart, OpenApplication disposes of the
  1143.             ** global offscreen layers and resets them to nil.  This means that this
  1144.             ** code will be executed when the program starts up initially, or whevever
  1145.             ** the AppsToGo editor issues a restart. */
  1146.  
  1147.     activeClock = 0;
  1148.     rs = mDerefRoot(root)->rightStart;
  1149.     if (rs > -1) {
  1150.         lr  = mDerefRoot(root)->numMoves[0];
  1151.         lr += mDerefRoot(root)->numMoves[1];
  1152.         lr += rs;
  1153.         lr &= 0x01;
  1154.         if (clock == lr) ++activeClock;
  1155.     }
  1156.  
  1157.     InsertLayer(gWorkLayer, gBackLayer[activeClock], 0);
  1158.     InsertLayer(windowLayer, gWorkLayer, 0);
  1159.  
  1160.     InvalLayer(windowLayer, rct, false);
  1161.     dcInfo.drawTime = mDerefRoot(root)->timeRemaining[clock];
  1162.     dcInfo.numMoves = mDerefRoot(root)->numMoves[clock];
  1163.  
  1164.     dcInfo.numTimeControls = 1;
  1165.     if (mDerefRoot(root)->numMovesInTimeControl[1]) {
  1166.         if (mDerefRoot(root)->timeControl[1 + (3 * clock)]) {
  1167.             ++dcInfo.numTimeControls;
  1168.             if (mDerefRoot(root)->numMovesInTimeControl[2]) {
  1169.                 if (mDerefRoot(root)->timeControl[2 + (3 * clock)]) {
  1170.                     ++dcInfo.numTimeControls;
  1171.                 }
  1172.             }
  1173.         }
  1174.     }
  1175.  
  1176.     dcInfo.timeControl  = 1;
  1177.     dcInfo.numRemaining = mDerefRoot(root)->numMovesInTimeControl[0] - dcInfo.numMoves;
  1178.     if (dcInfo.numRemaining < 1) {
  1179.         ++dcInfo.timeControl;
  1180.         dcInfo.numRemaining += mDerefRoot(root)->numMovesInTimeControl[1];
  1181.     }
  1182.     if (dcInfo.numRemaining < 1) {
  1183.         ++dcInfo.timeControl;
  1184.         dcInfo.numRemaining += mDerefRoot(root)->numMovesInTimeControl[2];
  1185.     }
  1186.  
  1187.     (*gWorkLayer)->layerData = (long)&dcInfo;
  1188.         /* Now it knows... */
  1189.  
  1190.     OpenRgn();
  1191.     FrameOval(&rct);
  1192.     CloseRgn(newClip = NewRgn());
  1193.     GetClip(oldClip = NewRgn());
  1194.     SetClip(newClip);
  1195.  
  1196.     if (gQDVersion) {
  1197.         GetBackColor(&oldc);
  1198.         newc.red = newc.green = newc.blue = 0xFFFF;
  1199.         RGBBackColor(&newc);
  1200.     }
  1201.  
  1202.     UpdateLayer(windowLayer);        /* Draw the clock. */
  1203.  
  1204.     if (gQDVersion)
  1205.         RGBBackColor(&oldc);
  1206.  
  1207.     SetClip(oldClip);
  1208.     DisposeRgn(oldClip);
  1209.     DisposeRgn(newClip);
  1210.  
  1211.     DisposeLayer(windowLayer);
  1212.         /* This orphans gWorkLayer and gBackLayer, which means that they will be around
  1213.         ** next time this is called, unless OpenApplication is called. */
  1214. }
  1215.  
  1216. static OSErr    BackLayerProc(LayerObj theLayer, short message)
  1217. {
  1218.     OSErr            err;
  1219.     Rect            rct, rr;
  1220.     Point            cc, pp;
  1221.     short            i, j, dh, dv, hh, vv;
  1222.     RgnHandle        rgn;
  1223.  
  1224.     err = noErr;
  1225.  
  1226.     switch (message) {
  1227.         case kLayerInit:
  1228.             err = DefaultLayerProc(theLayer, message);
  1229.             if (!err) {
  1230.                 SetLayerWorld(theLayer);
  1231.                 rct  = GetEffectiveDstRect(theLayer);
  1232.                 cc.h = (rct.left + rct.right) / 2;
  1233.                 cc.v = (rct.top + rct.bottom) / 2;
  1234.                 rgn  = NewRgn();
  1235.                 for (i = 60; i >= 45; --i) {
  1236.                     EraseRect(&rct);
  1237.                     FrameArc(&rct, -6 * i, 1);
  1238.                     if (gQDVersion)
  1239.                         BitMapToRegion(rgn, (BitMapPtr)(*((CGrafPtr)(*theLayer)->layerPort)->portPixMap));
  1240.                     else
  1241.                         BitMapToRegion(rgn, &(*theLayer)->layerPort->portBits);
  1242.                     j = i % 60;
  1243.                     pp.h = (*rgn)->rgnBBox.left;
  1244.                     pp.v = (*rgn)->rgnBBox.top;
  1245.                     gArcLocs[j].h = pp.h;
  1246.                     gArcLocs[j].v = pp.v;
  1247.                     j = 90 - i;
  1248.                     gArcLocs[j].h = pp.h;
  1249.                     gArcLocs[j].v = rct.bottom - (pp.v - rct.top);
  1250.                     j = 60 - j;
  1251.                     gArcLocs[j].h = rct.left + (rct.right - pp.h);
  1252.                     gArcLocs[j].v = rct.bottom - (pp.v - rct.top);
  1253.                     j = 60 - i;
  1254.                     gArcLocs[j].h = rct.left + (rct.right - pp.h);
  1255.                     gArcLocs[j].v = pp.v;
  1256.                 }        /* The above looks awful, but it is relatively simple.  Since the arc code
  1257.                         ** doesn't use trig, if we used trig to compute where the points were, we
  1258.                         ** may be a bit off.  Therefore we will use the arc rendering code itself
  1259.                         ** to determine where the minute marks are on the oval.  To do this, we
  1260.                         ** image a single degree, and then call BitMapToRegion to get a region that
  1261.                         ** is the single pixel.  The bounding box of this region will serve as the
  1262.                         ** coordinate for the minute-mark.  To save time, and to guarantee symmetry,
  1263.                         ** we do only one quadrant, and reflect the coordinate into the other 4. */
  1264.  
  1265.                 if ((*theLayer)->layerData) PenSize(2, 2);
  1266.                 FrameOval(&rct);
  1267.                 PenSize(1, 1);
  1268.                     /* We haven't done a lone EraseRect for the offscreen yet.  This is okay
  1269.                     ** because we did one prior to the FrameArc in the above loop.  Since the
  1270.                     ** FrameArc code does a portion of an oval, the remaining pixel will be
  1271.                     ** properly covered by this FrameOval. */
  1272.  
  1273.                 for (i = 0; i < 60; ++i) {
  1274.                     dh  = cc.h - gArcLocs[i].h;
  1275.                     dh *= 5;
  1276.                     dh /= (cc.h - rct.left);
  1277.                     dv = cc.v - gArcLocs[i].v;
  1278.                     dv *= 5;
  1279.                     dv /= (cc.v - rct.top);
  1280.                     PenSize(2, 2);
  1281.                     hh = gArcLocs[i].h + dh;
  1282.                     vv = gArcLocs[i].v + dv;
  1283.                     if (i % 5)
  1284.                         SetRect(&rr, hh - 1, vv - 1, hh + 1, vv + 1);
  1285.                     else
  1286.                         SetRect(&rr, hh - 2, vv - 2, hh + 2, vv + 2);
  1287.                     PaintOval(&rr);
  1288.                 }
  1289.  
  1290.                 DisposeRgn(rgn);
  1291.                 ResetLayerWorld(theLayer);
  1292.             }
  1293.             break;
  1294.         default:
  1295.             err = DefaultLayerProc(theLayer, message);
  1296.                 /* Default behavior for everything else. */
  1297.             break;
  1298.     }
  1299.  
  1300.     return(err);
  1301. }
  1302.  
  1303. static OSErr    WorkLayerProc(LayerObj theLayer, short message)
  1304. {
  1305.     WindowPtr        window;
  1306.     ControlHandle    ctl;
  1307.     Str63            pstr;
  1308.     OSErr            err;
  1309.     Rect            rct, r;
  1310.     short            dx, dy;
  1311.     Point            cc, pp, p;
  1312.     long            t, h, m, s;
  1313.     RgnHandle        rgn;
  1314.     DrawClocksInfo    dcInfo;
  1315.  
  1316.     err = noErr;
  1317.  
  1318.     switch (message) {
  1319.  
  1320.         case kLayerInit:
  1321.             err = DefaultLayerProc(theLayer, message);
  1322.             break;
  1323.  
  1324.         case kLayerUpdate:
  1325.             DefaultLayerProc(theLayer, message);
  1326.  
  1327.             SetLayerWorld(theLayer);
  1328.  
  1329.             dcInfo = *(DrawClocksInfo *)((*theLayer)->layerData);
  1330.  
  1331.             rct  = GetEffectiveDstRect(theLayer);
  1332.             cc.h = (rct.left + rct.right) / 2;
  1333.             cc.v = (rct.top + rct.bottom) / 2;
  1334.  
  1335.             t = dcInfo.drawTime;
  1336.             if ((t) && (t < 60)) t = 60;        /* Only show 0 when completely out of time. */
  1337.             t  = dcInfo.drawTime / 60;
  1338.             s  = t % 60;
  1339.             t /= 60;
  1340.             m  = t % 60;
  1341.             t /= 60;
  1342.             h  = t;
  1343.  
  1344.             window = (*(*theLayer)->aboveLayer)->layerPort;
  1345.  
  1346.             CNum2Ctl(window, kDigitalDisplay, &ctl);        /* Get the font/size/style set by          */
  1347.             UseControlStyle(ctl);                            /* the AppsToGo editor.                      */
  1348.             TextFont(window->txFont);                        /* UseCotrolStyle gets it, but it puts    */
  1349.             TextSize(window->txSize);                        /* it in the control's port.  Therefore   */
  1350.             TextFace(window->txFace);                        /* we still need to get it from there and */
  1351.             UseControlStyle(nil);                            /* put it into the offscreen.             */
  1352.  
  1353.             pcpypaddec(pstr, '0', 2, 2, h);                    /* Format hh:mm:ss. */
  1354.             pcatchr(pstr, ':', 1);
  1355.             pcatpaddec(pstr, '0', 2, 2, m);
  1356.             pcatchr(pstr, ':', 1);
  1357.             pcatpaddec(pstr, '0', 2, 2, s);
  1358.             r = (*ctl)->contrlRect;
  1359.             MoveTo(r.left, r.bottom - 2);
  1360.             DrawString(pstr);                                /* Draw hh:mm:ss. */
  1361.  
  1362.             CNum2Ctl(window, kMoveDisplay, &ctl);            /* The rest of the text uses a different font. */
  1363.             UseControlStyle(ctl);
  1364.             TextFont(window->txFont);
  1365.             TextSize(window->txSize);
  1366.             TextFace(window->txFace);
  1367.             UseControlStyle(nil);
  1368.  
  1369.             pcpy(pstr, (*ctl)->contrlTitle);                /* Draw the number of moves text. */
  1370.             pcatdec(pstr, dcInfo.numMoves);
  1371.             r = (*ctl)->contrlRect;
  1372.             MoveTo(r.left, r.bottom - 2);
  1373.             DrawString(pstr);
  1374.  
  1375.             if ((dcInfo.numTimeControls > 1) && (dcInfo.numRemaining > 0)) {
  1376.                 CNum2Ctl(window, kTimeControlDisplay, &ctl);
  1377.                 pcpy(pstr, (*ctl)->contrlTitle);
  1378.                 pcatdec(pstr, dcInfo.timeControl);
  1379.                 r = (*ctl)->contrlRect;
  1380.                 MoveTo(r.left, r.bottom - 2);
  1381.                 DrawString(pstr);
  1382.                 (*ctl)->contrlVis = 255;
  1383.                 CNum2Ctl(window, kTimeControlDisplay + 50, &ctl);
  1384.                 (*ctl)->contrlVis = 255;
  1385.  
  1386.                 CNum2Ctl(window, kMovesToMakeDisplay, &ctl);
  1387.                 pcpy(pstr, (*ctl)->contrlTitle);
  1388.                 pcatdec(pstr, dcInfo.numRemaining);
  1389.                 r = (*ctl)->contrlRect;
  1390.                 MoveTo(r.left, r.bottom - 2);
  1391.                 DrawString(pstr);
  1392.                 (*ctl)->contrlVis = 255;
  1393.                 CNum2Ctl(window, kMovesToMakeDisplay + 50, &ctl);
  1394.                 (*ctl)->contrlVis = 255;
  1395.             }
  1396.             else {
  1397.                 CNum2Ctl(window, kTimeControlDisplay, &ctl);
  1398.                 (*ctl)->contrlVis = 0;
  1399.                 CNum2Ctl(window, kTimeControlDisplay + 50, &ctl);
  1400.                 (*ctl)->contrlVis = 0;
  1401.                 CNum2Ctl(window, kMovesToMakeDisplay, &ctl);
  1402.                 (*ctl)->contrlVis = 0;
  1403.                 CNum2Ctl(window, kMovesToMakeDisplay + 50, &ctl);
  1404.                 (*ctl)->contrlVis = 0;
  1405.             }
  1406.  
  1407.             rgn = NewRgn();
  1408.  
  1409.             h = t % 12;
  1410.  
  1411.             OpenRgn();                                /* Draw second hand. */
  1412.             r = rct;
  1413.             InsetRect(&r, 8, 8);
  1414.             FrameOval(&r);
  1415.             CloseRgn(rgn);
  1416.             SetClip(rgn);
  1417.             MoveTo(gArcLocs[s].h, gArcLocs[s].v);
  1418.             LineTo(cc.h, cc.v);
  1419.  
  1420.             OpenRgn();                                /* Draw minute hand. */
  1421.             r = rct;
  1422.             InsetRect(&r, 16, 16);
  1423.             FrameOval(&r);
  1424.             CloseRgn(rgn);
  1425.             SetClip(rgn);
  1426.             pp = gArcLocs[m];
  1427.             p  = gArcLocs[(m + 1) % 60];
  1428.             dx = (p.h - pp.h) * s / 60;
  1429.             dy = (p.v - pp.v) * s / 60;
  1430.             pp.h += dx;
  1431.             pp.v += dy;
  1432.             MoveTo(pp.h, pp.v);
  1433.             LineTo(cc.h, cc.v);
  1434.             MoveTo(pp.h, pp.v);
  1435.             LineTo(cc.h + 1, cc.v);
  1436.             MoveTo(pp.h, pp.v);
  1437.             LineTo(cc.h - 1, cc.v);
  1438.             MoveTo(pp.h, pp.v);
  1439.             LineTo(cc.h, cc.v + 1);
  1440.             MoveTo(pp.h, pp.v);
  1441.             LineTo(cc.h, cc.v - 1);
  1442.  
  1443.             OpenRgn();                                /* Draw hour hand. */
  1444.             r = rct;
  1445.             InsetRect(&r, 50, 50);
  1446.             FrameOval(&r);
  1447.             CloseRgn(rgn);
  1448.             SetClip(rgn);
  1449.  
  1450.             SetRect(&r, cc.h - 2, cc.v - 2, cc.h + 3, cc.v + 3);
  1451.             PaintOval(&r);
  1452.  
  1453.             h *= 5;
  1454.             h += m / 12;
  1455.             pp = gArcLocs[h];
  1456.             p  = gArcLocs[(h + 1) % 60];
  1457.             dx = (p.h - pp.h) * m / 60;
  1458.             dy = (p.v - pp.v) * m / 60;
  1459.             pp.h += dx;
  1460.             pp.v += dy;
  1461.             MoveTo(pp.h, pp.v);
  1462.             LineTo(cc.h, cc.v);
  1463.             MoveTo(pp.h, pp.v);
  1464.             LineTo(cc.h + 1, cc.v);
  1465.             MoveTo(pp.h, pp.v);
  1466.             LineTo(cc.h - 1, cc.v);
  1467.             MoveTo(pp.h, pp.v);
  1468.             LineTo(cc.h, cc.v + 1);
  1469.             MoveTo(pp.h, pp.v);
  1470.             LineTo(cc.h, cc.v - 1);
  1471.             MoveTo(pp.h, pp.v);
  1472.             LineTo(cc.h + 2, cc.v);
  1473.             MoveTo(pp.h, pp.v);
  1474.             LineTo(cc.h - 2, cc.v);
  1475.             MoveTo(pp.h, pp.v);
  1476.             LineTo(cc.h, cc.v + 2);
  1477.             MoveTo(pp.h, pp.v);
  1478.             LineTo(cc.h, cc.v - 2);
  1479.  
  1480.             ClipRect(&rct);
  1481.             DisposeRgn(rgn);
  1482.  
  1483.             ResetLayerWorld(theLayer);
  1484.             break;
  1485.  
  1486.         default:
  1487.             err = DefaultLayerProc(theLayer, message);
  1488.                 /* Default behavior for everything else. */
  1489.             break;
  1490.     }
  1491.  
  1492.     return(err);
  1493. }
  1494.  
  1495.  
  1496.  
  1497. /*****************************************************************************/
  1498.  
  1499.  
  1500.  
  1501. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1502.  
  1503. /* This function does the remaining window initialization.
  1504. **
  1505. ** There may be additional content initialization for the window.  At this point,
  1506. ** you have a window, but it is currently invisible.  If you return noErr, then
  1507. ** the window will be set to the state indicated for that window.  Why this function?
  1508. ** You may wish to add controls to the content of the window.  You may have a
  1509. ** TextEdit record in the content.  All of these sort of things can't be created
  1510. ** until there is a window to contain them.  First a document is read in, and then
  1511. ** if the document creation succeeds, a window is created for that document.
  1512. ** At this point we have a document, and we are on our way to having a window.
  1513. ** All that remains is any additional content initialization.  Do it, return
  1514. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  1515. ** and the incomplete window will be disposed of. */
  1516.  
  1517. #pragma segment TheDoc
  1518. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  1519. {
  1520.     OSErr            err;
  1521.     ControlHandle    ctl;
  1522.     short            i, lr;
  1523.     Str15            pstr;
  1524.     long            t, h, m;
  1525.     TreeObjHndl        root;
  1526.  
  1527.     InitButtonsCtl(4960);
  1528.     gGetButtonVariant = MyGetButtonVariant;
  1529.  
  1530.     CNum2Ctl(window, kLeftClock, &ctl);
  1531.         /* Check to see if the controls have already been added.  This allows us to
  1532.         ** call this after the window has been created. */
  1533.  
  1534.     if (!ctl) {        /* If first time here... */
  1535.         err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  1536.         if (err) return(err);
  1537.         HiliteTabAndReturn(frHndl);
  1538.     }
  1539.  
  1540.     root = (*frHndl)->d.doc.root;
  1541.  
  1542.     lr = MyGetRadioButtonChoice(window, 0);
  1543.     if (lr == 2) lr = 0;
  1544.     lr *= 3;
  1545.  
  1546.     for (i = 0; i < 3; ++i) {
  1547.  
  1548.         CNum2Ctl(window, kTC1HUpArrow + i * (kTC2HUpArrow - kTC1HUpArrow), &ctl);
  1549.         SetTrackControlProc(ctl, TrackArrowProc);        /* Set the hour up arrow controls to track. */
  1550.  
  1551.         CNum2Ctl(window, kTC1HDnArrow + i * (kTC2HDnArrow - kTC1HDnArrow), &ctl);
  1552.         SetTrackControlProc(ctl, TrackArrowProc);        /* Set the hour down arrow controls to track. */
  1553.  
  1554.         t  = mDerefRoot(root)->timeControl[lr + i];
  1555.         h  = t / (60L * 60 * 60);
  1556.         m  = t - (60L * 60 * 60) * h;
  1557.         m /= (60 * 60);
  1558.  
  1559.         CNum2Ctl(window, kTimeControl1Hours + i * (kTimeControl2Hours - kTimeControl1Hours), &ctl);
  1560.         CTESetKeyFilter((TEHandle)GetControlReference(ctl), DigitsOnly);
  1561.             /* Filter out non-digits for the hours fields. */
  1562.  
  1563.         UseControlStyle(ctl);
  1564.         pcpypaddec(pstr, '0', 2, 2, h);
  1565.         CTEPutPStr(ctl, pstr);
  1566.         CTESetSelect(0, 2, (TEHandle)GetControlReference(ctl));
  1567.             /* Fill in the field with the hours value. */
  1568.  
  1569.         CNum2Ctl(window, kTC1MUpArrow + i * (kTC2MUpArrow - kTC1MUpArrow), &ctl);
  1570.         SetTrackControlProc(ctl, TrackArrowProc);        /* Set the minute down arrow controls to track. */
  1571.  
  1572.         CNum2Ctl(window, kTC1MDnArrow + i * (kTC2MDnArrow - kTC1MDnArrow), &ctl);
  1573.         SetTrackControlProc(ctl, TrackArrowProc);        /* Set the minute down arrow controls to track. */
  1574.  
  1575.         CNum2Ctl(window, kTimeControl1Minutes + i * (kTimeControl2Minutes - kTimeControl1Minutes), &ctl);
  1576.         CTESetKeyFilter((TEHandle)GetControlReference(ctl), DigitsOnly);
  1577.             /* Filter out non-digits for the minutes fields. */
  1578.  
  1579.         pcpypaddec(pstr, '0', 2, 2, m);
  1580.         CTEPutPStr(ctl, pstr);
  1581.         CTESetSelect(0, 2, (TEHandle)GetControlReference(ctl));
  1582.             /* Fill in the field with the minutes value. */
  1583.  
  1584.         CNum2Ctl(window, kLeftClockRadio + i, &ctl);
  1585.         SetTrackControlProc(ctl, TrackRadioProc);
  1586.             /* Track the radio buttons (for pre-switch fetch of values). */
  1587.     }
  1588.  
  1589.     for (i = 0; i < 3; ++i) {        /* Fill the numMoves fields. */
  1590.         CNum2Ctl(window, kTC1NumMoves + i * (kTC2NumMoves - kTC1NumMoves), &ctl);
  1591.         CTESetKeyFilter((TEHandle)GetControlReference(ctl), DigitsOnly);        /* Just digits again. */
  1592.         m = mDerefRoot(root)->numMovesInTimeControl[i];
  1593.         if (m) {
  1594.             pcpydec(pstr, m);
  1595.             CTEPutPStr(ctl, pstr);
  1596.             CTESetSelect(0, 3, (TEHandle)GetControlReference(ctl));
  1597.         }
  1598.     }
  1599.  
  1600.     return(noErr);
  1601. }
  1602.  
  1603.  
  1604.  
  1605. /*****************************************************************************/
  1606.  
  1607.  
  1608.  
  1609. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1610.  
  1611. /* The code below assumes that you are using the hierarchical document package.
  1612. ** If you are, the entire hierarchical document is read in with just these two
  1613. ** calls.  If you don't use it, you are on your own.  See DTS.StyleChat for an
  1614. ** example of an application that uses the DTS.framework without the hierarchical
  1615. ** document package. */
  1616.  
  1617. #pragma segment TheDoc
  1618. OSErr    ReadDocument(FileRecHndl frHndl)
  1619. {
  1620.     OSErr    err;
  1621.  
  1622.     err = DefaultReadDocument(frHndl);
  1623.     if (!err)
  1624.         DefaultReadDocumentFixup(frHndl);
  1625.  
  1626.     return(err);
  1627. }
  1628.  
  1629.  
  1630.  
  1631. /*****************************************************************************/
  1632.  
  1633.  
  1634.  
  1635. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1636.  
  1637. /* Resize application specific content (Called by ResizeWindow).
  1638. **
  1639. ** This gets called when a user does a zoom or window resizing operation.
  1640. ** It is possible that things in the content need to be resized in conjunction
  1641. ** with the resizing of the window. */
  1642.  
  1643. #pragma segment TheDoc
  1644. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  1645. {
  1646. #ifndef __MWERKS__
  1647. #pragma unused (window, oldh, oldv)
  1648. #endif
  1649.  
  1650.     /* See DTS.StyleChat for a sample usage of this function. */
  1651. }
  1652.  
  1653.  
  1654.  
  1655. /*****************************************************************************/
  1656.  
  1657.  
  1658.  
  1659. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1660.  
  1661. /* Scroll application specific frame (Called by DoScrollFrame).
  1662. **
  1663. ** Some applications may need to scroll the "frame" of the document along
  1664. ** with the document contents.  This is common for applications with rulers,
  1665. ** or other similar sidebar items. */
  1666.  
  1667. #pragma segment TheDoc
  1668. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  1669. {
  1670. #ifndef __MWERKS__
  1671. #pragma unused (frHndl, window, dh, dv)
  1672. #endif
  1673. }
  1674.  
  1675.  
  1676.  
  1677. /*****************************************************************************/
  1678.  
  1679.  
  1680.  
  1681. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1682.  
  1683. /* Since the hierarchical document package isn't used by DTS.StyleChat,
  1684. ** this function actually never gets called. */
  1685.  
  1686. #pragma segment TheDoc
  1687. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  1688. {
  1689. #ifndef __MWERKS__
  1690. #pragma unused (frHndl, contOrg, afterUndo)
  1691. #endif
  1692.  
  1693.     /* See DTS.Draw for an example of what you might do here. */
  1694. }
  1695.  
  1696.  
  1697.  
  1698. /*****************************************************************************/
  1699.  
  1700.  
  1701.  
  1702. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1703.  
  1704. /* This function is where you adjust the cursor to reflect the location in the
  1705. ** document or window.  You have the additional input of gCursorRgn to deal
  1706. ** with.  The way that the cursor handling works is as follows:
  1707. ** 1) The application calls DoWindowCursor().
  1708. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  1709. **    It looks at the document's windowCursorProc and checks to see if the document
  1710. **    has one.  If the document doesn't have one, then it assumes that that window
  1711. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  1712. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  1713. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  1714. **    is set to an arrow (for the non-document area of the screen).
  1715. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  1716. **    job is as follows:
  1717. **    a) If the cursor is over a position that is determined by the window, then
  1718. **       the proc removes other areas from gCursorRgn.  Note that it should not
  1719. **       simply set the area to what it "thinks" is the correct area.  This window
  1720. **       may not be the front-most.  Other windows will have already been subtracted
  1721. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  1722. **       and should be passed to WaitNextEvent calls in the application (already the case
  1723. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  1724. **       You should also return true, as the cursor has been determined.
  1725. **    b) If the cursor is not over a position for this window, then you should
  1726. **       return.  You will either pass back true or false.  If you don't wish
  1727. **       windows behind this window to have a shot at cursor determination, then
  1728. **       return true.  This states that the cursor is "determined".  It is, in the
  1729. **       sense that no further determination will occur.  If you return false, then
  1730. **       other windows get a shot at determining the cursor.
  1731. **
  1732. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  1733. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  1734. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  1735. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  1736. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  1737. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  1738. ** to the return result of DoSetResCursor(), and you will be set. */
  1739.  
  1740. #pragma segment TheDoc
  1741. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  1742. {
  1743.     WindowPtr        oldPort;
  1744.     RgnHandle        contRgn;
  1745.     Rect            rr, teViewRct, contRct;
  1746.     TEHandle        teHndl;
  1747.     ControlHandle    viewCtl;
  1748.     Point            contOrg;
  1749.  
  1750.     if (!window) {
  1751.         SetCursor(gCursorPtr = &qd.arrow);
  1752.         return(true);
  1753.     }
  1754.  
  1755.     oldPort = SetFilePort(frHndl);
  1756.     window = (*frHndl)->fileState.window;
  1757.  
  1758.     GetContentOrigin(window, &contOrg);
  1759.     SetOrigin(contOrg.h, contOrg.v);        /* Scroll position of window. */
  1760.  
  1761.     GetContentRect(window, &contRct);
  1762.         /* This returns the content portion of the window in local coordinates,
  1763.         ** less document scrollbar and sidebar areas. */
  1764.  
  1765.     contRgn = NewRgn();
  1766.     for (viewCtl = nil; ((viewCtl = CTENext(window, &teHndl, viewCtl, 1, false)) != nil);) {
  1767.         teViewRct = (*teHndl)->viewRect;
  1768.         SectRect(&teViewRct, &contRct, &rr);
  1769.         LocalToGlobalRect(&rr);
  1770.         if ((*viewCtl)->contrlVis) {
  1771.             if (!CTEReadOnly(teHndl)) {
  1772.                 RectRgn(contRgn, &rr);
  1773.                 if (PtInRect(globalPt, &rr)) {
  1774.                     gCursorPtr = DoSetResCursor(iBeamCursorSmall);
  1775.                     SectRgn(gCursorRgn, contRgn, gCursorRgn);
  1776.                     DisposeRgn(contRgn);
  1777.                     SetPort(oldPort);
  1778.                     return(true);
  1779.                 }
  1780.                 DiffRgn(gCursorRgn, contRgn, gCursorRgn);
  1781.             }
  1782.         }
  1783.     }
  1784.     SetCursor(gCursorPtr = &qd.arrow);
  1785.     DisposeRgn(contRgn);
  1786.     return(true);
  1787. }
  1788.  
  1789.  
  1790.  
  1791. /*****************************************************************************/
  1792.  
  1793.  
  1794.  
  1795. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1796.  
  1797. /* After the DTS.Lib framework disposes of a window, it calls here.  This is
  1798. ** to give the application a chance to do any additional tasks related to
  1799. ** a window closing.  DTS.StyleChat doesn't have anything else extra to do. */
  1800.  
  1801. #pragma segment TheDoc
  1802. void    WindowGoneFixup(WindowPtr window)
  1803. {
  1804. #ifndef __MWERKS__
  1805. #pragma unused (window)
  1806. #endif
  1807. }
  1808.  
  1809.  
  1810.  
  1811. /*****************************************************************************/
  1812.  
  1813.  
  1814.  
  1815. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1816.  
  1817. /* The reverse function of ReadDocument. */
  1818.  
  1819. #pragma segment TheDoc
  1820. OSErr    WriteDocument(FileRecHndl frHndl)
  1821. {
  1822.     FetchClockValues(frHndl, 0);
  1823.         /* Have to make sure that the latest info in the controls will be written out. */
  1824.  
  1825.     return(DefaultWriteDocument(frHndl));
  1826. }
  1827.  
  1828.  
  1829.  
  1830. /*****************************************************************************/
  1831.  
  1832.  
  1833.  
  1834. /* •• You don't call this.  DTS.Lib..framework does at open-application time. •• */
  1835.  
  1836. #pragma segment TheDoc
  1837. OSErr    DoOpenApplication(void)
  1838. {
  1839.     DisposeLayer(gWorkLayer);        /* May be restarted with new clock sizes. */
  1840.     DisposeLayer(gBackLayer[0]);
  1841.     DisposeLayer(gBackLayer[1]);
  1842.     gWorkLayer = nil;
  1843.     gBackLayer[0] = nil;
  1844.     gBackLayer[1] = nil;
  1845.     return(noErr);
  1846. }
  1847.  
  1848.  
  1849.  
  1850. /*****************************************************************************/
  1851. /*****************************************************************************/
  1852. /*****************************************************************************/
  1853.  
  1854.  
  1855.  
  1856. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1857.  
  1858. #pragma segment TheDoc
  1859. Boolean    AdjustMenuItems(WindowPtr window, short menuID)
  1860. {
  1861.     Boolean        redrawMenuBar;
  1862.     MenuHandle    menu;
  1863.  
  1864.     redrawMenuBar = false;
  1865.  
  1866.     switch (menuID) {
  1867.         case mFile:
  1868.             redrawMenuBar = DoAdjustFileMenu(window);
  1869.             break;
  1870.         case mEdit:
  1871.             redrawMenuBar = DoAdjustEditMenu(window);
  1872.             break;
  1873.         default:
  1874.             menu = GetMenuHandle(menuID);
  1875.             if (menu)
  1876.                 (*menu)->enableFlags |= 0xFFFFFFFEL;
  1877.             break;
  1878.     }
  1879.  
  1880.     return(redrawMenuBar);
  1881. }
  1882.  
  1883.  
  1884.  
  1885. /*****************************************************************************/
  1886.  
  1887.  
  1888.  
  1889. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  1890.  
  1891. #pragma segment TheDoc
  1892. Boolean    DoMenuItem(WindowPtr window, short menuID, short menuItem)
  1893. {
  1894. #ifndef __MWERKS__
  1895. #pragma unused (window)
  1896. #endif
  1897.  
  1898.     return(DoMenuCommand(menuID, menuItem));
  1899. }
  1900.  
  1901.  
  1902.  
  1903. /*****************************************************************************/
  1904.  
  1905.  
  1906.  
  1907. static short    MyGetRadioButtonChoice(WindowPtr window, short famNum)
  1908. {
  1909.     ControlHandle    nextCtl;
  1910.     short            firstInFam, cnum, chosen;
  1911.  
  1912.     nextCtl = ((WindowPeek)window)->controlList;
  1913.     for (firstInFam = chosen = 0;;) {
  1914.         if (!nextCtl) {
  1915.             if (!chosen) return(-1);
  1916.                 /* If a proper radio button family was passed in, this can't
  1917.                 ** happen.  The -1 is an error that indicates that the
  1918.                 ** requested family didn't exist, or that there was no selected
  1919.                 ** radio of the requested family. */
  1920.             return(chosen - firstInFam);
  1921.         }
  1922.         if (IsButtonsCtl(nextCtl)) {
  1923.             if (GetControlReference(nextCtl) == famNum) {
  1924.                 cnum = Ctl2CNum(nextCtl);
  1925.                 if (!firstInFam)
  1926.                     firstInFam = cnum;
  1927.                 else {
  1928.                     if (firstInFam > cnum)
  1929.                         firstInFam = cnum;
  1930.                 }
  1931.                 if (GetControlValue(nextCtl)) chosen = cnum;
  1932.             }
  1933.         }
  1934.         nextCtl = (*nextCtl)->nextControl;
  1935.     }
  1936. }
  1937.  
  1938.  
  1939.  
  1940. /*****************************************************************************/
  1941.  
  1942.  
  1943.  
  1944. short    MyGetButtonVariant(ControlHandle ctl, Boolean *stop)
  1945. {
  1946.     if (ctl)
  1947.         if (IsButtonsCtl(ctl))
  1948.             return(2);
  1949.  
  1950.     return(-1);
  1951. }
  1952.  
  1953.  
  1954.  
  1955. pascal long    xxx(short varCode, ControlHandle ctl, short msg, long parm);
  1956. pascal long    xxx(short varCode, ControlHandle ctl, short msg, long parm)
  1957. {
  1958.     long    retVal;
  1959.     Rect    rct;
  1960.  
  1961.  
  1962.     retVal = 0;
  1963.  
  1964.     rct = (*ctl)->contrlRect;
  1965.     switch (msg) {
  1966.         case drawCntl:
  1967.             FrameRect(&rct);
  1968.             break;
  1969.  
  1970.         case calcCRgns:
  1971.         case calcCntlRgn:
  1972.         case calcThumbRgn:
  1973.             if (msg == calcCRgns) parm &= 0x00FFFFFF;
  1974.             RectRgn((RgnHandle)parm, &rct);
  1975.             if (msg != calcCRgns) retVal = 1;
  1976.             break;
  1977.     }
  1978.  
  1979.     return(retVal);
  1980. }
  1981.  
  1982.  
  1983.  
  1984.