home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Technology Seed / Jan. '98 ATS.toast / NavServices1.0b3 / Navigation Services SDK / Examples / SimpleText / SimpleText ƒ / SimpleText.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-01-12  |  146.0 KB  |  5,565 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        SimpleText.c
  3.  
  4.     Contains:    xxx put contents here xxx
  5.  
  6.     Version:    xxx put the technology version here xxx
  7.  
  8.     Copyright:    © 1997 by Apple Computer, Inc., all rights reserved.
  9.  
  10.     File Ownership:
  11.  
  12.         DRI:                Yan Arrouye
  13.  
  14.         Other Contact:        xxx put alternate contact (owner’s functional manager) here xxx
  15.  
  16.         Technology:            xxx put the technology group name here xxx
  17.  
  18.     Writers:
  19.  
  20.         (Yan)    Yan Arrouye
  21.  
  22.     Change History (most recent first):
  23.  
  24.          <5>    10/02/97    Yan        Call NavLoad / NavUnload
  25.          <4>    /19/1997    Yan        Mixed mode support
  26.          <3>     4/11/97    Yan        Added support for windows staggering
  27. */
  28.  
  29. /*
  30.     File:        SimpleText.c
  31.  
  32.     Contains:    SimpleText - a simple document editing application for shipping
  33.                              with system software.
  34.  
  35.     Version:    SimpleText 1.4 or later
  36.  
  37. ** Copyright 1993-1996 Apple Computer. All rights reserved.
  38. **
  39. **    You may incorporate this sample code into your applications without
  40. **    restriction, though the sample code has been provided "AS IS" and the
  41. **    responsibility for its operation is 100% yours.  However, what you are
  42. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  43. **    after having made changes. If you're going to re-distribute the source,
  44. **    we require that you make it clear in the source that the code was
  45. **    descended from Apple Sample Code, but that you've made changes.
  46.  
  47. */
  48.  
  49. #include "MacIncludes.h"
  50.  
  51. #include <ImageCompression.h>    // for CustomGetFilePreview
  52. #include <Threads.h>
  53.  
  54. #define CompilingMain=1
  55. #include "SimpleText.h"
  56. #include "Clipboard.h"
  57.  
  58. #include "NavigationServicesSupport.h"
  59.  
  60.  
  61. // --------------------------------------------------------------------------------------------------------------
  62. // INTERNAL TYPES AND TYPEDEFS
  63. // --------------------------------------------------------------------------------------------------------------
  64.  
  65.  
  66. // refCon value between SimpleCatchShape and GXInstallQDTranslator    
  67. typedef struct
  68.     {
  69.     gxShape         thePage;
  70.     gxRectangle        thePageRectangle;
  71.     Boolean            doLayout;
  72.     gxJob            theJob;
  73.     } CatchRefCon;
  74.  
  75. // --------------------------------------------------------------------------------------------------------------
  76. // FORWARD DECLARES
  77. // --------------------------------------------------------------------------------------------------------------
  78. OSErr     DoActivate(WindowRef pWindow, Boolean activating);
  79. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult);
  80. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls);
  81. Boolean CommandToIDs(short commandID, short * menuID, short *itemID);
  82. void     AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn);
  83.  
  84. // --------------------------------------------------------------------------------------------------------------
  85. // GLOBAL VARIABLES
  86. // --------------------------------------------------------------------------------------------------------------
  87. EventRecord            gEvent;                    // currently pending event
  88. Boolean                gAllDone;                // true if the application is the in process of terminating
  89. MachineInfoRec        gMachineInfo;            // info about abilities and options installed on this machine
  90. short                gApplicationResFile;    // resource fork of application
  91. RgnHandle            gCursorRgn;                // region to control the cursor apearence
  92. AGRefNum            gAGRefNum = -1;            // AppleGuide database which is open
  93. FSSpec                gAGSpec;                // where to find our database
  94. AGCoachRefNum        gAGCoachRefNum = -1;    // coach handler refNum
  95. FontMappingHandle    gFontMappingList = nil;    // list of font mappings
  96. ThreadID            gFontThread;            // thread that builds font menu
  97. ThreadID            gAGThread;                // thread that looks for AppleGuide database
  98. ThreadID            gStarterThread;            // starts our other threads for us
  99. Boolean                gDontYield;                // whether our threads should yield
  100. void*                gThreadResults;            // scratch space for thread results
  101.  
  102. // These variables are for the find/replace commands
  103. Str255            gFindString = "\p", gReplaceString = "\p";
  104. Boolean            gWrapAround = false, gCaseSensitive = false;
  105.  
  106. // Metrowerks MWCRuntime.lib defines qd for us on PPC, and their
  107. // __runtime module does under the 68K case. OTOH, neither SC nor
  108. // MrC give us qd for free, so we need it there. I'm still not
  109. // certain which way to go for the ThinkC or Symantec PPC case.
  110. #if !defined(__MWERKS__)
  111. // QuickDraw globals
  112. QDGlobals        qd;
  113. #endif
  114.  
  115. // --------------------------------------------------------------------------------------------------------------
  116. #pragma segment Utility
  117.  
  118. static pascal Boolean AlertFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  119. {
  120.     if (theEvent->what == activateEvt && (DialogRef) theEvent->message == theDialog)
  121.         {
  122.         SetDialogDefaultItem(theDialog, 1);
  123.         }
  124.  
  125.     if (StdFilterProc(theDialog, theEvent, itemHit))
  126.         return true;
  127.  
  128.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  129.     // drastically changing how the system handles the menu bar during our alert)
  130.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  131.         {
  132.         HandleEvent(theEvent);
  133.         }
  134.  
  135.     return false;
  136.  
  137. } // AlertFilter
  138.  
  139.  
  140. void ConductErrorDialog(OSErr error, short commandID, short alertType)
  141. {
  142.     long        foundError;            // The error, converted to a number
  143.     short        stringIndex;        // Index into the strings
  144.     Str255        errorText;            // the error in a string format
  145.     
  146.     // Start with no error so far
  147.     foundError = 0;
  148.     
  149.     // Start with the first string
  150.     stringIndex = 1;
  151.     
  152.     // Loop until we find an error string
  153.     errorText[0] = 0;
  154.  
  155.     do
  156.         {
  157.         // Get the string, and convert it to a number
  158.         GetIndString(errorText, kErrorBaseID + commandID, stringIndex);
  159.         if (errorText[0] == 0)
  160.             break;
  161.         StringToNum(errorText, &foundError);
  162.         
  163.         // If we reach the last string, or we match the error code
  164.         if ((foundError == 0) ||
  165.             (foundError == error))
  166.             {
  167.             // Get the text string for this error
  168.             GetIndString(errorText, kErrorBaseID + commandID, stringIndex+1);
  169.             }
  170.         else
  171.             {
  172.             // Otherwise, make us continue until we get a string
  173.             errorText[0] = 0;
  174.             }
  175.             
  176.         // Advance so we get the next string number
  177.         stringIndex += 2;
  178.         
  179.         } while (errorText[0] == 0);                // errorText[0] == 0
  180.         
  181.     if (errorText[0] != 0)
  182.         {
  183.         DialogRef    dPtr;
  184.         short        hit;
  185.         
  186.         SetCursor(&qd.arrow);
  187.         ParamText(errorText, "\p", "\p", "\p");
  188.         
  189.         #if !GENERATINGPOWERPC
  190.             if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  191.                 {
  192.                 short ** hDialog;
  193.                 
  194.                 hDialog = (short**) GetResource('DLOG', kErrorBaseID + alertType);
  195.                 (*hDialog)[4] = dBoxProc;
  196.                 
  197.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  198.                 
  199.                 do
  200.                     {
  201.                     ModalDialog(nil, &hit);
  202.                     } while (hit != ok);
  203.                 
  204.                 DisposeDialog(dPtr);
  205.                 }
  206.             else
  207.                 {
  208.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  209.                 
  210.                 SetDialogDefaultItem(dPtr, ok);
  211.                 
  212.                 BeginMovableModal();
  213.                 
  214.                 do
  215.                     {
  216.                     MovableModalDialog(nil, &hit);
  217.                     } while (hit != ok);
  218.                 
  219.                 DisposeDialog(dPtr);
  220.                 EndMovableModal();
  221.                 }
  222.         #else
  223.             dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  224.             
  225.             SetDialogDefaultItem(dPtr, ok);
  226.             
  227.             BeginMovableModal();
  228.             
  229.             do
  230.                 {
  231.                 MovableModalDialog(nil, &hit);
  232.                 } while (hit != ok);
  233.             
  234.             DisposeDialog(dPtr);
  235.             EndMovableModal();
  236.         #endif
  237.         }
  238.         
  239. } // ConductErrorDialog
  240.  
  241. // --------------------------------------------------------------------------------------------------------------
  242. #pragma segment Utility
  243.  
  244. static void MovableModalMenus(DialogRef dPtr, short *pItem, long menuResult)
  245. {
  246.     short    iCut, iCopy, iClear, iPaste;
  247.     short    editMenu;
  248.     short    menuItem = menuResult & 0xFFFF;
  249.     
  250.     // find out where edit menus are
  251.     CommandToIDs(cCut, &editMenu, &iCut);
  252.     CommandToIDs(cCopy, &editMenu, &iCopy);
  253.     CommandToIDs(cClear, &editMenu, &iClear);
  254.     CommandToIDs(cPaste, &editMenu, &iPaste);
  255.     
  256.     HiliteMenu(0);
  257.     switch (menuResult >> 16)
  258.         {
  259.         case mApple:
  260.             {
  261.             Str255    tempString;
  262.             
  263.             GetMenuItemText(GetMenuHandle(menuResult>>16), menuItem, tempString);
  264.             OpenDeskAcc(tempString);
  265.             }
  266.             break;
  267.             
  268.         case mEdit:
  269.             {
  270.             short    type;
  271.             Handle    item;
  272.             Rect    box;
  273.             short    editField = GetDialogKeyboardFocusItem(dPtr);
  274.             
  275.             // return typed item, if it isn't disabled
  276.             GetDialogItem(dPtr, editField, &type, &item, &box);
  277.             if ((type & itemDisable) == 0)
  278.                 *pItem = editField;
  279.                 
  280.             if (menuItem == iCut)
  281.                 {
  282.                 DialogCut(dPtr);
  283.                 ZeroScrap();
  284.                 TEToScrap();
  285.                 }
  286.                 
  287.             if (menuItem == iCopy)
  288.                 {
  289.                 DialogCopy(dPtr);
  290.                 ZeroScrap();
  291.                 TEToScrap();
  292.                 }
  293.                 
  294.             if (menuItem == iClear)
  295.                 DialogDelete(dPtr);
  296.                 
  297.             if (menuItem == iPaste)
  298.                 DialogPaste(dPtr);
  299.             }
  300.             break;
  301.         }
  302.         
  303. } // MovableModalMenus
  304.  
  305. // --------------------------------------------------------------------------------------------------------------
  306. #pragma segment Utility
  307.  
  308. void MovableModalDialog(ModalFilterProcPtr filterProc, short *pItem)
  309. /*
  310.     Call this as you would ModalDialog, when the dialog is moveable
  311.     modal.
  312.     
  313.     However, first call BeginMovableModal, and afterwards (after
  314.     disposing of dialog) call EndMovableModal.
  315. */
  316. {
  317.     GrafPtr     curPort;
  318.     DialogRef    dPtr = FrontWindow();
  319.     
  320.     *pItem = 0;    
  321.     if (dPtr)
  322.         {
  323.         GetPort(&curPort);
  324.         SetPort(dPtr);
  325.         
  326.         do
  327.             {
  328.             WaitNextEvent(mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask,
  329.                             &gEvent, 0, nil);
  330.             
  331.             // call the filter proc
  332.             if ( (filterProc) && ((*filterProc) (dPtr, &gEvent, pItem)) )
  333.                 break;
  334.                             
  335.             // call the basic filtering
  336.             if (StdFilterProc(dPtr, &gEvent, pItem))
  337.                 break;
  338.                 
  339.             // handle keyboard
  340.             if ((gEvent.what == keyDown) && (gEvent.modifiers & cmdKey))
  341.                 {
  342.                 MovableModalMenus(dPtr, pItem, MenuKey(gEvent.message & charCodeMask));
  343.                 break;
  344.                 }
  345.                 
  346.             // handle clicks and drags
  347.             if (gEvent.what == mouseDown)
  348.                 {
  349.                 WindowRef    whichWindow;
  350.                 short        part = FindWindow(gEvent.where, &whichWindow);
  351.                 
  352.                 // menu bar events
  353.                 if (part == inMenuBar)
  354.                     {
  355.                     MovableModalMenus(dPtr, pItem, MenuSelect(gEvent.where));
  356.                     break;
  357.                     }
  358.                     
  359.                 // check for outside of our window
  360.                 if (!PtInRgn(gEvent.where, ((WindowPeek)dPtr)->strucRgn))
  361.                     {
  362.                     SysBeep(1);
  363.                     gEvent.what = nullEvent;
  364.                     }
  365.                     
  366.                 // drag the window around
  367.                 if ( (part == inDrag) && (whichWindow == dPtr) )
  368.                     {
  369.                     Rect    tempRect = (**GetGrayRgn()).rgnBBox;
  370.                     
  371.                     DragWindow(GetDialogWindow(dPtr), gEvent.where, &tempRect);
  372.                     gEvent.what = nullEvent;
  373.                     }
  374.                 }
  375.                 
  376.             // check with standard dialog stuff    
  377.             {
  378.             DialogRef    tempDialog;
  379.             
  380.             if ( IsDialogEvent(&gEvent) && DialogSelect(&gEvent, &tempDialog, pItem) )
  381.                 break;
  382.             }
  383.             
  384.             // handle updates
  385.             if (gEvent.what == updateEvt)
  386.                 {
  387.                 HandleEvent(&gEvent);
  388.                 break;
  389.                 }
  390.             } while (true);
  391.         
  392.         SetPort(curPort);
  393.         }
  394.         
  395. } // MovableModalDialog
  396.  
  397. // --------------------------------------------------------------------------------------------------------------
  398. #pragma segment Utility
  399.  
  400. void BeginMovableModal(void)
  401. {
  402.     DialogRef    dPtr = FrontWindow();
  403.     WindowRef    nextWindow = GetNextWindow(dPtr);
  404.     
  405.     if (nextWindow)
  406.         DoActivate(nextWindow, false);
  407.     AdjustMenus(GetDialogWindow(dPtr), (GetDialogKeyboardFocusItem(dPtr) > 0), false);
  408.  
  409. } // BeginMovableModal
  410.  
  411. // --------------------------------------------------------------------------------------------------------------
  412. #pragma segment Utility
  413.  
  414. void EndMovableModal(void)
  415. {
  416.     WindowRef    nextWindow = FrontWindow();
  417.     
  418.     AdjustMenus(nextWindow, true, false);
  419.     if (nextWindow)
  420.         DoActivate(nextWindow, true);
  421.     
  422. } // EndMovableModal
  423.  
  424. // --------------------------------------------------------------------------------------------------------------
  425. #pragma segment Utility
  426.  
  427. short ConductFindOrReplaceDialog(short dialogID)
  428. {
  429.     DialogRef    dPtr;
  430.     short        hit;
  431.     
  432.     dPtr = GetNewDialog(dialogID, nil, (WindowRef)-1);
  433.     if (dPtr)
  434.         {
  435.         short    kind;
  436.         Rect    box;
  437.         Handle    item;
  438.         
  439.         // standard default behavior
  440.         SetDialogDefaultItem(dPtr, ok);
  441.         SetDialogCancelItem (dPtr, cancel);
  442.         SetDialogTracksCursor(dPtr, true);
  443.         
  444.         // Find string
  445.         GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  446.         SetDialogItemText(item, gFindString);
  447.  
  448.         // check boxes
  449.         GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  450.         SetControlValue((ControlRef)item, gCaseSensitive);
  451.         GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  452.         SetControlValue((ControlRef)item, gWrapAround);
  453.         
  454.         if (dialogID == kReplaceWindowID)
  455.             {
  456.             // Replace string
  457.             GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  458.             SetDialogItemText(item, gReplaceString);
  459.             }
  460.         
  461.         // select the search text by default
  462.         SelectDialogItemText(dPtr, iFindEdit, 0, 32767);
  463.         
  464.         // and away we go!
  465.         ShowWindow(GetDialogWindow(dPtr));
  466.         BeginMovableModal();
  467.         
  468.         do
  469.             {
  470.             MovableModalDialog(nil, &hit);
  471.             switch (hit)
  472.                 {
  473.                 case iCaseSensitive:
  474.                 case iWrapAround:
  475.                     GetDialogItem(dPtr, hit, &kind, &item, &box);
  476.                     SetControlValue((ControlRef)item, 1-GetControlValue((ControlRef)item));
  477.                     break;
  478.                 }
  479.             } while ( (hit != ok) && (hit != cancel) && (hit != iReplaceAll) );
  480.         
  481.         if (hit != cancel)
  482.             {
  483.             // Find string
  484.             GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  485.             GetDialogItemText(item, gFindString);
  486.     
  487.             // check boxes
  488.             GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  489.             gCaseSensitive = GetControlValue((ControlRef)item);
  490.             GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  491.             gWrapAround = GetControlValue((ControlRef)item);
  492.             
  493.             if (dialogID == kReplaceWindowID)
  494.                 {
  495.                 // Replace string
  496.                 GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  497.                 GetDialogItemText(item, gReplaceString);
  498.                 }
  499.             }
  500.             
  501.         DisposeDialog(dPtr);
  502.         EndMovableModal();
  503.         }
  504.         
  505.     return(hit);
  506.     
  507. } // ConductFindOrReplaceDialog
  508.  
  509. // --------------------------------------------------------------------------------------------------------------
  510. #pragma segment Utility
  511.  
  512. void SetWatchCursor(void)
  513. {
  514.     CursHandle    theWatch;
  515.         
  516.     theWatch = GetCursor(watchCursor);
  517.     if (theWatch)
  518.         {
  519.         char    oldState;
  520.         
  521.         oldState = HGetState((Handle) theWatch);
  522.         HLock((Handle) theWatch);
  523.         SetCursor(*theWatch);
  524.         HSetState((Handle) theWatch, oldState);
  525.         }
  526.         
  527. } // SetWatchCursor
  528.  
  529. // --------------------------------------------------------------------------------------------------------------
  530. #pragma segment Utility
  531.  
  532. void LongRectToRect(LongRect* longRect, Rect *rect)
  533. {
  534.     rect->top         = longRect->top;
  535.     rect->left         = longRect->left;
  536.     rect->bottom     = longRect->bottom;
  537.     rect->right     = longRect->right;
  538.     
  539. } // LongRectToRect
  540.  
  541. // --------------------------------------------------------------------------------------------------------------
  542. #pragma segment Utility
  543.  
  544. void RectToLongRect(Rect *rect, LongRect *longRect)
  545. {
  546.     longRect->top         = rect->top;
  547.     longRect->left         = rect->left;
  548.     longRect->bottom     = rect->bottom;
  549.     longRect->right     = rect->right;
  550.     
  551. } // RectToLongRect
  552.  
  553. // --------------------------------------------------------------------------------------------------------------
  554. #pragma segment Utility
  555.  
  556. void GetPICTRectangleAt72dpi(PicHandle hPicture, Rect *pictureRect)
  557. {
  558.     typedef struct FixedRect {
  559.         Fixed left;
  560.         Fixed top;
  561.         Fixed right;
  562.         Fixed bottom;
  563.     } FixedRect;
  564.     
  565.     typedef struct {
  566.         Picture                pictInfo;
  567.         unsigned short        versionOp;        // 0x1101
  568.         Byte                opCodes[1];
  569.     } PICTHeaderVer1;
  570.     
  571.     typedef struct {
  572.         Picture            pictInfo;
  573.         unsigned short    versionOp;        // 0x0011
  574.         unsigned short    versionOp2;        // 0x02ff
  575.         unsigned short    headerOp;        // 0x0c00
  576.         unsigned short    version;        // 0xffff
  577.         unsigned short    version2;        // 0xffff
  578.         FixedRect        pictBounds;
  579.         unsigned long    reserved;
  580.         unsigned short    opCodes[1];
  581.     } PICTHeaderVer2;
  582.     
  583.     typedef struct {
  584.         Picture            pictInfo;
  585.         unsigned short    versionOp;        // 0x0011
  586.         unsigned short    versionOp2;        // 0x02ff
  587.         unsigned short    headerOp;        // 0x0c00
  588.         unsigned short    version;        // 0xfffe
  589.         unsigned short    reserved;        // 0x0000
  590.         Fixed            hRes;
  591.         Fixed            vRes;
  592.         Rect            pictBounds;
  593.         unsigned long    reserved2;
  594.         unsigned short    opCodes[1];
  595.     } PICTHeaderVer2Ext;
  596.  
  597.     Fixed            hRes, vRes;
  598.     PICTHeaderVer1* pPict = (PICTHeaderVer1*) *hPicture;
  599.  
  600.     hRes = vRes = ff(72);        // assume 72 dpi
  601.  
  602.     if (pPict->versionOp == 0x0011) 
  603.         {    
  604.         // Version 2 PICT
  605.     
  606.         PICTHeaderVer2* pPict2 = (PICTHeaderVer2*) pPict;
  607.         
  608.         if (pPict2->version == 0xfffe) 
  609.             {    
  610.             // Extended Version 2
  611.             PICTHeaderVer2Ext* pPict2ext = (PICTHeaderVer2Ext*) pPict;
  612.             hRes = pPict2ext->hRes;
  613.             vRes = pPict2ext->vRes;
  614.             }
  615.         }
  616.  
  617.     hRes = FixDiv(hRes, ff(72));
  618.     vRes = FixDiv(vRes, ff(72));
  619.     pictureRect->left     = Fix2Long(FixDiv( ff((**hPicture).picFrame.left),         hRes ));
  620.     pictureRect->right     = Fix2Long(FixDiv( ff((**hPicture).picFrame.right),     hRes ));
  621.     pictureRect->top     = Fix2Long(FixDiv( ff((**hPicture).picFrame.top),         vRes ));
  622.     pictureRect->bottom = Fix2Long(FixDiv( ff((**hPicture).picFrame.bottom),     vRes ));
  623.     
  624. } // GetPICTRectangleAt72dpi
  625.  
  626. // --------------------------------------------------------------------------------------------------------------
  627. #pragma segment Utility
  628.  
  629. static WindowDataPtr    GetWindowInfo(WindowRef pWindow)
  630. {
  631.     WindowDataPtr result = nil;
  632.     
  633.     if     (
  634.         (pWindow) &&
  635.         (GetWindowKind(pWindow) == userKind)
  636.         )
  637.         result = (WindowDataPtr) GetWRefCon(pWindow);
  638.  
  639.     return result;
  640.     
  641. } // GetWindowInfo
  642.  
  643. // --------------------------------------------------------------------------------------------------------------
  644. #pragma segment Utility
  645.  
  646. static short ZeroStringSub(Str255 destString, Str255 subStr)
  647.     // returns number of substitutions performed
  648. {
  649.     OSErr    anErr;
  650.     Handle    destHandle = nil;
  651.     Handle    subHandle = nil;
  652.     short    count = 0;
  653.  
  654.     anErr = PtrToHand(&destString[1], &destHandle, destString[0]);
  655.     if (anErr == noErr)
  656.         {        
  657.         anErr = PtrToHand(&subStr[1], &subHandle, subStr[0]);
  658.         if (anErr == noErr)
  659.             {
  660.             count = ReplaceText(destHandle, subHandle, "\p^0");        // error or # of substitutions
  661.                         
  662.             destString[0] = GetHandleSize(destHandle);
  663.             BlockMoveData(*destHandle, &destString[1], destString[0]);
  664.             }
  665.         }
  666.  
  667.     DisposeHandle(destHandle);
  668.     DisposeHandle(subHandle);
  669.  
  670.     if (count < 0)
  671.         count = 0;        // change error code into count = 0 substitutions
  672.  
  673.     return count;
  674.  
  675. } // ZeroStringSub
  676.  
  677. // --------------------------------------------------------------------------------------------------------------
  678. // SEARCH/REPLACE UTILITY FUNCTIONS
  679. // --------------------------------------------------------------------------------------------------------------
  680. static Boolean IsThisTheString(
  681.             Ptr p,                        // pointer to check
  682.             Str255 searchString,        // string to check for
  683.             Boolean isCaseSensitive)    // case sensitive check or not
  684. /*
  685.     Returns true if the supplied string is at the specified offset.
  686.     Otherwise returns false.
  687. */
  688. {
  689.     Boolean    returnValue = false;
  690.     
  691.     if (isCaseSensitive)
  692.         returnValue = ( IUMagString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  693.     else
  694.         returnValue = ( IUMagIDString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  695.         
  696.     return(returnValue);
  697.     
  698. } // IsThisTheString
  699.  
  700. // --------------------------------------------------------------------------------------------------------------
  701.  
  702. Boolean PerformSearch(
  703.         Handle    h,                    // handle to search
  704.         long start,                    // offset to begin with
  705.         Str255 searchString,        // string to search for
  706.         Boolean isCaseSensitive,    // case sensitive search
  707.         Boolean isBackwards,        // search backwards from starting point
  708.         Boolean isWraparound,        // wrap search around from end->begining
  709.         long * pNewStart,            // returned new selection start
  710.         long * pNewEnd)                // returned new selection end
  711. /*
  712.     Performs a search on the supplied handle, starting at the provided
  713.     offset.  Returns the new selection start and end values, and true
  714.     if the search is successful.  Otherwise it returns false.
  715. */
  716. {
  717.     char    flags;
  718.     Ptr        startPtr;
  719.     Ptr        endPtr;
  720.     Ptr        searchPtr;
  721.     Boolean    foundIt = false;
  722.     
  723.     flags = HGetState(h);
  724.     HLock(h);
  725.             
  726.     // back up one when searching backwards, or we'll hit every time on the current
  727.     // character
  728.     if (isBackwards)
  729.         {
  730.         if (start != 0)
  731.             {
  732.             --start;
  733.             }
  734.         else
  735.             {
  736.             if (isWraparound)
  737.                 start = GetHandleSize(h);
  738.             else
  739.                 return(false);
  740.             }
  741.         }
  742.         
  743.     // determine the bounds of the searching
  744.     startPtr = (*h) + start;
  745.     if ( isWraparound )
  746.         {
  747.         if (isBackwards)
  748.             {
  749.             // go backwards until just after the start, or begining of
  750.             // document is start is the end
  751.             if (start == GetHandleSize(h))
  752.                 endPtr = *h;
  753.             else
  754.                 endPtr = startPtr + 1;
  755.             }
  756.         else
  757.             {
  758.             // go forwards until just before the start, or to the end
  759.             // of the document is the start is already the begining
  760.             if (start == 0)
  761.                 endPtr = *h + GetHandleSize(h);
  762.             else
  763.                 endPtr = startPtr - 1;
  764.             }
  765.         }
  766.     else
  767.         {
  768.         if (isBackwards)
  769.             {
  770.             // go back until hit begining of document
  771.             endPtr = *h-1;    
  772.             }
  773.         else
  774.             {
  775.             // go forward until hit end of document
  776.             endPtr = *h + GetHandleSize(h);
  777.             }
  778.         }
  779.         
  780.     searchPtr = startPtr;
  781.     while (searchPtr != endPtr)
  782.         {
  783.         if (IsThisTheString(searchPtr, searchString, isCaseSensitive))
  784.             {
  785.             foundIt = true;
  786.             *pNewStart = searchPtr - *h;
  787.             *pNewEnd = *pNewStart + searchString[0];
  788.             break;
  789.             }
  790.             
  791.         if (isBackwards)
  792.             --searchPtr;
  793.         else
  794.             ++searchPtr;
  795.             
  796.         if (isWraparound)
  797.             {
  798.             if (searchPtr < *h)
  799.                 searchPtr = *h + GetHandleSize(h);
  800.             if (searchPtr > *h + GetHandleSize(h))
  801.                 searchPtr = *h;
  802.             }
  803.         }
  804.         
  805.     HSetState(h, flags);
  806.     
  807.     return(foundIt);
  808.     
  809. } // PerformSearch
  810.  
  811. // --------------------------------------------------------------------------------------------------------------
  812. // SELECTION UTILITY ROUTINES
  813. // --------------------------------------------------------------------------------------------------------------
  814. void DrawSelection(WindowDataPtr pData, Rect *pSelection, short * pPhase, Boolean bumpPhase)
  815. {
  816.     if    (!EmptyRect(pSelection) ) 
  817.         {
  818.         RgnHandle    oldClip = NewRgn();
  819.         Pattern        aPattern;
  820.         Rect        newClip;
  821.  
  822.         
  823.         if     ( 
  824.             (bumpPhase) && 
  825.             (MOVESELECTION(TickCount()) ) 
  826.             )
  827.             {
  828.             if ((++(*pPhase)) > 7 )
  829.                 *pPhase = 1;
  830.             }
  831.             
  832.         // setup for drawing in this window
  833.         SetPort((GrafPtr) pData);
  834.         GetClip(oldClip);
  835.         PenMode(notPatXor);
  836.         
  837.         // offset the draw area (SetOrigin a must to preserve pattern appearence)
  838.         // and the clip area to avoid stepping on the scroll bars
  839.         SetOrigin(GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  840.         newClip = pData->contentRect;
  841.         OffsetRect(&newClip, GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  842.         ClipRect(&newClip);
  843.         
  844.         // do the draw
  845.         GetIndPattern(&aPattern, kPatternListID, (*pPhase)+1);
  846.         PenPat(&aPattern);
  847.         FrameRect(pSelection);
  848.         SetOrigin(0, 0);
  849.         
  850.         // restore the old port settings
  851.         SetClip(oldClip);
  852.         DisposeRgn(oldClip);
  853.         PenNormal();
  854.  
  855.         }
  856.  
  857. } // DrawSelection
  858.  
  859. // --------------------------------------------------------------------------------------------------------------
  860. OSErr SelectContents(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Rect *pSelection, Rect *pContent, short *pPhase)
  861. {
  862.  
  863.     OSErr            anErr = noErr;
  864.     Point            clickPoint = pEvent->where;
  865.     Point            currentPoint;
  866.     Boolean         didJustScroll;
  867.     ControlRef        theControl;
  868.     
  869.     GlobalToLocal(&clickPoint);
  870.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  871.         {
  872.     
  873.         // move the click point into the proper range
  874.         clickPoint.h += GetControlValue(pData->hScroll);
  875.         clickPoint.v += GetControlValue(pData->vScroll);
  876.         
  877.         // if the shift key is held down then the selection starts from
  878.         // a preexisting point such that we are doing an expand/contract
  879.         // of the original selection
  880.         if (pEvent->modifiers & shiftKey)
  881.             {
  882.             if (clickPoint.h < pSelection->right)
  883.                 clickPoint.h = pSelection->right;
  884.             else
  885.                 clickPoint.h = pSelection->left;
  886.  
  887.             if (clickPoint.v < pSelection->bottom)
  888.                 clickPoint.v = pSelection->bottom;
  889.             else
  890.                 clickPoint.v = pSelection->top;
  891.             }
  892.                         
  893.         while (StillDown())
  894.             {                    
  895.             // get the current mouse 
  896.             GetMouse(¤tPoint);
  897.             
  898.             didJustScroll = false;
  899.             // scroll contents if needed
  900.             {
  901.             short    deltaH = 0;
  902.             short    deltaV = 0;
  903.             
  904.             if (currentPoint.h < 0)
  905.                 deltaH = pData->hScrollAmount;
  906.             if (currentPoint.h > qd.thePort->portRect.right)
  907.                 deltaH = -pData->hScrollAmount;
  908.             if (currentPoint.v < 0)
  909.                 deltaV = pData->vScrollAmount;
  910.             if (currentPoint.v > qd.thePort->portRect.bottom)
  911.                 deltaV = -pData->vScrollAmount;
  912.                 
  913.             if ( (deltaH != 0) || (deltaV != 0) )
  914.                 {                
  915.                 if (deltaH)
  916.                     SetControlAndClipAmount(pData->hScroll, &deltaH);
  917.                 if (deltaV)
  918.                     SetControlAndClipAmount(pData->vScroll, &deltaV);
  919.  
  920.                 DoScrollContent(pWindow, pData, deltaH, deltaV);
  921.                 
  922.                 didJustScroll = true;
  923.                 }
  924.             }
  925.             
  926.             // map mouse into proper range
  927.             currentPoint.h += GetControlValue(pData->hScroll);
  928.             currentPoint.v += GetControlValue(pData->vScroll);
  929.     
  930.             // clip to the document size
  931.             if (currentPoint.h < 0)
  932.                 currentPoint.h = 0;
  933.             if (currentPoint.v < 0)
  934.                 currentPoint.v = 0;
  935.             if (currentPoint.h > pContent->right)
  936.                 currentPoint.h = pContent->right;
  937.             if (currentPoint.v > pContent->bottom)
  938.                 currentPoint.v = pContent->bottom;
  939.                 
  940.             // draw the new selection if it is time or we are about to 
  941.             // exit this loop
  942.             if ((MOVESELECTION(TickCount())) || (!Button()) || (didJustScroll) )
  943.                 {
  944.                 // first, erase any old selection we might have had
  945.                 DrawSelection(pData, pSelection, pPhase, false);
  946.  
  947.                 // make a rectangle out of the two points
  948.                 pSelection->left     = Min(currentPoint.h, clickPoint.h);
  949.                 pSelection->right     = Max(currentPoint.h, clickPoint.h);
  950.                 pSelection->top     = Min(currentPoint.v, clickPoint.v);
  951.                 pSelection->bottom     = Max(currentPoint.v, clickPoint.v);
  952.     
  953.                 // draw the new selection
  954.                 DrawSelection(pData, pSelection, pPhase, true);
  955.                 }
  956.             }
  957.         
  958.         // we handled the selection
  959.         anErr = eActionAlreadyHandled;
  960.         }
  961.         
  962.     return(anErr);
  963.     
  964. } // SelectContents
  965.  
  966. // --------------------------------------------------------------------------------------------------------------
  967. void DragAndDropArea(WindowRef pWindow, WindowDataPtr pData, EventRecord* event, Rect *pFrameRect)
  968. {
  969.     RgnHandle        hilightRgn;
  970.     Rect            r;
  971.     DragReference    theDrag;
  972.     OSErr            anErr = noErr;
  973.     
  974.     if (NewDrag(&theDrag) == noErr)
  975.         {
  976.         if (pData->pDragAddFlavors)
  977.             anErr = (*(pData->pDragAddFlavors)) (pWindow, pData, theDrag);
  978.         
  979.         if (anErr == noErr)
  980.             {
  981.             Rect    globalRect = *pFrameRect;
  982.             
  983.             hilightRgn = NewRgn();    
  984.             LocalToGlobal(&TopLeft(globalRect));
  985.             LocalToGlobal(&BotRight(globalRect));
  986.             RectRgn(hilightRgn, &globalRect);
  987.             SetDragItemBounds(theDrag, 1, &r);
  988.     
  989.             // turn the region from a fill into a frame
  990.             {    
  991.                 RgnHandle tempRgn = NewRgn();
  992.     
  993.                 CopyRgn(hilightRgn, tempRgn);
  994.                 InsetRgn(tempRgn, 1, 1);
  995.                 DiffRgn(hilightRgn, tempRgn, hilightRgn);
  996.                 DisposeRgn(tempRgn);
  997.             }
  998.             
  999.             TrackDrag(theDrag, event, hilightRgn);
  1000.             DisposeDrag(theDrag);
  1001.             DisposeRgn(hilightRgn);
  1002.             }
  1003.         }
  1004.  
  1005. } // DragAndDropArea
  1006.  
  1007. // --------------------------------------------------------------------------------------------------------------
  1008. // WINDOW UTILITY ROUTINES
  1009. // --------------------------------------------------------------------------------------------------------------
  1010. #pragma segment Main
  1011.  
  1012. static void CalculateGrowIcon(WindowDataPtr pData, Rect * location)
  1013. {
  1014.     if (pData->vScroll)
  1015.         location->top = (**pData->vScroll).contrlRect.bottom;
  1016.     else
  1017.         {
  1018.         if (pData->hScroll)
  1019.             location->top = (**pData->hScroll).contrlRect.top;
  1020.         else
  1021.             location->top = pData->theWindow.port.portRect.bottom - 15;
  1022.         }
  1023.         
  1024.     if (pData->hScroll)
  1025.         location->left = (**pData->hScroll).contrlRect.right;
  1026.     else
  1027.         {
  1028.         if (pData->vScroll)
  1029.             location->left = (**pData->vScroll).contrlRect.left;
  1030.         else
  1031.             location->left = pData->theWindow.port.portRect.right - 15;
  1032.         }
  1033.         
  1034.     location->right = location->left + 16;
  1035.     location->bottom = location->top + 16;
  1036.     
  1037. } // CalculateGrowIcon
  1038.  
  1039. // --------------------------------------------------------------------------------------------------------------
  1040. #pragma segment Main
  1041.  
  1042. OSErr    AdjustScrollBars(WindowRef pWindow,
  1043.     Boolean moveControls,                 // might the controls have moved?
  1044.     Boolean didResize,                     // did we just resize the window?
  1045.     Boolean *needInvalidate)            // does the caller need to invalidate contents as a result?
  1046. {
  1047.     OSErr            anErr = noErr;
  1048.     LongRect        docRect;
  1049.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  1050.     Rect            growIconRect;
  1051.     
  1052.     if (needInvalidate)
  1053.         *needInvalidate = false;
  1054.  
  1055.     if (pData)
  1056.         {
  1057.         short    oldHMax, oldVMax;
  1058.         short    oldHValue, oldVValue;
  1059.         
  1060.         // cache current values, we'll force an update if we needed to change em!
  1061.         if (pData->hScroll)
  1062.             {
  1063.             oldHMax = GetControlMaximum(pData->hScroll);
  1064.             oldHValue = GetControlValue(pData->hScroll);
  1065.             }
  1066.         if (pData->vScroll)
  1067.             {
  1068.             oldVMax = GetControlMaximum(pData->vScroll);
  1069.             oldVValue = GetControlValue(pData->vScroll);
  1070.             }
  1071.             
  1072.         // if we have a grow box but not all controls we have to invalidate the grow bar areas
  1073.         // by caclulating them
  1074.         if ( (didResize) && (pData->hasGrow) )
  1075.             {
  1076.             // if we regrow without any scroll bars, we need to update the content area
  1077.             if ( (needInvalidate) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  1078.                 *needInvalidate = true;
  1079.             
  1080.             // invalidate old grow bar areas
  1081.             if (pData->vScroll == nil)
  1082.                 {
  1083.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1084.                 growIconRect.left = pData->contentRect.right;
  1085.                 InvalRect(&growIconRect);
  1086.                 }
  1087.             if (pData->hScroll == nil)
  1088.                 {
  1089.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1090.                 growIconRect.top = pData->contentRect.bottom;
  1091.                 InvalRect(&growIconRect);
  1092.                 }
  1093.             
  1094.             // invalidate new grow bar areas
  1095.             if (pData->vScroll == nil)
  1096.                 {
  1097.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1098.                 growIconRect.left = growIconRect.right - kScrollBarSize;
  1099.                 InvalRect(&growIconRect);
  1100.                 }
  1101.             if (pData->hScroll == nil)
  1102.                 {
  1103.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1104.                 growIconRect.top = growIconRect.bottom - kScrollBarSize;
  1105.                 InvalRect(&growIconRect);
  1106.                 }
  1107.             }
  1108.             
  1109.         // if the controls need moving, recalculate the visible area
  1110.         if (moveControls)
  1111.             {
  1112.             pData->contentRect = GetWindowPort(pWindow)->portRect;
  1113.             if ((pData->hScroll) || (pData->hasGrow) )
  1114.                 pData->contentRect.bottom -= kScrollBarSize;
  1115.             if ((pData->vScroll) || (pData->hasGrow) )
  1116.                 pData->contentRect.right -= kScrollBarSize;
  1117.             }
  1118.             
  1119.         // before doing anything, make the controls invisible
  1120.         if (pData->hScroll)
  1121.             (**pData->hScroll).contrlVis = 0;    
  1122.         if (pData->vScroll)
  1123.             (**pData->vScroll).contrlVis = 0;
  1124.  
  1125.         // based on document and visiable area, adjust possible control values
  1126.         if ( (pData->pGetDocumentRect) && ((pData->hScroll) || (pData->vScroll)) )
  1127.             {
  1128.             // let the object calc the size and content if it wishes to
  1129.             anErr = (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, false);
  1130.             if (anErr == noErr)
  1131.                 {
  1132.                 short    amountOver;
  1133.                 short    newMax;
  1134.                 
  1135.                 amountOver = (docRect.right - docRect.left) - (pData->contentRect.right - pData->contentRect.left);
  1136.                 if     (
  1137.                     (pData->hScroll) &&
  1138.                     (amountOver > 0)
  1139.                     )
  1140.                     newMax = amountOver;
  1141.                 else
  1142.                     newMax = 0;
  1143.     
  1144.                 if (pData->hScroll)
  1145.                     {
  1146.                     if (GetControlValue(pData->hScroll) > newMax)
  1147.                         {
  1148.                         if (needInvalidate)
  1149.                             *needInvalidate = true;
  1150.                         }
  1151.                     SetControlMaximum(pData->hScroll, newMax);
  1152.                     }
  1153.                 
  1154.                 amountOver = (docRect.bottom - docRect.top) - (pData->contentRect.bottom - pData->contentRect.top);
  1155.                 if     (
  1156.                     (pData->vScroll) &&
  1157.                     (amountOver > 0)
  1158.                     )
  1159.                     newMax = amountOver;
  1160.                 else
  1161.                     newMax = 0;
  1162.                     
  1163.                 if (pData->vScroll)
  1164.                     {
  1165.                     if (GetControlValue(pData->vScroll) > newMax)
  1166.                         {
  1167.                         if (needInvalidate)
  1168.                             *needInvalidate = true;
  1169.                         }
  1170.                     SetControlMaximum(pData->vScroll, newMax);
  1171.                     }
  1172.                 }
  1173.             }
  1174.             
  1175.         // then, if the controls need moving, we move them and inval the old
  1176.         // and new locations
  1177.         if (moveControls)
  1178.             {
  1179.             // if we have grow box we invalidate the old grow location
  1180.             if ( pData->hasGrow) 
  1181.                 {
  1182.                 CalculateGrowIcon(pData, &growIconRect);
  1183.                 InvalRect(&growIconRect);
  1184.                 }
  1185.                 
  1186.             if (pData->hScroll)
  1187.                 {
  1188.                 short    widthAdjust;
  1189.                 
  1190.                 if ((pData->vScroll) || (pData->hasGrow))
  1191.                     widthAdjust = -kGrowScrollAdjust;
  1192.                 else
  1193.                     widthAdjust = -1;
  1194.                     
  1195.                 growIconRect = (**pData->hScroll).contrlRect;
  1196.                 InvalRect(&growIconRect);
  1197.                 
  1198.                 MoveControl(pData->hScroll, pData->hScrollOffset-1, GetWindowPort(pWindow)->portRect.bottom - kScrollBarSize);
  1199.                 SizeControl(pData->hScroll, (GetWindowPort(pWindow)->portRect.right - 
  1200.                             GetWindowPort(pWindow)->portRect.left) + widthAdjust - pData->hScrollOffset,
  1201.                             16);
  1202.  
  1203.                 growIconRect = (**pData->hScroll).contrlRect;
  1204.                 InvalRect(&growIconRect);
  1205.                 }
  1206.  
  1207.             if (pData->vScroll)
  1208.                 {
  1209.                 short    heightAdjust;
  1210.                 
  1211.                 if ((pData->hScroll) || (pData->hasGrow))
  1212.                     heightAdjust = -kGrowScrollAdjust;
  1213.                 else
  1214.                     heightAdjust = -1;
  1215.                     
  1216.                 growIconRect = (**pData->vScroll).contrlRect;
  1217.                 InvalRect(&growIconRect);
  1218.  
  1219.                 MoveControl(pData->vScroll, GetWindowPort(pWindow)->portRect.right - kScrollBarSize, pData->vScrollOffset-1);
  1220.                 SizeControl(pData->vScroll, 16,
  1221.                             (GetWindowPort(pWindow)->portRect.bottom - 
  1222.                             GetWindowPort(pWindow)->portRect.top) + heightAdjust - pData->vScrollOffset);
  1223.                 growIconRect = (**pData->vScroll).contrlRect;
  1224.                 InvalRect(&growIconRect);
  1225.                 }
  1226.                 
  1227.             // if we have scroll bars, update the grow icon
  1228.             if ( pData->hasGrow )
  1229.                 {
  1230.                 CalculateGrowIcon(pData, &growIconRect);
  1231.                 InvalRect(&growIconRect);
  1232.                 }
  1233.             
  1234.             }
  1235.  
  1236.         // let the document adjust anything it needs to
  1237.         if (pData->pAdjustSize)
  1238.             anErr = (*(pData->pAdjustSize)) (pWindow, pData, &didResize);
  1239.             
  1240.         if ((didResize) && (needInvalidate))
  1241.             *needInvalidate = true;
  1242.  
  1243.  
  1244.         if ( ((WindowPeek) pWindow)->hilited )
  1245.             {
  1246.             // after doing something, make the controls visible
  1247.             if (pData->hScroll)
  1248.                 {
  1249.                 if ((oldHMax != GetControlMaximum(pData->hScroll)) || (oldHValue != GetControlValue(pData->hScroll)) )
  1250.                     ShowControl(pData->hScroll);
  1251.                 else
  1252.                     (**pData->hScroll).contrlVis = 0xFF;    
  1253.                 }
  1254.             if (pData->vScroll)
  1255.                 {
  1256.                 if ((oldVMax != GetControlMaximum(pData->vScroll)) || (oldVValue != GetControlValue(pData->vScroll)) )
  1257.                     ShowControl(pData->vScroll);
  1258.                 else
  1259.                     (**pData->vScroll).contrlVis = 0xFF;
  1260.                 }
  1261.             }
  1262.  
  1263.         }
  1264.         
  1265.     return anErr;
  1266.     
  1267. } // AdjustScrollBars
  1268.  
  1269. // --------------------------------------------------------------------------------------------------------------
  1270. // MENU UTILITY ROUTINES
  1271. // --------------------------------------------------------------------------------------------------------------
  1272. #pragma segment Main
  1273.  
  1274. Boolean CommandToIDs(short commandID, short * menuID, short *itemID)
  1275. {
  1276.  
  1277.     short    ** commandHandle;
  1278.     short    whichMenu;
  1279.     short    oldResFile = CurResFile();
  1280.     Boolean    returnValue = false;
  1281.     
  1282.     UseResFile(gApplicationResFile);
  1283.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1284.         {
  1285.         commandHandle = (short**) Get1Resource('MCMD', whichMenu);
  1286.         if (commandHandle)
  1287.             {
  1288.             short    * pCommands = *commandHandle;
  1289.             short    commandIndex;
  1290.             short    numCommands = pCommands[0];
  1291.             
  1292.             for (commandIndex = 1; commandIndex <= numCommands; ++commandIndex)
  1293.                 if (pCommands[commandIndex] == commandID)
  1294.                     {
  1295.                     *menuID = whichMenu;
  1296.                     *itemID = commandIndex;
  1297.                     
  1298.                     returnValue = (commandIndex == numCommands);
  1299.                     }
  1300.             }    
  1301.         }
  1302.         
  1303.     UseResFile(oldResFile);
  1304.     
  1305.     return returnValue;
  1306.     
  1307. } // CommandToIDs
  1308.  
  1309. // --------------------------------------------------------------------------------------------------------------
  1310. #pragma segment Main
  1311.  
  1312. Boolean IsCommandEnabled(short commandID)
  1313. /*
  1314.     returns true if a given command is currently enabled
  1315. */
  1316. {
  1317.     short        whichMenu, whichItem;
  1318.     MenuHandle    menu;
  1319.     
  1320.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1321.     menu = GetMenuHandle(whichMenu);
  1322.     
  1323.     if ((**menu).enableFlags & (1 << whichItem))
  1324.         return(true);
  1325.     
  1326.     return(false);
  1327.     
  1328. } // IsCommandEnabled
  1329.  
  1330. // --------------------------------------------------------------------------------------------------------------
  1331. #pragma segment Main
  1332.  
  1333. void EnableCommand(short commandID)
  1334. /*
  1335.     Given a command ID, enables the first menu item with that command ID.
  1336.     
  1337.     If the command table for a given menu is less than the number of items in the menu,
  1338.     and the command being enabled is the last item in the command table, then all
  1339.     items from there on down are also enabled.  This is useful for menus that get
  1340.     appended to, such as the desk accessory list, font list, or speaking voices list.
  1341. */
  1342. {
  1343.     short    whichMenu;
  1344.     short    whichItem;
  1345.     
  1346.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1347.         {
  1348.         short        i;
  1349.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1350.         
  1351.         if (menu)
  1352.             {
  1353.             short        numItems = CountMItems(menu);
  1354.             
  1355.             for (i = whichItem; i <= numItems; ++i)
  1356.                 EnableItem(menu, i);
  1357.             }
  1358.         }
  1359.     else
  1360.         {
  1361.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1362.  
  1363.         if (menu)
  1364.             EnableItem(menu, whichItem);
  1365.         }
  1366.         
  1367. } // EnableCommand
  1368.  
  1369. // --------------------------------------------------------------------------------------------------------------
  1370. #pragma segment Main
  1371.  
  1372. void ChangeCommandName(short commandID, short resourceID, short resourceIndex)
  1373. {
  1374.     short        whichMenu;
  1375.     short        whichItem;
  1376.     MenuHandle    menu;
  1377.     
  1378.     // figure out how this command maps into the menu bar
  1379.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1380.     menu = GetMenuHandle(whichMenu);
  1381.     
  1382.     // then make this item into the requested new string
  1383.     {
  1384.     Str255        theString;
  1385.     
  1386.     GetIndString(theString, resourceID, resourceIndex);
  1387.     SetMenuItemText(menu, whichItem, theString);
  1388.     }
  1389.     
  1390. } // ChangeCommandName
  1391.  
  1392. // --------------------------------------------------------------------------------------------------------------
  1393. #pragma segment Main
  1394.  
  1395. void EnableCommandCheck(short commandID, Boolean check)
  1396. {
  1397.  
  1398.     short    whichMenu;
  1399.     short    whichItem;
  1400.     
  1401.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1402.         {
  1403.         short        i;
  1404.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1405.         short        numItems = CountMItems(menu);
  1406.         
  1407.         for (i = whichItem; i <= numItems; ++i)
  1408.             {
  1409.             EnableItem(menu, i);
  1410.             CheckItem(menu, i, check);
  1411.             }
  1412.         }
  1413.     else
  1414.         {
  1415.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1416.  
  1417.         EnableItem(menu, whichItem);
  1418.         CheckItem(menu, whichItem, check);
  1419.         }
  1420.         
  1421. } // EnableCommandCheck
  1422.  
  1423.  
  1424. // --------------------------------------------------------------------------------------------------------------
  1425. #pragma segment Main
  1426.  
  1427. void EnableCommandCheckStyle(short commandID, Boolean check, short style)
  1428. {
  1429.  
  1430.     short    whichMenu;
  1431.     short    whichItem;
  1432.     
  1433.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1434.         {
  1435.         short        i;
  1436.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1437.         short        numItems = CountMItems(menu);
  1438.         
  1439.         for (i = whichItem; i <= numItems; ++i)
  1440.             {
  1441.             EnableItem(menu, i);
  1442.             CheckItem(menu, i, check);
  1443.             SetItemStyle(menu, i, style);
  1444.             }
  1445.         }
  1446.     else
  1447.         {
  1448.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1449.  
  1450.         EnableItem(menu, whichItem);
  1451.         CheckItem(menu, whichItem, check);
  1452.         SetItemStyle(menu, whichItem, style);
  1453.         }
  1454.         
  1455. } // EnableCommandCheckStyle
  1456.  
  1457. // --------------------------------------------------------------------------------------------------------------
  1458. #pragma segment Main
  1459.  
  1460. void AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn)
  1461. {
  1462.     Boolean                 wasEnabled[mNumberMenus];    // Old state of menus
  1463.     short                    whichMenu;                    // for stepping through menus
  1464.     MenuHandle                menu;                        // for reading in menu IDs
  1465.     WindowDataPtr             pData = GetWindowInfo(pWindow);
  1466.     
  1467.     // Step through all of the menus 
  1468.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1469.         {
  1470.         // Save the old state of the menu title 
  1471.         menu = GetMenuHandle(whichMenu);
  1472.         if (menu)                                // because contents menu may not be around
  1473.             {
  1474.             if (forceTitlesOn)                
  1475.                 wasEnabled[mLastMenu - whichMenu] = false;
  1476.             else
  1477.                 wasEnabled[mLastMenu - whichMenu] = (((**menu).enableFlags && 1) == 1);
  1478.             
  1479.             // Disable the entire menu 
  1480.             (**menu).enableFlags = 0;        
  1481.             }
  1482.         }
  1483.     
  1484.     // select all, unless someone else changes it
  1485.     ChangeCommandName(cSelectAll, kMiscStrings, iSelectAllCommand);
  1486.  
  1487.     // if we have NO windows, or the current window is one we understand
  1488.     if ((pWindow == nil) || (pData))
  1489.         {
  1490.         // enable the default commands
  1491.         EnableCommand(cAbout);
  1492.         EnableCommand(cDeskAccessory);
  1493.         
  1494.         EnableCommand(cNew);
  1495.         EnableCommand(cOpen);
  1496.         EnableCommand(cQuit);
  1497.     
  1498.         EnableCommand(cShowClipboard);
  1499.         }
  1500.     else
  1501.         {
  1502.         // it's printing or a dialog, so enable cut/copy/paste
  1503.         if (editDialogs)
  1504.             {
  1505.             EnableCommand(cCut);
  1506.             EnableCommand(cCopy);
  1507.             EnableCommand(cPaste);
  1508.             EnableCommand(cClear);
  1509.             }
  1510.         
  1511.         // and desk accs too!        
  1512.         EnableCommand(cDeskAccessory);
  1513.  
  1514.         }
  1515.         
  1516.     if ( (pWindow) && (pData) )
  1517.         {
  1518.         // all windows can be closed
  1519.         if (FrontWindow())
  1520.             EnableCommand(cClose);
  1521.  
  1522.         // changed documents can be saved, but only if the file is open for write
  1523.         if (     (pData->changed) && 
  1524.                 ((pData->isWritable) || (pData->dataRefNum == -1)) )
  1525.             EnableCommand(cSave);
  1526.         
  1527.         // objects with a print method can be printed and page setup-ed
  1528.         if (pData->pPrintPage)
  1529.             {
  1530.             EnableCommand(cPrint);
  1531.             EnableCommand(cPageSetup);
  1532.             EnableCommand(cPrintOneCopy);
  1533.             }
  1534.             
  1535.         // let object enable anything else that needs to be enabled
  1536.         if (pData->pAdjustMenus)
  1537.             (*(pData->pAdjustMenus)) (pWindow, pData);
  1538.         }
  1539.         
  1540.     // Now determine if any of the menus have changed state
  1541.     {
  1542.     Boolean gotToRedraw = false;
  1543.     
  1544.     for (whichMenu = mApple; whichMenu <= mLastMenu; ++whichMenu)
  1545.         {
  1546.         menu = GetMenuHandle(whichMenu);
  1547.     
  1548.         if (menu)        // because contents menu may not be around
  1549.             {
  1550.             // If any of the menu is enabled 
  1551.             if ((**menu).enableFlags != 0)
  1552.                 {
  1553.                 // Make sure to turn on the menu title 
  1554.                 (**menu).enableFlags |= 1;
  1555.                 }
  1556.                 
  1557.             /*     If this new state is different than the saved state, then the menu bar
  1558.                 will need to be redrawn */
  1559.             if (wasEnabled[mLastMenu - whichMenu] != ((**menu).enableFlags && 1))
  1560.                 {
  1561.                 gotToRedraw = true;
  1562.                 }
  1563.             }
  1564.         }
  1565.         
  1566.     // And if any titles have changed state, redraw them 
  1567.     if (gotToRedraw)
  1568.         DrawMenuBar();
  1569.     }
  1570.         
  1571. } // AdjustMenus
  1572.  
  1573. // --------------------------------------------------------------------------------------------------------------
  1574. // PRINTING UTILITY ROUTINES
  1575. // --------------------------------------------------------------------------------------------------------------
  1576. #pragma segment Printing
  1577.  
  1578. Boolean IsSomewhereInRectangle(gxRectangle *pContainer, gxRectangle *pShape)
  1579. /*
  1580.     Calculates this by saying the rectangle doesn't intersect at ALL,
  1581.     and then NOTs that expression.
  1582. */
  1583. {
  1584.     return
  1585.         (!(
  1586.         pShape->top > pContainer->bottom ||
  1587.         pShape->bottom < pContainer->top ||
  1588.         pShape->left > pContainer->right ||
  1589.         pShape->right < pContainer->left
  1590.         ));
  1591.         
  1592. } // IsSomewhereInRectangle
  1593.  
  1594. // --------------------------------------------------------------------------------------------------------------
  1595. #pragma segment Printing
  1596.     
  1597. static OSErr SimpleCatchShape(gxShape newShape, CatchRefCon * pRefCon)
  1598. {
  1599.     Boolean                addShape = false;
  1600.     gxGraphicsError        anErr;
  1601.  
  1602.     // did the user abort printing?
  1603.     anErr = GXGetJobError(pRefCon->theJob);
  1604.     if (anErr != noErr)
  1605.         return anErr;
  1606.         
  1607.     switch (GXGetShapeType(newShape))
  1608.         {
  1609.         
  1610.         // if we have a layout, turn off justification so that we can get
  1611.         // better looking morph effects, and also enable the default features
  1612.         // of the layout.  However, we can only do this if the layout is
  1613.         // in a script system where the translation wasn't done by a Print Action Hook.
  1614.         // For now, this means only smRoman styles.
  1615.         case gxLayoutType:
  1616.             if (pRefCon->doLayout)
  1617.                 {
  1618.                 Boolean            enableLayout = pRefCon->doLayout;
  1619.         
  1620.                 gxStyle            * theStyles;
  1621.                 long            styleCount, index;
  1622.                 
  1623.                 GXGetLayout(newShape, nil,
  1624.                             &styleCount, nil, nil,    // style runs
  1625.                             nil, nil, nil,    // run levels
  1626.                             nil, nil);
  1627.                 theStyles = (gxStyle*) NewPtr(sizeof(gxStyle)*styleCount);
  1628.                 if (theStyles)
  1629.                     {
  1630.                     GXGetLayout(newShape, nil,
  1631.                                 &styleCount, nil, theStyles,    // style runs
  1632.                                 nil, nil, nil,    // run levels
  1633.                                 nil, nil);
  1634.                     
  1635.                     enableLayout = true;
  1636.                     for (index = 0; index < styleCount; ++index)
  1637.                         {
  1638.                         gxFontScript theScript;
  1639.                         gxFontPlatform thePlatform = GXGetStyleEncoding(theStyles[index], &theScript, nil);
  1640.                         
  1641.                         if ((thePlatform != gxMacintoshPlatform) || (theScript != gxRomanScript))
  1642.                             enableLayout = false;
  1643.                         }
  1644.                             
  1645.                     if (enableLayout)
  1646.                         for (index = 0; index < styleCount; ++index)
  1647.                             {
  1648.                             gxRunControls theControls;
  1649.                             
  1650.                             // re-enable run control features
  1651.                             GXGetStyleRunControls(theStyles[index], &theControls);
  1652.                             theControls.flags = 0;
  1653.                             theControls.track = 0;
  1654.                             theControls.hangingInhibitFactor = 0;
  1655.                             theControls.kerningInhibitFactor = 0;
  1656.                             GXSetStyleRunControls(theStyles[index], &theControls);
  1657.                             
  1658.                             // and turn back on default features
  1659.                             GXSetStyleRunFeatures(theStyles[index], 0, nil);
  1660.                             }
  1661.                         
  1662.                     DisposePtr((Ptr) theStyles);
  1663.                     }
  1664.         
  1665.                 if ( (enableLayout) && (GetSysDirection() == 0) )
  1666.                     {
  1667.                     // turn off justification
  1668.                     {
  1669.                     gxLayoutOptions    layoutOptions;
  1670.             
  1671.                     // get the current layout options
  1672.                     layoutOptions.baselineRec = nil;
  1673.                     GXGetLayout(newShape, nil,
  1674.                                 nil, nil, nil,    // style runs
  1675.                                 nil, nil, nil,    // run levels
  1676.                                 &layoutOptions, nil);
  1677.                         
  1678.                     // setting width to zero allows lines to float, but causes multi-styled
  1679.                     // lines (separate layouts from the translation process) to run into
  1680.                     // one another, so we can't do that
  1681.                     //layoutOptions.width = 0;
  1682.                     layoutOptions.just = gxNoJustification;
  1683.                     GXSetLayout(newShape, 
  1684.                                 0, nil, nil,    // text runs
  1685.                                 0, nil, nil,    // style runs
  1686.                                 0, nil, nil, // run levels
  1687.                                 &layoutOptions, nil);
  1688.                     }
  1689.                     
  1690.                     // un-clip the shape left and right so we can see things like hanging puncs.
  1691.                         {
  1692.                         gxShape            newClip;
  1693.                         gxRectangle        bounds;
  1694.                         
  1695.                         newClip = GXGetShapeClip(newShape);
  1696.                         GXGetShapeLocalBounds(newClip, &bounds);
  1697.                         GXDisposeShape(newClip);
  1698.                         
  1699.                         bounds.left     = gxNegativeInfinity;
  1700.                         bounds.right     = gxPositiveInfinity;
  1701.                         newClip = GXNewRectangle(&bounds);
  1702.                         GXSetShapeClip(newShape, newClip);
  1703.                         GXDisposeShape(newClip);
  1704.                         }
  1705.                     }
  1706.                 
  1707.                 }
  1708.  
  1709.             // if we aren't forming layouts, we'll go ahead and check for this being on
  1710.             // the shape.  But for pure text, we'll just always add the text.  TextEdit
  1711.             // is pretty good about pre-clipping for us.
  1712.             if (!pRefCon->doLayout)
  1713.                 {
  1714.                 gxRectangle    bounds;
  1715.                 
  1716.                 GXGetShapeLocalBounds(newShape, &bounds);
  1717.                 if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1718.                     addShape = true;
  1719.                 }
  1720.             else
  1721.                 addShape = true;
  1722.             break;
  1723.         
  1724.         // never add these for text case
  1725.         case gxRectangleType:
  1726.             if (!pRefCon->doLayout)
  1727.                 addShape = true;
  1728.             break;
  1729.             
  1730.         // always add these shapes if we see any because we don't know how to filter them
  1731.         case gxEmptyType:
  1732.         case gxFullType:
  1733.         case gxPictureType:
  1734.             addShape = true;
  1735.             break;
  1736.             
  1737.         default:
  1738.             {
  1739.             gxRectangle    bounds;
  1740.             
  1741.             GXGetShapeLocalBounds(newShape, &bounds);
  1742.             if (IsSomewhereInRectangle(&pRefCon->thePageRectangle, &bounds))
  1743.                 addShape = true;
  1744.             }
  1745.             break;
  1746.             
  1747.         } // switch
  1748.         
  1749.     if (addShape)
  1750.         GXSetPictureParts(pRefCon->thePage, 0, 0, 1, &newShape, nil, nil, nil);    /* Add shape */
  1751.     
  1752.     GXGetGraphicsError(&anErr);
  1753.     
  1754.     if (anErr == noErr)
  1755.         {
  1756.         GXIdleJob(pRefCon->theJob);
  1757.         anErr = GXGetJobError(pRefCon->theJob);
  1758.         }
  1759.         
  1760.     return anErr;
  1761.     
  1762. } // SimpleCatchShape
  1763.  
  1764. #if GENERATINGCFM
  1765.     static RoutineDescriptor gSimpleCatchShapeRD = BUILD_ROUTINE_DESCRIPTOR(uppgxShapeSpoolProcInfo, SimpleCatchShape);
  1766.     static gxShapeSpoolUPP gSimpleCatchShape = &gSimpleCatchShapeRD;
  1767. #else
  1768.     static gxShapeSpoolUPP gSimpleCatchShape = NewgxShapeSpoolProc(SimpleCatchShape);
  1769. #endif
  1770.  
  1771. // --------------------------------------------------------------------------------------------------------------
  1772. #pragma segment Printing
  1773.  
  1774. static OSErr CompleteSpoolFileMessage( gxSpoolFile theSpoolFile )
  1775. {
  1776.     OSErr            anErr         = noErr;
  1777.     Handle            hIcon;
  1778.     short            sourceIcon     = 0;
  1779.     short            docIcon     = 132;
  1780.     WindowDataPtr    pData         = GXGetJobRefCon(GXGetJob());
  1781.     
  1782.     // for some file types, we can supply a nicer icon for the Finder to display
  1783.     // within the queue when this document is printing
  1784.     switch (pData->originalFileType)
  1785.         {
  1786.         case 'TEXT':
  1787.         case 'sEXT':
  1788.             sourceIcon = kTextIcon;
  1789.             break;
  1790.                         
  1791.         case 'ttro':
  1792.             sourceIcon = kReadOnlyIcon;
  1793.             break;
  1794.  
  1795.         case 'PICT':
  1796.             sourceIcon = kPICTIcon;
  1797.             break;
  1798.         }
  1799.         
  1800.     if (sourceIcon != 0)
  1801.         {
  1802.         hIcon = GetResource('ICN#', sourceIcon);
  1803.         if (hIcon != nil) 
  1804.             {
  1805.             HNoPurge(hIcon);
  1806.             DetachResource(hIcon);
  1807.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'ICN#', docIcon);
  1808.             }
  1809.         nrequire(anErr, SpoolOneBit);
  1810.         
  1811.         hIcon = GetResource('icl4', sourceIcon);
  1812.         if (hIcon != nil) 
  1813.             {
  1814.             HNoPurge(hIcon);
  1815.             DetachResource(hIcon);
  1816.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl4', docIcon);
  1817.             }
  1818.         nrequire(anErr, SpoolFourBit);
  1819.     
  1820.         hIcon = GetResource('icl8', sourceIcon);
  1821.         if (hIcon != nil) 
  1822.             {
  1823.             HNoPurge(hIcon);
  1824.             DetachResource(hIcon);
  1825.             anErr = Send_GXSpoolResource (theSpoolFile, hIcon, 'icl8', docIcon);
  1826.             }
  1827.         nrequire(anErr, SpoolEightBit);
  1828.         }
  1829.     
  1830.     anErr = Forward_GXCompleteSpoolFile( theSpoolFile );
  1831.     
  1832. // FALL THROUGH EXCEPTION HANDLING
  1833. SpoolOneBit:
  1834. SpoolFourBit:
  1835. SpoolEightBit:
  1836.  
  1837.     return anErr;
  1838.     
  1839. } // CompleteSpoolFileMessage
  1840.  
  1841. #if GENERATINGCFM
  1842.     static RoutineDescriptor gCompleteSpoolFileMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXCompleteSpoolFileProcInfo, CompleteSpoolFileMessage);
  1843.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = &gCompleteSpoolFileMessageRD;
  1844. #else
  1845.     static GXCompleteSpoolFileUPP gCompleteSpoolFileMessage = NewGXCompleteSpoolFileProc(CompleteSpoolFileMessage);
  1846. #endif
  1847.  
  1848. // --------------------------------------------------------------------------------------------------------------
  1849. #pragma segment Printing
  1850.  
  1851. static OSErr PrintingEventMessage(EventRecord *event, Boolean filter)
  1852. {
  1853.     OSErr     anErr = noErr;
  1854.     GrafPtr    curPort;
  1855.     
  1856.     GetPort(&curPort);
  1857.     if (filter == false) 
  1858.         {
  1859.         switch ( event->what ) 
  1860.             {
  1861.             case mouseDown:
  1862.             case keyDown:
  1863.             case autoKey:
  1864.                 break;
  1865.                 
  1866.             case activateEvt:
  1867.             case updateEvt:
  1868.             default:
  1869.                 HandleEvent(event);
  1870.                 break;
  1871.             }
  1872.         }
  1873.     
  1874.     anErr = Forward_GXPrintingEvent(event, filter);
  1875.     SetPort(curPort);
  1876.     
  1877.     return anErr;
  1878.     
  1879. } // PrintingEventMessage
  1880.  
  1881. #if GENERATINGCFM
  1882.     static RoutineDescriptor gPrintingEventMessageRD = BUILD_ROUTINE_DESCRIPTOR(uppGXPrintingEventProcInfo, PrintingEventMessage);
  1883.     static GXPrintingEventUPP gPrintingEventMessage = &gPrintingEventMessageRD;
  1884. #else
  1885.     static GXPrintingEventUPP gPrintingEventMessage = NewGXPrintingEventProc(PrintingEventMessage);
  1886. #endif
  1887.  
  1888.  
  1889.  
  1890. // --------------------------------------------------------------------------------------------------------------
  1891. // FILE UTILITY ROUTINES
  1892. // --------------------------------------------------------------------------------------------------------------
  1893. #pragma segment Main
  1894.  
  1895. static Boolean BringToFrontIfOpen(FSSpecPtr pSpec)
  1896. {
  1897.     WindowRef        pWindow;
  1898.     
  1899.     pWindow = FrontWindow();
  1900.     while (pWindow)
  1901.         {
  1902.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1903.         
  1904.         if (
  1905.             (pData) &&
  1906.             (pData->fileSpec.vRefNum == pSpec->vRefNum) &&
  1907.             (pData->fileSpec.parID == pSpec->parID) &&
  1908.             EqualString(pData->fileSpec.name, pSpec->name, false, false)
  1909.             )
  1910.             {
  1911.             SelectWindow(pWindow);
  1912.             return true;
  1913.             }
  1914.             
  1915.         pWindow = GetNextWindow(pWindow);
  1916.         }
  1917.         
  1918.     return false;
  1919.     
  1920. } // BringToFrontIfOpen
  1921.  
  1922. // --------------------------------------------------------------------------------------------------------------
  1923. #pragma segment Main
  1924.  
  1925. static Boolean BringToFrontIfExists(ResType windowKind)
  1926. {
  1927.     WindowRef        pWindow;
  1928.     
  1929.     pWindow = FrontWindow();
  1930.     while (pWindow)
  1931.         {
  1932.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1933.         
  1934.         if ((pData) && (pData->windowKind == windowKind))
  1935.             {
  1936.             SelectWindow(pWindow);
  1937.             return true;
  1938.             }
  1939.             
  1940.         pWindow = GetNextWindow(pWindow);
  1941.         }
  1942.         
  1943.     return false;
  1944.     
  1945. } // BringToFrontIfExists
  1946.  
  1947. // --------------------------------------------------------------------------------------------------------------
  1948. // MAIN SIMPLETEXT ROUTINES
  1949. // --------------------------------------------------------------------------------------------------------------
  1950. #pragma segment Main
  1951.  
  1952. static OSErr MakeNewWindow(ResType windowKind, FSSpecPtr fileSpec, OSType fileType, Boolean *pWasAlreadyOpen)
  1953. {
  1954.     OSErr                anErr = fnfErr;
  1955.     PreflightRecord        thePreflight;
  1956.     PreflightWindowProc    pPreflight = nil;
  1957.     WindowRef            pWindow;
  1958.     WindowDataPtr        pData;
  1959.     
  1960.     // require a certain amount of RAM free before we allow the new window to be created
  1961.     if (FreeMem() < kRAMNeededForNew)
  1962.         anErr = memFullErr;
  1963.         
  1964.     // <50> if we already have a document open from this file, bring the window to the
  1965.     // front and return with no error
  1966.     if ( (fileSpec) && (fileType != 'sEXT') && (BringToFrontIfOpen(fileSpec)) )
  1967.         {
  1968.         if (pWasAlreadyOpen)
  1969.             *pWasAlreadyOpen = true;
  1970.         anErr = noErr;
  1971.         return(anErr);
  1972.         }
  1973.     if (pWasAlreadyOpen)
  1974.         *pWasAlreadyOpen = false;
  1975.     if (anErr != fnfErr)
  1976.         {
  1977.         nrequire(anErr, SanityCheckFailed);
  1978.         }
  1979.         
  1980.     // initialize our behavior
  1981.     thePreflight.continueWithOpen     = true;
  1982.     thePreflight.resourceID         = kDefaultWindowID;
  1983.     thePreflight.wantHScroll         = false;
  1984.     thePreflight.wantVScroll         = false;
  1985.     thePreflight.storageSize         = sizeof(WindowDataRecord);
  1986.     thePreflight.makeProcPtr         = nil;
  1987.     thePreflight.openKind            = fsRdPerm;
  1988.     thePreflight.needResFork        = false;
  1989.     thePreflight.doZoom                = false;
  1990.     thePreflight.fileType            = fileType;
  1991.     
  1992.     switch (windowKind)
  1993.         {
  1994.         case kAboutWindow:
  1995.             pPreflight = AboutPreflightWindow;
  1996.             break;
  1997.  
  1998.         case kPICTWindow:
  1999.             pPreflight = PICTPreflightWindow;
  2000.             break;
  2001.  
  2002.         case kMovieWindow:
  2003.             pPreflight = MoviePreflightWindow;
  2004.             break;
  2005.  
  2006.         case kClipboardWindow:
  2007.             pPreflight = ClipboardPreflightWindow;
  2008.             break;
  2009.  
  2010.         case kTextWindow:
  2011.             pPreflight = TextPreflightWindow;
  2012.             break;
  2013.  
  2014.         case kGXWindow:
  2015.             pPreflight = GXPreflightWindow;
  2016.             break;
  2017.  
  2018.         case kThreeDWindow:
  2019.             pPreflight = ThreeDPreflightWindow;
  2020.             break;
  2021.         }
  2022.     
  2023.     // preflight the window    
  2024.     if (pPreflight)
  2025.         anErr = (*pPreflight) (&thePreflight);
  2026.     nrequire(anErr, PreflightFailed);
  2027.     
  2028.     if (thePreflight.continueWithOpen)
  2029.         {
  2030.         // allocate a place for the window
  2031.         pData = (WindowDataPtr)NewPtrClear(thePreflight.storageSize);
  2032.         anErr = MemError();
  2033.         nrequire(anErr, FailedToAllocateWindow);
  2034.         
  2035.         // then actually create the window
  2036.         if (gMachineInfo.theEnvirons.hasColorQD)
  2037.             pWindow = (WindowRef)GetNewCWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2038.         else
  2039.             pWindow = (WindowRef)GetNewWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  2040.         if (!pWindow) anErr = memFullErr;
  2041.         nrequire(anErr, NewWindowFailed);
  2042.         SetWRefCon(pWindow, (long) pData);
  2043.                 
  2044.         // zoom the rectangle to big size on this monitor 
  2045.         // based upon which scroll bars they want
  2046.         {
  2047.         Rect    rect = GetWindowPort(pWindow)->portRect;
  2048.         Rect    bigRect;
  2049.         
  2050.         if (gMachineInfo.theEnvirons.hasColorQD)
  2051.             bigRect = (**GetMainDevice()).gdRect;
  2052.         else
  2053.             bigRect = qd.screenBits.bounds;
  2054.         bigRect.top += GetMBarHeight() * 2;
  2055.         bigRect.left += 4;
  2056.         bigRect.bottom -= 4;
  2057.         bigRect.right -= 65;
  2058.         
  2059.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2060.         LocalToGlobal(&TopLeft(rect));
  2061.         LocalToGlobal(&BotRight(rect));
  2062.         
  2063.         if ( (thePreflight.wantHScroll) || (thePreflight.doZoom) )
  2064.             {
  2065. //            rect.left = bigRect.left;
  2066.             rect.right = bigRect.right;
  2067.             }
  2068.             
  2069.         if ( (thePreflight.wantVScroll) || (thePreflight.doZoom) )
  2070.             {
  2071. //            rect.top = bigRect.top;
  2072.             rect.bottom = bigRect.bottom;
  2073.             }
  2074.         
  2075.         MoveWindow(pWindow, rect.left, rect.top, false);
  2076.         SizeWindow(pWindow, rect.right - rect.left, rect.bottom - rect.top, false);
  2077.         }
  2078.         
  2079.         // fill in the default contents of the window
  2080.         pData->windowKind         = windowKind;
  2081.         pData->originalFileType    = fileType;
  2082.         pData->pMakeWindow         = (MakeWindowProc)thePreflight.makeProcPtr;
  2083.         pData->resRefNum        = -1;
  2084.         pData->dataRefNum        = -1;
  2085.         pData->contentRect         = GetWindowPort(pWindow)->portRect;
  2086.         
  2087.         // make the scroll bars
  2088.         {
  2089.         Rect    controlRect;
  2090.         
  2091.         if (thePreflight.wantHScroll)
  2092.             {
  2093.             pData->contentRect.bottom -= kScrollBarSize;
  2094.             controlRect = GetWindowPort(pWindow)->portRect;
  2095.             controlRect.top = controlRect.bottom - 16;
  2096.             if (thePreflight.wantVScroll)
  2097.                 controlRect.right -= kGrowScrollAdjust;
  2098.             OffsetRect(&controlRect, -1, 1);
  2099.             pData->hScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2100.             }
  2101.         if (thePreflight.wantVScroll)
  2102.             {
  2103.             pData->contentRect.right -= kScrollBarSize;
  2104.             controlRect = GetWindowPort(pWindow)->portRect;
  2105.             controlRect.left = controlRect.right - 16;
  2106.             if (thePreflight.wantVScroll)
  2107.                 controlRect.bottom -= kGrowScrollAdjust;
  2108.             OffsetRect(&controlRect, 1, -1);
  2109.             pData->vScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  2110.             }
  2111.         }
  2112.  
  2113.         // got a name?  Open the file        
  2114.         if (fileSpec)
  2115.             {
  2116.             anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2117.             if     ( 
  2118.                     ((anErr == afpAccessDenied) || (anErr == opWrErr) || (anErr == permErr) ) && 
  2119.                     (thePreflight.openKind != fsRdPerm)
  2120.                 )
  2121.                 {
  2122.                 thePreflight.openKind = fsRdPerm;
  2123.                 pData->isWritable = false;
  2124.                 anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  2125.                 }
  2126.             else
  2127.                 pData->isWritable = true;
  2128.             nrequire(anErr, FailedToOpenFile);
  2129.  
  2130.             // okay not to find a resource fork, because some don't have them                
  2131.             pData->resRefNum = FSpOpenResFile(fileSpec, thePreflight.openKind);
  2132.             
  2133.             // save the file spec in case someone is interested
  2134.             pData->fileSpec = *fileSpec;
  2135.             }
  2136.             
  2137.         if (pData->pMakeWindow)
  2138.             {
  2139.             Rect oldContent = pData->contentRect;
  2140.             anErr = (*(pData->pMakeWindow)) (pWindow, pData);
  2141.             if (!EqualRect(&oldContent, &pData->contentRect))
  2142.                 {
  2143.                 SizeWindow(pWindow, 
  2144.                         pData->contentRect.right  + (pData->vScroll != 0) * kScrollBarSize,
  2145.                         pData->contentRect.bottom + (pData->hScroll != 0) * kScrollBarSize,
  2146.                         false);
  2147.                 }
  2148.             }
  2149.         nrequire(anErr, FailedMakeWindow);
  2150.  
  2151.         // got a name?  Use it as the window title
  2152.         if ( (fileSpec) && (!pData->openAsNew) )
  2153.             SetWTitle(pWindow, fileSpec->name);
  2154.         else
  2155.             {
  2156.             if ((gMachineInfo.documentCount == 1) && (pData->windowKind == kTextWindow))
  2157.                 {
  2158.                 Str255 tempString;
  2159.         
  2160.                 GetIndString(tempString, kMiscStrings, iFirstNewDocumentTitle);    // get the "untitled" string (no number)
  2161.                 SetWTitle(pWindow, tempString);
  2162.                 }
  2163.             else
  2164.                 {
  2165.                 Str255    tempString;
  2166.                 Str32    numString;
  2167.     
  2168.                 GetWTitle(pWindow, tempString);
  2169.                 NumToString(gMachineInfo.documentCount, numString);
  2170.                 (void) ZeroStringSub(tempString, numString);
  2171.                 SetWTitle(pWindow, tempString);
  2172.                 }
  2173.  
  2174.             if (pData->bumpUntitledCount)
  2175.                 gMachineInfo.documentCount++;    // bump count if appropriate for this kind of document
  2176.             }
  2177.  
  2178.         // Make sure the scroll bars are reasonable in size, and move if they must
  2179.         AdjustScrollBars(pWindow, true, true, nil);
  2180.  
  2181.         // finally, if all goes well, we can see the window itself!
  2182.         ShowWindow(pWindow);
  2183.         }
  2184.  
  2185.     return noErr;
  2186.  
  2187. // EXCEPTION HANDLING
  2188. FailedMakeWindow:
  2189.     if (pData->resRefNum != -1)
  2190.         CloseResFile(pData->resRefNum);
  2191.     if (pData->dataRefNum != -1)
  2192.         FSClose(pData->dataRefNum);
  2193.     
  2194. FailedToOpenFile:
  2195.     CloseWindow(pWindow);
  2196.     
  2197. NewWindowFailed:
  2198.     DisposePtr((Ptr)pData);
  2199.     
  2200. FailedToAllocateWindow:
  2201. PreflightFailed:
  2202. SanityCheckFailed:
  2203.     return anErr;
  2204.     
  2205. } // MakeNewWindow
  2206.  
  2207. // --------------------------------------------------------------------------------------------------------------
  2208. #pragma segment Main
  2209.  
  2210.  
  2211. #define kVisualDelay 8
  2212.  
  2213. static pascal Boolean SaveChangesFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  2214. {
  2215.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2216.         return true;
  2217.  
  2218.     if (theEvent->what == updateEvt)
  2219.         {
  2220.         HandleEvent(theEvent);
  2221.         }
  2222.  
  2223.     if (theEvent->what == keyDown)
  2224.         {
  2225.         StringPtr keyEquivs = *GetString(kSaveChangesWindowID);
  2226.         unsigned char theKey = theEvent->message & charCodeMask;
  2227.  
  2228.         if (keyEquivs && (theKey == keyEquivs[1] || theKey == keyEquivs[2]))
  2229.             {
  2230.             short itemType;
  2231.             Rect theRect;
  2232.             ControlRef theControl;
  2233.             long finalTicks;
  2234.  
  2235.             GetDialogItem(theDialog, dontSaveChanges, &itemType, (Handle *) &theControl, &theRect);
  2236.             HiliteControl(theControl, kControlButtonPart);
  2237.             Delay(kVisualDelay, &finalTicks);
  2238.             HiliteControl(theControl, 0);
  2239.  
  2240.             *itemHit = dontSaveChanges;
  2241.             return true;
  2242.             }
  2243.         }
  2244.  
  2245.     return false;
  2246. }
  2247.  
  2248.  
  2249. static OSErr DoCloseWindow(WindowRef pWindow, Boolean quitting)
  2250. {
  2251.     OSErr            anErr = noErr;
  2252.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2253.     
  2254.     if ( (pData) && (pData->changed) )
  2255.         {
  2256.         short         hit;
  2257.         Str255        wTitle;
  2258.         DialogRef    dPtr;
  2259.         
  2260.         GetWTitle(pWindow, wTitle);
  2261.         SetCursor(&qd.arrow);
  2262.         ParamText(wTitle, "\p", "\p", "\p");
  2263.         
  2264.         if (gMachineInfo.haveNavigationServices)
  2265.         {
  2266.             hit = ConfirmSaveDialog(wTitle, quitting, MyEventProc);
  2267.         }
  2268.         else
  2269.         {
  2270.             hit = cancel;
  2271.             dPtr = GetNewDialog(kSaveChangesWindowID, nil, (WindowRef)-1);
  2272.             if (dPtr)
  2273.                 {
  2274.                 SetDialogDefaultItem(dPtr, ok);
  2275.                 SetDialogCancelItem (dPtr, cancel);
  2276.                 BeginMovableModal();
  2277.                 do
  2278.                     {
  2279.                     MovableModalDialog(SaveChangesFilter, &hit);
  2280.                     } while ((hit != dontSaveChanges) && (hit != ok) && (hit != cancel));
  2281.                     
  2282.                 DisposeDialog(dPtr);
  2283.                 EndMovableModal();
  2284.                 }
  2285.         }
  2286.     
  2287.         switch (hit)
  2288.             {
  2289.             case ok:
  2290.                 anErr = DoCommand(pWindow, cSave, 0);
  2291.                 if (anErr == eUserCanceled)        // redundant?
  2292.                     gAllDone = false;
  2293.                 break;
  2294.                 
  2295.             case cancel:
  2296.                 anErr = eUserCanceled;
  2297.                 gAllDone = false;
  2298.                 break;
  2299.                 
  2300.             case dontSaveChanges:
  2301.                 // don't save, so just close it
  2302.                 break;
  2303.             }
  2304.         }
  2305.  
  2306.     if (anErr == noErr)
  2307.         {
  2308.         if ( (pData) && (pData->pCloseWindow) )
  2309.             {
  2310.             // let the object close the window if it wishes to
  2311.             anErr = (*(pData->pCloseWindow)) (pWindow, pData);
  2312.             }
  2313.  
  2314.         // otherwise we close it the default way
  2315.         if (anErr == noErr)
  2316.             {
  2317.             if (pData)
  2318.                 {
  2319.                 CloseWindow(pWindow);
  2320.     
  2321.                 if (pData->hPrint)
  2322.                     {
  2323.                     if (gMachineInfo.haveGX)
  2324.                         GXDisposeJob( pData->hPrint);
  2325.                     else
  2326.                         DisposeHandle((Handle) pData->hPrint);
  2327.                     }
  2328.                     
  2329.                 if (pData->resRefNum != -1)
  2330.                     CloseResFile(pData->resRefNum);
  2331.                 if (pData->dataRefNum != -1)
  2332.                     FSClose(pData->dataRefNum);
  2333.                 DisposePtr((Ptr) pData);
  2334.                 }
  2335.             }
  2336.         }
  2337.  
  2338.     // If we closed the last window, clean up
  2339.     if (FrontWindow() == nil)
  2340.         {
  2341.         AdjustMenus(nil, true, false);
  2342.         gMachineInfo.documentCount = 1;        // back to "untitled"
  2343.         }
  2344.     
  2345.     return anErr;
  2346.     
  2347. } // DoCloseWindow
  2348.  
  2349. #undef dontSaveChanges
  2350.  
  2351. // --------------------------------------------------------------------------------------------------------------
  2352. #pragma segment Main
  2353.  
  2354. static OSErr    DetermineWindowTypeOrOpen(
  2355.     FSSpecPtr theSpec, OSType theType,                 // optional input params -- file to open
  2356.     OSType *returnedTypeList, short * pNumTypes,    // optional input params -- returns list of files
  2357.     Boolean *pWasAlreadyOpen)                        // optional input params -- was file already open
  2358. {
  2359.     OSErr        anErr = noErr;
  2360.     OSType        typeList[20];
  2361.     OSType        docList[20];
  2362.     short        numTypes;
  2363.  
  2364.     // use local copies if the input params are nil    
  2365.     if (returnedTypeList == nil)
  2366.         returnedTypeList = &typeList[0];
  2367.     if (pNumTypes == nil)
  2368.         pNumTypes = &numTypes;
  2369.     *pNumTypes = 0;
  2370.     
  2371.     // Load up all of the file types we know how to handle
  2372.     AboutGetFileTypes(returnedTypeList, docList, pNumTypes);
  2373.     PICTGetFileTypes(returnedTypeList, docList, pNumTypes);
  2374.     MovieGetFileTypes(returnedTypeList, docList, pNumTypes);
  2375.     ClipboardGetFileTypes(returnedTypeList, docList, pNumTypes);
  2376.     TextGetFileTypes(returnedTypeList, docList, pNumTypes);
  2377.     GXGetFileTypes(returnedTypeList, docList, pNumTypes);
  2378.     ThreeDGetFileTypes(returnedTypeList, docList, pNumTypes);
  2379.  
  2380.     if (theSpec != nil)
  2381.         {
  2382.         short         index;
  2383.         OSType        windowType = '????';
  2384.  
  2385.         for (index = 0; index < (*pNumTypes); ++index)
  2386.             if (theType == returnedTypeList[index])
  2387.                 windowType = docList[index];
  2388.                 
  2389.         if (windowType != '????')
  2390.             {
  2391.             
  2392.             if ( (theType == 'TEXT') || (theType == 'sEXT') )
  2393.                 {
  2394.                 FInfo    theInfo;
  2395.                 
  2396.                 FSpGetFInfo(theSpec, &theInfo);
  2397.                 if ((theInfo.fdFlags & kIsStationary) != 0)
  2398.                     theType = 'sEXT';
  2399.                 else
  2400.                     theType = 'TEXT';
  2401.                 }
  2402.                 
  2403.             anErr = MakeNewWindow(windowType, theSpec, theType, pWasAlreadyOpen);
  2404.             }
  2405.         else
  2406.             anErr = eDocumentWrongKind;
  2407.         }
  2408.         
  2409.         
  2410.     return anErr;
  2411.     
  2412. } // DetermineWindowTypeOrOpen
  2413.  
  2414. // --------------------------------------------------------------------------------------------------------------
  2415. #pragma segment Main
  2416.  
  2417. // Handle update/activate events behind Standard File
  2418. static pascal Boolean OpenDialogFilter(DialogRef theDialog, EventRecord *theEvent,
  2419.                                       short *itemHit, void *myDataPtr)
  2420. {
  2421.     #pragma unused(myDataPtr)
  2422.  
  2423.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  2424.     // drastically changing how the system handles the menu bar during our alert)
  2425.     if ( (theEvent->what == updateEvt) && (theEvent->message != (long)theDialog) )
  2426.         HandleEvent(theEvent);
  2427.  
  2428.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2429.         return(true);
  2430.  
  2431.     return(false);
  2432.  
  2433. } // OpenDialogFilter
  2434.  
  2435. #if GENERATINGCFM
  2436.     static RoutineDescriptor gOpenDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, OpenDialogFilter);
  2437.     static ModalFilterYDUPP gOpenDialogFilter = &gOpenDialogFilterRD;
  2438. #else
  2439.     static ModalFilterYDUPP gOpenDialogFilter = NewModalFilterYDProc(OpenDialogFilter);
  2440. #endif
  2441.  
  2442.  
  2443. static OSErr DoOpenWindow(void)
  2444. {
  2445.     OSErr                anErr = noErr;
  2446.     short                numTypes;
  2447.     OSType                typeList[20];
  2448.     Point                thePoint = { -1, -1 };
  2449.     FSSpec                fileSpec;
  2450.     OSType                fileType;
  2451.     Boolean                fileSelected;
  2452.     
  2453.     DetermineWindowTypeOrOpen(nil, '????', &typeList[0], &numTypes, nil);
  2454.     
  2455.     if (gMachineInfo.haveNavigationServices)
  2456.         {
  2457.         #if qSingleSelectionOnly
  2458.             fileSelected = OpenFileDialog('ttxt', numTypes, typeList, MyEventProc, &fileSpec, &fileType) == noErr;
  2459.         #else
  2460.             // Open as many documents as the user wishes thruogh Appleevents
  2461.             OpenFileDialog('ttxt', numTypes, typeList, MyEventProc, NULL, NULL);
  2462.             fileSelected = false;
  2463.         #endif
  2464.         }
  2465.     else 
  2466.         {
  2467.         StandardFileReply    sfReply;
  2468.  
  2469.         if (gMachineInfo.haveQuickTime)
  2470.             {
  2471.             CustomGetFilePreview(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2472.             }
  2473.         else
  2474.             {
  2475.             CustomGetFile(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2476.             }
  2477.             
  2478.         fileSelected= sfReply.sfGood;
  2479.         fileSpec    = sfReply.sfFile;
  2480.         fileType    = sfReply.sfType;
  2481.         }
  2482.     
  2483.     if (fileSelected)
  2484.         {
  2485.         SetWatchCursor();
  2486.         
  2487.         anErr = DetermineWindowTypeOrOpen(&fileSpec, fileType, &typeList[0], &numTypes, nil);
  2488.  
  2489.         SetCursor(&qd.arrow);
  2490.         }
  2491.         
  2492.     return anErr;
  2493.     
  2494. } // DoOpenWindow
  2495.  
  2496. // --------------------------------------------------------------------------------------------------------------
  2497. #pragma segment Main
  2498.  
  2499. static OSErr DoUpdateWindow(WindowRef pWindow)
  2500. {
  2501.     OSErr            anErr = noErr;
  2502.     WindowDataPtr    pData = GetWindowInfo(pWindow);
  2503.     GrafPtr            curPort;
  2504.     
  2505.     // only handle updates for windows we know about
  2506.     if (pData)
  2507.         {
  2508.         GetPort(&curPort);
  2509.         SetPort((GrafPtr)GetWindowPort(pWindow));
  2510.         BeginUpdate(pWindow);
  2511.                     
  2512.         if (pData->pUpdateWindow)
  2513.             anErr = (*(pData->pUpdateWindow)) (pWindow, pData);
  2514.     
  2515.         EndUpdate(pWindow);
  2516.         SetPort(curPort);
  2517.         }
  2518.     
  2519.     return anErr;
  2520.     
  2521. } // DoUpdateWindow
  2522.  
  2523. // --------------------------------------------------------------------------------------------------------------
  2524. #pragma segment Main
  2525.  
  2526. OSErr DoScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  2527. {
  2528.     OSErr    anErr = noErr;
  2529.     
  2530.     if ((deltaH) || (deltaV))
  2531.         {
  2532.         // if we have a balloon displayed, update before scrolling anything
  2533.         if (HMIsBalloon())
  2534.             DoUpdateWindow(pWindow);
  2535.         
  2536.         if ((pData) && (pData->pScrollContent))
  2537.             anErr = (*(pData->pScrollContent)) (pWindow, pData, deltaH, deltaV);
  2538.             
  2539.         if (anErr == noErr)
  2540.             {
  2541.             RgnHandle    invalidRgn = NewRgn();
  2542.             
  2543.             ScrollRect(&pData->contentRect, deltaH, deltaV, invalidRgn);
  2544.             InvalRgn(invalidRgn);
  2545.             DisposeRgn(invalidRgn);
  2546.     
  2547.             DoUpdateWindow(pWindow);
  2548.             }
  2549.         }
  2550.     
  2551.     return anErr;
  2552.     
  2553. } // DoScrollContent
  2554.  
  2555. // --------------------------------------------------------------------------------------------------------------
  2556. // BEGIN SCROLL ACTION PROCS
  2557. // --------------------------------------------------------------------------------------------------------------
  2558. #pragma segment Main
  2559.  
  2560. void SetControlAndClipAmount(ControlRef control, short * amount)
  2561. {
  2562.     short        value, max;
  2563.     
  2564.     value = GetControlValue(control);    /* get current value */
  2565.     max = GetControlMaximum(control);        /* and maximum value */
  2566.     *amount = value - *amount;
  2567.     if ( *amount < 0 )
  2568.         *amount = 0;
  2569.     else
  2570.         {
  2571.         if ( *amount > max )
  2572.             *amount = max;
  2573.         }
  2574.     SetControlValue(control, *amount);
  2575.     *amount = value - *amount;        /* calculate the real change */
  2576.     
  2577. } // SetControlAndClipAmount
  2578.  
  2579. // --------------------------------------------------------------------------------------------------------------
  2580. static pascal void VActionProc(ControlRef control, short part)
  2581. {
  2582.     if (part != 0)
  2583.         {
  2584.         WindowRef        pWindow = (**control).contrlOwner;
  2585.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2586.         short            amount = 0;
  2587.         
  2588.         switch (part)
  2589.             {
  2590.             case kControlUpButtonPart:
  2591.                 amount = pData->vScrollAmount;
  2592.                 break;
  2593.                 
  2594.             case kControlDownButtonPart:
  2595.                 amount = -pData->vScrollAmount;
  2596.                 break;
  2597.                 
  2598.             // vertical page scrolling should be a multiple of the incremental scrolling -- so that
  2599.             // we avoid half-lines of text at the bottom of pages.
  2600.             
  2601.             // More generically, if there was a method for dealing with text scrolling by a non-constant
  2602.             // amount, this would be better -- but SimpleText currently doesn't have a framework to allow
  2603.             // the document object to override the scroll amount dynamically.  Maybe something to add in
  2604.             // the future.
  2605.             case kControlPageUpPart:
  2606.                 amount = (((pData->contentRect.bottom - pData->contentRect.top) / pData->vScrollAmount)-1) * pData->vScrollAmount;
  2607.                 if (amount == 0)
  2608.                     amount = pData->contentRect.bottom - pData->contentRect.top;
  2609.                 break;
  2610.  
  2611.             case kControlPageDownPart:
  2612.                 amount = (((pData->contentRect.top - pData->contentRect.bottom) / pData->vScrollAmount)+1) * pData->vScrollAmount;
  2613.                 if (amount == 0)
  2614.                     amount = pData->contentRect.top - pData->contentRect.bottom;
  2615.                 break;
  2616.             }
  2617.         
  2618.         SetControlAndClipAmount(control, &amount);
  2619.         if (amount != 0)
  2620.             DoScrollContent(pWindow, pData, 0, amount);
  2621.         }
  2622.         
  2623. } // VActionProc
  2624.  
  2625. #if GENERATINGCFM
  2626.     static RoutineDescriptor gVActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, VActionProc);
  2627.     static ControlActionUPP gVActionProc = &gVActionProcRD;
  2628. #else
  2629.     static ControlActionUPP gVActionProc = VActionProc;
  2630. #endif
  2631.  
  2632. // --------------------------------------------------------------------------------------------------------------
  2633. static pascal void HActionProc(ControlRef control, short part)
  2634. {
  2635.     if (part != 0)
  2636.         {
  2637.         WindowRef        pWindow = (**control).contrlOwner;
  2638.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2639.         short            amount = 0;
  2640.         
  2641.         switch (part)
  2642.             {
  2643.             case kControlUpButtonPart:
  2644.                 amount = pData->hScrollAmount;
  2645.                 break;
  2646.                 
  2647.             case kControlDownButtonPart:
  2648.                 amount = -pData->hScrollAmount;
  2649.                 break;
  2650.                 
  2651.             case kControlPageUpPart:
  2652.                 amount = pData->contentRect.right - pData->contentRect.left;
  2653.                 break;
  2654.  
  2655.             case kControlPageDownPart:
  2656.                 amount = pData->contentRect.left - pData->contentRect.right;
  2657.                 break;
  2658.             }
  2659.         
  2660.         SetControlAndClipAmount(control, &amount);
  2661.         if (amount != 0)
  2662.             DoScrollContent(pWindow, pData, amount, 0);
  2663.         }
  2664.         
  2665. } // HActionProc
  2666.  
  2667. #if GENERATINGCFM
  2668.     static RoutineDescriptor gHActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, HActionProc);
  2669.     static ControlActionUPP gHActionProc = &gHActionProcRD;
  2670. #else
  2671.     static ControlActionUPP gHActionProc = HActionProc;
  2672. #endif
  2673.  
  2674. // --------------------------------------------------------------------------------------------------------------
  2675. // END SCROLL ACTION PROCS
  2676. // --------------------------------------------------------------------------------------------------------------
  2677.  
  2678. #pragma segment Main
  2679.  
  2680. static OSErr DoContentClick(WindowRef pWindow)
  2681. {
  2682.     OSErr            anErr = noErr;
  2683.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2684.     
  2685.     
  2686.     if ( pData )
  2687.         {
  2688.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2689.         
  2690.         if (pData->pContentClick)
  2691.             {
  2692.             // let the object handle the click if it wishes to
  2693.             anErr = (*(pData->pContentClick)) (pWindow, pData, &gEvent);
  2694.             }
  2695.         
  2696.         if (anErr == noErr) 
  2697.             {
  2698.             ControlRef        theControl;
  2699.             short            part;
  2700.             
  2701.             GlobalToLocal(&gEvent.where);
  2702.             part = FindControl(gEvent.where, pWindow, &theControl);
  2703.             switch (part)
  2704.                 {
  2705.                 // do nothing for viewRect case
  2706.                 case 0:
  2707.                     break;
  2708.  
  2709.                 // track the thumb, and then update all at once
  2710.                 case kControlIndicatorPart:
  2711.                     {
  2712.                     short    value = GetControlValue(theControl);
  2713.                     
  2714.                     part = TrackControl(theControl, gEvent.where, nil);
  2715.                     if (part != 0)
  2716.                         {
  2717.                         // turn the value into a delta
  2718.                         value -= GetControlValue(theControl);
  2719.                         
  2720.                         // if we actually moved
  2721.                         if (value != 0)
  2722.                             {
  2723.                             if (theControl == pData->hScroll)
  2724.                                 DoScrollContent(pWindow, pData, value, 0);
  2725.                             if (theControl == pData->vScroll)
  2726.                                 DoScrollContent(pWindow, pData, 0, value);
  2727.                                 
  2728.                             }
  2729.                         }
  2730.                     }
  2731.                     break;
  2732.                     
  2733.                 // track the control, and scroll as we go
  2734.                 default:
  2735.                     if (theControl)
  2736.                         {
  2737.                         if (theControl == pData->hScroll)
  2738.                             part = TrackControl(theControl, gEvent.where, gHActionProc);
  2739.                         if (theControl == pData->vScroll)
  2740.                             part = TrackControl(theControl, gEvent.where, gVActionProc);
  2741.                         }
  2742.                     break;
  2743.                 }
  2744.             }
  2745.  
  2746.         }
  2747.  
  2748.         
  2749.     return anErr;
  2750.     
  2751. } // DoContentClick
  2752.  
  2753. // --------------------------------------------------------------------------------------------------------------
  2754. #pragma segment Main
  2755.  
  2756. static OSErr DoGrowWindow(WindowRef pWindow, EventRecord *pEvent)
  2757. {
  2758.     OSErr            anErr = noErr;
  2759.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2760.     Rect            tempRect;
  2761.     LongRect        docRect;
  2762.     long            growResult;
  2763.     
  2764.     if (pData)
  2765.         {
  2766.         GrafPtr    pPort = (GrafPtr)GetWindowPort(pWindow);
  2767.         
  2768.         SetPort(pPort);
  2769.         
  2770.         RectToLongRect(&pData->contentRect, &docRect);
  2771.         if (pData->pGetDocumentRect)
  2772.             (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2773.         if (pData->vScroll)
  2774.             docRect.right += 16;
  2775.         if (pData->hScroll)
  2776.             docRect.bottom += 16;
  2777.         
  2778.         if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2779.             {
  2780.             docRect.right += 16;
  2781.             docRect.bottom += 16;
  2782.             }
  2783.             
  2784.         // set up resize constraints
  2785.         tempRect.left = pData->minHSize;
  2786.         if (tempRect.left == 0)
  2787.             tempRect.left = kMinDocSize;
  2788.         tempRect.right = docRect.right - docRect.left;
  2789.         if (tempRect.right < tempRect.left)
  2790.             tempRect.right = tempRect.left;
  2791.         tempRect.top = pData->minVSize;
  2792.         if (tempRect.top == 0)
  2793.             tempRect.top = kMinDocSize;
  2794.         tempRect.bottom = docRect.bottom - docRect.top;
  2795.         if (tempRect.bottom < tempRect.top)
  2796.             tempRect.bottom = tempRect.top;
  2797.             
  2798.         growResult = GrowWindow(pWindow, pEvent->where, &tempRect);
  2799.         if ( growResult != 0 ) 
  2800.             {
  2801.             Rect        oldRect;
  2802.             RgnHandle    currentInval = NewRgn();
  2803.             Boolean        needInvalidate;
  2804.             
  2805.             // save old content area
  2806.             oldRect = pData->contentRect;
  2807.             
  2808.             // save old pending update
  2809.             GetWindowUpdateRgn(pWindow, currentInval);
  2810.             OffsetRgn(currentInval, pPort->portBits.bounds.left, pPort->portBits.bounds.top);
  2811.             
  2812.             // grow window and recalc what is needed
  2813.             SizeWindow(pWindow, growResult & 0xFFFF, growResult >> 16, true);
  2814.             AdjustScrollBars(pWindow, true, true, &needInvalidate);
  2815.             
  2816.             if (needInvalidate)
  2817.                 {
  2818.                 InvalRect(&pData->contentRect);
  2819.                 }
  2820.             else
  2821.                 {
  2822.                 // don't bother to redraw things that haven't changed
  2823.                 SectRect(&oldRect, &pData->contentRect, &oldRect);
  2824.                 ValidRect(&oldRect);
  2825.                 
  2826.                 // but, if a pending update was there, be sure to deal with that!
  2827.                 InvalRgn(currentInval);
  2828.                 }
  2829.  
  2830.             // if we have offset scrollbars, then force a redraw of them
  2831.             if (pData->hScrollOffset)
  2832.                 {
  2833.                 oldRect = GetWindowPort(pWindow)->portRect;
  2834.                 oldRect.right = oldRect.left + pData->hScrollOffset;
  2835.                 oldRect.top = oldRect.bottom - kScrollBarSize;
  2836.                 InvalRect(&oldRect);
  2837.                 }
  2838.             if (pData->vScrollOffset)
  2839.                 {
  2840.                 oldRect = GetWindowPort(pWindow)->portRect;
  2841.                 oldRect.bottom = oldRect.top + pData->vScrollOffset;
  2842.                 oldRect.left = oldRect.right - kScrollBarSize;
  2843.                 InvalRect(&oldRect);
  2844.                 }
  2845.  
  2846.             DisposeRgn(currentInval);
  2847.             }
  2848.             
  2849.         }
  2850.         
  2851.     
  2852.     return anErr;
  2853.     
  2854. } // DoGrowWindow
  2855.  
  2856. // --------------------------------------------------------------------------------------------------------------
  2857. #pragma segment Main
  2858.  
  2859. static OSErr DoZoomWindow(WindowRef pWindow, short zoomDir)
  2860. {
  2861.     Rect                *windRect, *zoomRect;
  2862.     Rect                globalPortRect, theSect, dGDRect;
  2863.     GDHandle            nthDevice, dominantGDevice;
  2864.     long                sectArea, greatestArea;
  2865.     short                 hMax, vMax;
  2866.     
  2867.     // determine the max size of the window
  2868.     {
  2869.     WindowDataPtr        pData = GetWindowInfo(pWindow);
  2870.     LongRect            docRect;
  2871.     
  2872.     RectToLongRect(&pData->contentRect, &docRect);
  2873.     if (pData->pGetDocumentRect)
  2874.         (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2875.     if (pData->vScroll)
  2876.         docRect.right += kScrollBarSize;
  2877.     if (pData->hScroll)
  2878.         docRect.bottom += kScrollBarSize;
  2879.     
  2880.     if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2881.         {
  2882.         docRect.right += kScrollBarSize;
  2883.         docRect.bottom += kScrollBarSize;
  2884.         }
  2885.  
  2886.     hMax = docRect.right - docRect.left;
  2887.     vMax = docRect.bottom - docRect.top;
  2888.     }
  2889.     
  2890.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2891.     EraseRect(&GetWindowPort(pWindow)->portRect);    // recommended for cosmetic reasons
  2892.  
  2893.     if (zoomDir == inZoomOut) 
  2894.         {
  2895.  
  2896.         /*
  2897.          *    ZoomWindow() is a good basic tool, but it doesn't do everything necessary to
  2898.          *    implement a good human interface when zooming. In fact it's not even close for
  2899.          *    more high-end hardware configurations. We must help it along by calculating an
  2900.          *    appropriate window size and location any time a window zooms out.
  2901.          */
  2902.         {
  2903.         RgnHandle    structRgn = NewRgn();
  2904.         
  2905.         GetWindowStructureRgn(pWindow, structRgn);
  2906.         windRect = &(**structRgn).rgnBBox;
  2907.         DisposeRgn(structRgn);
  2908.         }
  2909.         dominantGDevice = nil;
  2910.         if (gMachineInfo.theEnvirons.hasColorQD) 
  2911.             {
  2912.  
  2913.             /*
  2914.              *    Color QuickDraw implies the possibility of multiple monitors. This is where
  2915.              *    zooming becomes more interesting. One should zoom onto the monitor containing
  2916.              *    the greatest portion of the window. This requires walking the gDevice list.
  2917.              */
  2918.  
  2919.             nthDevice = GetDeviceList();
  2920.             greatestArea = 0;
  2921.             while (nthDevice != nil) 
  2922.                 {
  2923.                 if (TestDeviceAttribute(nthDevice, screenDevice)) 
  2924.                     {
  2925.                     if (TestDeviceAttribute(nthDevice, screenActive)) 
  2926.                         {
  2927.                         SectRect(windRect, &(**nthDevice).gdRect, &theSect);
  2928.                         sectArea = (long) RectWidth(theSect) * (long) RectHeight(theSect);
  2929.                         if (sectArea > greatestArea) 
  2930.                             {
  2931.                             greatestArea = sectArea;        // save the greatest intersection
  2932.                             dominantGDevice = nthDevice;    // and which device it belongs to
  2933.                             }
  2934.                         }
  2935.                     }
  2936.                 nthDevice = GetNextDevice(nthDevice);
  2937.                 }
  2938.             }
  2939.  
  2940.         /*
  2941.          *    At this point, we know the dimensions of the window we're zooming, and we know
  2942.          *    what screen we're going to put it on. To be more specific, however, we need a
  2943.          *    rectangle which defines the maximum dimensions of the resized window's contents.
  2944.          *    This rectangle accounts for the thickness of the window frame, the menu bar, and
  2945.          *    one or two pixels around the edges for cosmetic compatibility with ZoomWindow().
  2946.          */
  2947.  
  2948.         if (dominantGDevice != nil) 
  2949.             {
  2950.             dGDRect = (**dominantGDevice).gdRect;
  2951.             if (dominantGDevice == GetMainDevice())        // account for menu bar on main device
  2952.                 dGDRect.top += GetMBarHeight();
  2953.             }
  2954.         else 
  2955.             {
  2956.             dGDRect = qd.screenBits.bounds;                // if no gDevice, use default monitor
  2957.             dGDRect.top += GetMBarHeight();
  2958.             }
  2959.  
  2960.         globalPortRect = GetWindowPort(pWindow)->portRect;
  2961.         LocalToGlobal(&TopLeft(globalPortRect));        // calculate the window's portRect
  2962.         LocalToGlobal(&BotRight(globalPortRect));        // in global coordinates
  2963.  
  2964.         // account for the window frame and inset it a few pixels
  2965.         dGDRect.left    += 2 + globalPortRect.left - windRect->left;
  2966.         dGDRect.top        += 2 + globalPortRect.top - windRect->top;
  2967.         dGDRect.right    -= 1 + windRect->right - globalPortRect.right;
  2968.         dGDRect.bottom    -= 1 + windRect->bottom - globalPortRect.bottom;
  2969.  
  2970.         /*
  2971.          *    Now we know exactly what our limits are, and since there are input parameters
  2972.          *    specifying the dimensions we'd like to see, we can move and resize the zoom
  2973.          *    state rectangle for the best possible results. We have three goals in this:
  2974.          *    1. Display the window entirely visible on a single device.
  2975.          *    2. Resize the window to best represent the dimensions of the document itself.
  2976.          *    3. Move the window as short a distance as possible to achieve #1 and #2.
  2977.          */
  2978.  
  2979.         zoomRect = &(**(WStateDataHandle) ((WindowPeek) pWindow)->dataHandle).stdState;
  2980.  
  2981.         /*
  2982.          *    Initially set the zoom rectangle to the size requested by the input parameters,
  2983.          *    although not smaller than a minimum size. We do this without moving the origin.
  2984.          */
  2985.  
  2986.         zoomRect->right = (zoomRect->left = globalPortRect.left) +
  2987.                                 Max(hMax, kMinDocSize);
  2988.         zoomRect->bottom = (zoomRect->top = globalPortRect.top) +
  2989.                                 Max(vMax, kMinDocSize);
  2990.  
  2991.         // Shift the entire rectangle if necessary to bring its origin inside dGDRect.
  2992.         OffsetRect(zoomRect,
  2993.                     Max(dGDRect.left - zoomRect->left, 0),
  2994.                     Max(dGDRect.top - zoomRect->top, 0));
  2995.  
  2996.         /*
  2997.          *    Shift the rectangle up and/or to the left if necessary to accomodate the view,
  2998.          *    and if it is possible to do so. The rectangle may not be moved such that its
  2999.          *    origin would fall outside of dGDRect.
  3000.          */
  3001.  
  3002.         OffsetRect(zoomRect,
  3003.                     -Pin(zoomRect->right - dGDRect.right, 0, zoomRect->left - dGDRect.left),
  3004.                     -Pin(zoomRect->bottom - dGDRect.bottom, 0, zoomRect->top - dGDRect.top));
  3005.  
  3006.         // Clip expansion to dGDRect, in case view is larger than dGDRect.
  3007.         zoomRect->right = Min(zoomRect->right, dGDRect.right);
  3008.         zoomRect->bottom = Min(zoomRect->bottom, dGDRect.bottom);
  3009.         }
  3010.  
  3011.     ZoomWindow(pWindow, zoomDir, pWindow == FrontWindow());
  3012.     
  3013.     AdjustScrollBars(pWindow, true, true, nil);
  3014.  
  3015.     InvalRect(&GetWindowPort(pWindow)->portRect);
  3016.     
  3017.     return noErr;
  3018.     
  3019. } // DoZoomWindow
  3020.  
  3021. // --------------------------------------------------------------------------------------------------------------
  3022. #pragma segment Main
  3023.  
  3024. OSErr DoActivate(WindowRef pWindow, Boolean activating)
  3025. {
  3026.  
  3027.     OSErr            anErr = noErr;
  3028.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3029.     
  3030.     SetPort((GrafPtr) GetWindowPort(pWindow));
  3031.     
  3032.     if ( pData )
  3033.         {
  3034.         if (pData->pActivateEvent)
  3035.             anErr = (*(pData->pActivateEvent)) (pWindow, pData, activating);
  3036.             
  3037.         if (anErr == noErr)
  3038.             {
  3039.             if (activating)
  3040.                 {
  3041.                 // Reshow all controls on activation
  3042.                 if (pData->hScroll)
  3043.                     ShowControl(pData->hScroll);
  3044.                 if (pData->vScroll)
  3045.                     ShowControl(pData->vScroll);
  3046.                 }
  3047.             else
  3048.                 {
  3049.                 // Hide all controls on deactivation
  3050.                 if (pData->hScroll)
  3051.                     HideControl(pData->hScroll);
  3052.                 if (pData->vScroll)
  3053.                     HideControl(pData->vScroll);
  3054.                 }
  3055.                 
  3056.             if (pData->hasGrow) 
  3057.                 {
  3058.                 Rect    growIconRect;
  3059.                 
  3060.                 CalculateGrowIcon(pData, &growIconRect);
  3061.                 InvalRect(&growIconRect);
  3062.                 }
  3063.             }
  3064.         }
  3065.  
  3066.     AdjustMenus(pWindow, true, false);
  3067.         
  3068.     return anErr;
  3069.     
  3070. } // DoActivate
  3071.  
  3072. // --------------------------------------------------------------------------------------------------------------
  3073. #pragma segment Main
  3074.  
  3075. static OSErr    DoStartupGX(void)
  3076. {
  3077.     gxGraphicsError    anErr = noErr;
  3078.     
  3079.     if (!gMachineInfo.haveStartedGX)
  3080.         {
  3081.         GXEnterGraphics();
  3082.         GXGetGraphicsError(&anErr);
  3083.         if ( (anErr == noErr) && (GXGetGraphicsClient() == nil) )
  3084.             anErr = out_of_memory;
  3085.         if (anErr == noErr)
  3086.             {
  3087.             anErr = GXInitPrinting();
  3088.             if (anErr != noErr)
  3089.                 GXExitGraphics();
  3090.             }
  3091.             
  3092.         if (anErr != noErr)
  3093.             GXSetGraphicsClient(nil);
  3094.         }
  3095.         
  3096.     if (GXGetGraphicsClient() == nil)
  3097.         anErr = out_of_memory;
  3098.  
  3099.     if (anErr == noErr)
  3100.         gMachineInfo.haveStartedGX = true;
  3101.  
  3102.     return anErr;
  3103.     
  3104. } // DoStartupGX
  3105.  
  3106. // --------------------------------------------------------------------------------------------------------------
  3107. #pragma segment Main
  3108.  
  3109. OSErr    DoDefault(WindowDataPtr     pData)
  3110. {
  3111.     OSErr    anErr = noErr;
  3112.     
  3113.     if (gMachineInfo.haveGX)
  3114.         {
  3115.         // start up GX, if needed
  3116.         anErr = DoStartupGX();
  3117.  
  3118.         if (anErr == noErr)
  3119.             {
  3120.             
  3121.             // default the job if we don't have it
  3122.             if (pData->hPrint == nil)
  3123.                 {
  3124.                 anErr = GXNewJob((gxJob*)&pData->hPrint);
  3125.                 if (anErr == noErr)
  3126.                     {
  3127.                     GXInstallApplicationOverride(pData->hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3128.                     GXInstallApplicationOverride(pData->hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3129.                     }
  3130.                 }
  3131.             }
  3132.         }
  3133.     else
  3134.         {
  3135.         PrOpen();
  3136.         anErr = PrError();
  3137.         if (anErr == noErr)
  3138.             {
  3139.             if (pData->hPrint == nil)
  3140.                 {
  3141.                 pData->hPrint = NewHandleClear(sizeof(TPrint));
  3142.                 anErr = MemError();
  3143.                 if (anErr == noErr)
  3144.                     PrintDefault(pData->hPrint);
  3145.                 }
  3146.                 
  3147.             }
  3148.         PrClose();
  3149.         }
  3150.         
  3151.     return anErr;
  3152.     
  3153. } // DoDefault
  3154.  
  3155. // --------------------------------------------------------------------------------------------------------------
  3156. #pragma segment Main
  3157.  
  3158. static void SetupForPrintDialogs(gxEditMenuRecord * pEdit)
  3159. {
  3160.     MenuHandle    menu;
  3161.     short        menuID, itemID;
  3162.  
  3163.     CommandToIDs(cCut,   &pEdit->editMenuID, &pEdit->cutItem);
  3164.     CommandToIDs(cCopy,  &pEdit->editMenuID, &pEdit->copyItem);
  3165.     CommandToIDs(cPaste, &pEdit->editMenuID, &pEdit->pasteItem);
  3166.     CommandToIDs(cClear, &pEdit->editMenuID, &pEdit->clearItem);
  3167.     CommandToIDs(cUndo,  &pEdit->editMenuID, &pEdit->undoItem);
  3168.     
  3169.     // diable everything we don't want to deal with
  3170.     for (menuID = mApple; menuID <= mLastMenu; ++menuID)
  3171.         {
  3172.         menu = GetMenuHandle(menuID);
  3173.         
  3174.         if (menu)
  3175.             {
  3176.             switch (menuID)
  3177.                 {
  3178.                 case mApple:
  3179.                     CommandToIDs(cAbout, &menuID, &itemID);
  3180.                     DisableItem(menu, itemID);
  3181.                     break;
  3182.                     
  3183.                 case mEdit:
  3184.                     CommandToIDs(cSelectAll, &menuID, &itemID);
  3185.                     DisableItem(menu, itemID);
  3186.                     CommandToIDs(cShowClipboard, &menuID, &itemID);
  3187.                     DisableItem(menu, itemID);
  3188.                     break;
  3189.                     
  3190.                 default:
  3191.                     DisableItem(menu, 0);
  3192.                     break;
  3193.                 }
  3194.             }
  3195.         }
  3196.     
  3197.     // Disable the current indicator because the dialogs are moveable modal    
  3198.     HiliteMenu(0);
  3199.         
  3200. } // SetupForPrintDialogs
  3201.  
  3202. // --------------------------------------------------------------------------------------------------------------
  3203. #pragma segment Main
  3204.  
  3205. OSErr    DoPageSetup(WindowRef pWindow)
  3206. {
  3207.     OSErr            anErr = noErr;
  3208.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3209.         
  3210.     anErr = DoDefault(pData);
  3211.     nrequire(anErr, DoDefault);
  3212.     
  3213.     if (gMachineInfo.haveGX)
  3214.         {
  3215.         gxEditMenuRecord    theEdit;
  3216.         
  3217.         SetupForPrintDialogs(&theEdit);
  3218.         
  3219.         GXJobDefaultFormatDialog(pData->hPrint, &theEdit);
  3220.         anErr = GXGetJobError(pData->hPrint);
  3221.             
  3222.         AdjustMenus(pWindow, true, true);
  3223.         }
  3224.     else
  3225.         {
  3226.         PrOpen();
  3227.         anErr = PrError();
  3228.         if (anErr == noErr)
  3229.             {
  3230.             SetCursor(&qd.arrow);
  3231.             PrStlDialog(pData->hPrint);
  3232.             }
  3233.         PrClose();
  3234.         }
  3235.  
  3236. // FALL THROUGH EXCEPTION HANDLING
  3237. DoDefault:        
  3238.     return anErr;
  3239.     
  3240. } // DoPageSetup
  3241.  
  3242. // --------------------------------------------------------------------------------------------------------------
  3243. #pragma segment Main
  3244.  
  3245. static OSErr    DoPrintSetup(WindowRef pWindow, StringPtr pPrinterName)
  3246. {
  3247.     OSErr            anErr = noErr;
  3248.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  3249.         
  3250.     anErr = DoDefault(pData);
  3251.     nrequire(anErr, DoDefault);
  3252.     
  3253.     if (gMachineInfo.haveGX)
  3254.         {
  3255.         gxEditMenuRecord    theEdit;
  3256.         gxDialogResult        result;
  3257.         
  3258.         // toss any previous errors that might be around
  3259.         (void)GXGetJobError(pData->hPrint);
  3260.         
  3261.         if ( (pPrinterName) && (pPrinterName[0] != 0) )
  3262.             GXSelectJobOutputPrinter(pData->hPrint, pPrinterName);
  3263.         else
  3264.             {
  3265.             SetupForPrintDialogs(&theEdit);
  3266.             result = GXJobPrintDialog(pData->hPrint, &theEdit);
  3267.             AdjustMenus(pWindow, true, true);
  3268.             }
  3269.         if (anErr == noErr)
  3270.             {
  3271.             anErr = GXGetJobError(pData->hPrint);
  3272.             if ( (anErr == noErr) && (result == gxCancelSelected) )
  3273.                 anErr = eUserCanceled;
  3274.             }
  3275.         }
  3276.     else
  3277.         {
  3278.         PrOpen();
  3279.         anErr = PrError();
  3280.         if (anErr == noErr)
  3281.             {
  3282.             SetCursor(&qd.arrow);
  3283.             if (PrJobDialog(pData->hPrint) == false)
  3284.                 anErr = eUserCanceled;
  3285.             }
  3286.             
  3287.         PrClose();
  3288.         }
  3289.         
  3290. // FALL THROUGH EXCEPTION HANDLING
  3291. DoDefault:        
  3292.     return anErr;
  3293.     
  3294. } // DoPrintSetup
  3295.  
  3296. // --------------------------------------------------------------------------------------------------------------
  3297. #pragma segment Main
  3298.  
  3299. static OSErr    DoPrint(WindowRef pWindow, void * hPrint, Boolean oneCopy)
  3300. {
  3301.     gxGraphicsError        anErr = noErr;
  3302.     WindowDataPtr         pData = GetWindowInfo(pWindow);
  3303.     Boolean                didAllocate = false;
  3304.     
  3305.     // use a watch cursor while printing
  3306.     SetWatchCursor();
  3307.     
  3308.     if (gMachineInfo.haveGX)
  3309.         {
  3310.         // startup GX, if needed
  3311.         anErr = DoStartupGX();
  3312.  
  3313.         if ( (anErr == noErr) && (hPrint == nil) )
  3314.             {
  3315.             anErr = GXNewJob((gxJob*)&hPrint);
  3316.             if (anErr == noErr)            
  3317.                 {
  3318.                 GXInstallApplicationOverride(hPrint, gxPrintingEventMsg,         gPrintingEventMessage);
  3319.                 GXInstallApplicationOverride(hPrint, gxCompleteSpoolFileMsg,     gCompleteSpoolFileMessage);
  3320.                 didAllocate = true;
  3321.                 }
  3322.             }
  3323.  
  3324.         if (anErr == noErr)
  3325.             {            
  3326.             Str255    docName;
  3327.             
  3328.             if (oneCopy)
  3329.                 {
  3330.                 gxCopiesInfo theCopies;
  3331.                 
  3332.                 theCopies.copies = 1;
  3333.                 AddCollectionItem(GXGetJobCollection(hPrint), gxCopiesTag, gxPrintingTagID, sizeof(theCopies), &theCopies);
  3334.                 }
  3335.                 
  3336.             GXSetJobRefCon(hPrint, pData);
  3337.             
  3338.             GetWTitle(pWindow, docName);
  3339.             GXStartJob(hPrint, docName, 0);
  3340.             anErr = GXGetJobError(hPrint);
  3341.             if (anErr == noErr)
  3342.                 {
  3343.                 long        first, last, pageIndex;
  3344.                 Rect        pageRect;
  3345.                 CGrafPort    thePort;
  3346.                 
  3347.                 // determine size of page, and number of pages
  3348.                 {
  3349.                 gxRectangle    pageSize;
  3350.                 GXGetFormatDimensions(GXGetJobFormat(hPrint, 1), &pageSize, nil);
  3351.                 pageRect.top    = pageSize.top >> 16;
  3352.                 pageRect.left    = pageSize.left >> 16;
  3353.                 pageRect.bottom    = pageSize.bottom >> 16;
  3354.                 pageRect.right    = pageSize.right >> 16;
  3355.                 }            
  3356.                 GXGetJobPageRange(hPrint, &first, &last);
  3357.                 anErr = GXGetJobError(hPrint);
  3358.                 if (first < 1)
  3359.                     first = 1;
  3360.                 if (last < first)
  3361.                     last = first;
  3362.                     
  3363.                 if (anErr == noErr)
  3364.                     {
  3365.                     // make a port to perform translation in
  3366.                     OpenCPort(&thePort);
  3367.                     
  3368.                     for (pageIndex = first; pageIndex <= last; ++pageIndex)
  3369.                         {
  3370.                         SetPort((GrafPtr) &thePort);
  3371.                         
  3372.                         if (pData->documentOutputsGX)
  3373.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3374.                         else
  3375.                             {
  3376.                             Point                    patStretch = {1,1};
  3377.                             gxTranslationOption     options = gxOptimizedTranslation;
  3378.                             CatchRefCon                theRefCon;
  3379.                             
  3380.                             if (GXGetPrinterDriverType(GXGetJobPrinter(hPrint)) == 'post')
  3381.                                 options += gxPostScriptTargetTranslation;
  3382.                             else
  3383.                                 options += gxRasterTargetTranslation;
  3384.                             
  3385.                             theRefCon.theJob                    = hPrint;
  3386.                             theRefCon.doLayout                    = (pData->originalFileType == 'TEXT');
  3387.                             theRefCon.thePage                     = GXNewShape(gxPictureType);
  3388.                             theRefCon.thePageRectangle.top         = ff(pageRect.top);
  3389.                             theRefCon.thePageRectangle.left     = ff(pageRect.left);
  3390.                             theRefCon.thePageRectangle.bottom     = ff(pageRect.bottom);
  3391.                             theRefCon.thePageRectangle.right     = ff(pageRect.right);
  3392.     
  3393.                             GXInstallQDTranslator(
  3394.                                         qd.thePort,
  3395.                                         options,
  3396.                                         &pageRect,
  3397.                                         &pageRect,
  3398.                                         patStretch,
  3399.                                         gSimpleCatchShape,
  3400.                                         &theRefCon);
  3401.                             GXGetGraphicsError(&anErr);
  3402.                             
  3403.                             if (anErr == noErr)
  3404.                                 {
  3405.                                 long    whichPage = pageIndex;
  3406.                                 
  3407.                                 anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &whichPage);
  3408.                                 GXRemoveQDTranslator(qd.thePort, nil);
  3409.                                 
  3410.                                 GXPrintPage(hPrint, pageIndex, nil, theRefCon.thePage);
  3411.                                 anErr = GXGetJobError(hPrint);
  3412.                                 pageIndex = whichPage;
  3413.                                 }
  3414.                             
  3415.                             GXDisposeShape(theRefCon.thePage);
  3416.                             }
  3417.                         
  3418.                         if (anErr == noErr)
  3419.                             GXGetGraphicsError(&anErr);
  3420.                         
  3421.                         // bail when we are told to stop
  3422.                         if ( (pageIndex == -1) || (anErr != noErr) )
  3423.                             break;
  3424.                         }
  3425.                     
  3426.                     // all done with our temp port and job
  3427.                     CloseCPort(&thePort);
  3428.                     }
  3429.                     
  3430.                 GXFinishJob(hPrint);
  3431.                 if (anErr == noErr) anErr = GXGetJobError(hPrint);
  3432.                 }
  3433.                 
  3434.             }
  3435.                 
  3436.         if (didAllocate)
  3437.             GXDisposeJob(hPrint);
  3438.  
  3439.         // restore those menus!
  3440.         AdjustMenus(pWindow, true, true);
  3441.  
  3442.         }
  3443.     else
  3444.         {
  3445.         TPPrPort    printingPort;
  3446.         
  3447.         PrOpen();
  3448.         anErr = PrError();
  3449.         if (anErr == noErr)
  3450.             {
  3451.             if (hPrint == nil)
  3452.                 {
  3453.                 hPrint = NewHandleClear(sizeof(TPrint));
  3454.                 anErr = MemError();
  3455.                 if (anErr == noErr)
  3456.                     {
  3457.                     PrintDefault(hPrint);
  3458.                     didAllocate = true;
  3459.                     }
  3460.                 }
  3461.             
  3462.             if (anErr == noErr)
  3463.                 {
  3464.                 short    firstPage, lastPage;
  3465.                 
  3466.                 // be sure to get the page range BEFORE calling PrValidate(), 
  3467.                 // which blows it away for many drivers.
  3468.                 firstPage = (**(THPrint)hPrint).prJob.iFstPage;
  3469.                 lastPage = (**(THPrint)hPrint).prJob.iLstPage;
  3470.                 
  3471.                 // make sure the print record is cool to use
  3472.                 PrValidate(hPrint);
  3473.  
  3474.                 // then clear out the job itself -- some drivers don't
  3475.                 // do this from within PrValidate().  We want the job
  3476.                 // clear in case the driver bases background behavior
  3477.                 // from this range (and many do).
  3478.                 (**(THPrint)hPrint).prJob.iFstPage = 1;
  3479.                 (**(THPrint)hPrint).prJob.iLstPage = 9999;
  3480.                 
  3481.                 if (oneCopy)
  3482.                     (** ((THPrint)hPrint)).prJob.iCopies = 1;
  3483.                     
  3484.                 // start printing, and then loop over the pages
  3485.                 printingPort = PrOpenDoc(hPrint, nil, nil);
  3486.                 anErr = PrError();
  3487.                 if (anErr == noErr)
  3488.                     {
  3489.                     long    pageIndex;
  3490.                     Rect    pageRect;
  3491.                     
  3492.                     SetPort((GrafPtr) printingPort);
  3493.                     
  3494.                     pageRect = (**(THPrint)hPrint).prInfo.rPage;
  3495.                     if (firstPage < 1)
  3496.                         firstPage = 1;
  3497.                     if (lastPage < firstPage)
  3498.                         lastPage = firstPage;
  3499.                     for (pageIndex = firstPage; pageIndex <= lastPage; ++pageIndex)
  3500.                         {
  3501.                         PrOpenPage(printingPort, nil);
  3502.                         anErr = PrError();
  3503.                         if (anErr == noErr)
  3504.                             anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  3505.                             
  3506.                         PrClosePage(printingPort);
  3507.                         if (anErr == noErr)
  3508.                             anErr = PrError();
  3509.                         if ( (anErr != noErr) || (pageIndex == -1) )
  3510.                             break;
  3511.                         }
  3512.                     }
  3513.                     
  3514.                 // Finish up printing of the document
  3515.                 PrCloseDoc(printingPort);
  3516.                 if (anErr == noErr)
  3517.                     anErr = PrError();
  3518.                 if ( (anErr == noErr) && ((**(THPrint)hPrint).prJob.bJDocLoop == bSpoolLoop) )
  3519.                     {
  3520.                     TPrStatus    theStatus;
  3521.                     
  3522.                     PrPicFile(hPrint, nil, nil, nil, &theStatus);
  3523.                     anErr = PrError();
  3524.                     }
  3525.                 }
  3526.                 
  3527.             if (didAllocate)
  3528.                 DisposeHandle((Handle) hPrint);
  3529.                 
  3530.             }
  3531.         PrClose();
  3532.         }
  3533.     
  3534.         
  3535.     // restore cursor
  3536.     SetCursor(&qd.arrow);
  3537.  
  3538.     return anErr;
  3539.     
  3540. } // DoPrint
  3541.  
  3542. // --------------------------------------------------------------------------------------------------------------
  3543. #pragma segment Main
  3544.  
  3545. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult)
  3546. {
  3547.     OSErr            anErr = noErr;
  3548.     WindowDataPtr     pData = nil;
  3549.     
  3550.     if (pWindow)
  3551.         {
  3552.         pData = (WindowDataPtr) GetWindowInfo(pWindow);
  3553.         
  3554.         if ( (pData) && (pData->pCommand) )
  3555.             anErr = (*(pData->pCommand)) (pWindow, pData, commandID, menuResult);
  3556.         }
  3557.     
  3558.     if (anErr == noErr)
  3559.         {
  3560.         // default command handling
  3561.         switch (commandID)
  3562.             {
  3563.             // About box command
  3564.             case cAbout:
  3565.                 if (!BringToFrontIfExists(kAboutWindow))
  3566.                     anErr = MakeNewWindow(kAboutWindow, nil, '????', nil);
  3567.                 break;
  3568.                 
  3569.             case cDeskAccessory:
  3570.                 {
  3571.                 Str255    tempString;
  3572.                 
  3573.                 GetMenuItemText(GetMenuHandle(menuResult>>16), menuResult & 0xFFFF, tempString);
  3574.                 OpenDeskAcc(tempString);
  3575.                 }
  3576.                 break;
  3577.                 
  3578.             // New window command
  3579.             case cNew:
  3580.                 anErr = MakeNewWindow(kTextWindow, nil, 'TEXT', nil);
  3581.                 break;
  3582.                 
  3583.             // Open window command
  3584.             case cOpen:
  3585.                 anErr = DoOpenWindow();
  3586.                 break;
  3587.                 
  3588.             // Close window command
  3589.             case cClose:
  3590.                 anErr = DoCloseWindow(pWindow, false);
  3591.                 break;
  3592.                 
  3593.             case cPageSetup:
  3594.                 anErr = DoPageSetup(pWindow);
  3595.                 break;
  3596.                 
  3597.             case cPrint:
  3598.                 anErr = DoPrintSetup(pWindow, nil);
  3599.                 if (anErr == noErr)
  3600.                     anErr = DoPrint(pWindow, pData->hPrint, false);
  3601.                 break;
  3602.                 
  3603.             case cPrintOneCopy:
  3604.                 anErr = DoPrint(pWindow, pData->hPrint, true);
  3605.                 break;
  3606.                 
  3607.             // get out of here command!
  3608.             case cQuit:
  3609.                 gAllDone = true;
  3610.                 break;
  3611.     
  3612.             // show/hide clipboard
  3613.             case cShowClipboard:
  3614.                 if (!BringToFrontIfExists(kClipboardWindow))
  3615.                     {
  3616.                     anErr = MakeNewWindow(kClipboardWindow, nil, '????', nil);
  3617.                     }
  3618.                 else
  3619.                     {
  3620.                     pWindow = FrontWindow();
  3621.                     anErr = DoCloseWindow(pWindow, false);
  3622.                     }
  3623.                 break;
  3624.                 
  3625.             case cNextPage:
  3626.                 gEvent.what = keyDown;
  3627.                 gEvent.message = kPageDown << 8;
  3628.                 gEvent.modifiers = 0;
  3629.                 DoKeyEvent(pWindow, &gEvent, false);
  3630.                 break;
  3631.                 
  3632.             case cPreviousPage:
  3633.                 gEvent.what = keyDown;
  3634.                 gEvent.message = kPageUp << 8;
  3635.                 gEvent.modifiers = 0;
  3636.                 DoKeyEvent(pWindow, &gEvent, false);
  3637.                 break;
  3638.                 
  3639.             // Do nothing command
  3640.             case cNull:
  3641.                 break;
  3642.                             
  3643.             default:
  3644.                 break;
  3645.             }
  3646.         }
  3647.         
  3648.     // don't report cancels
  3649.     if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  3650.         anErr = noErr;
  3651.     
  3652.     // some errors map to other errors because they are basically the same
  3653.     // This way we can use the same string.
  3654.     if (anErr == out_of_memory)
  3655.         anErr = memFullErr;
  3656.         
  3657.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  3658.         {
  3659.         // some commands are so similar to other commands that we map their IDs
  3660.         // for the purposes of the error strings
  3661.         if (commandID == cSaveAs)
  3662.             commandID = cSave;
  3663.         if (commandID == cPrintOneCopy)
  3664.             commandID = cPrint;
  3665.             
  3666.         ConductErrorDialog(anErr, commandID, cancel);
  3667.         }
  3668.         
  3669.     // in any case, unhilite the menu selected after command processing is done    
  3670.     HiliteMenu(0);
  3671.     
  3672.     return anErr;
  3673.     
  3674. } // DoCommand
  3675.  
  3676. // --------------------------------------------------------------------------------------------------------------
  3677. #pragma segment Main
  3678.  
  3679. static OSErr    DoMenuCommand(WindowRef pWindow, long menuResult)
  3680. {
  3681.     OSErr    anErr = noErr;
  3682.     short    commandID = cNull;
  3683.     short    ** commandHandle;
  3684.     short    menuID = menuResult >> 16;
  3685.     
  3686.     if (menuID == kHMHelpMenuID)
  3687.         {
  3688.         // close existing database (if any)
  3689.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  3690.             {
  3691.             AGClose(&gAGRefNum);
  3692.             gAGRefNum = -1;
  3693.             }
  3694.             
  3695.         // and open the database we have found in the past
  3696.         AGOpen(&gAGSpec, 0, nil, &gAGRefNum);
  3697.         }
  3698.     else
  3699.         {
  3700.         if (menuID >= mFontSubMenusStart)
  3701.             {
  3702.             commandID = cSelectFontStyle;
  3703.             }
  3704.         else
  3705.             {
  3706.             // read in the resource that controls this menu
  3707.                 {
  3708.                 short    oldResFile = CurResFile();
  3709.                 
  3710.                 UseResFile(gApplicationResFile);
  3711.                 commandHandle = (short**) Get1Resource('MCMD', menuID);
  3712.                 UseResFile(oldResFile);
  3713.                 anErr = ResError();
  3714.                 nrequire(anErr, FailedToLoadCommandTable);
  3715.                 }
  3716.             
  3717.             if (commandHandle)
  3718.                 {
  3719.                 short    item = menuResult & 0xFFFF;
  3720.                 short    * pCommands = *commandHandle;
  3721.                 
  3722.                 if (item <= pCommands[0])
  3723.                     commandID = pCommands[item];
  3724.                 else
  3725.                     commandID = pCommands[pCommands[0]];
  3726.                 }
  3727.             }
  3728.         
  3729.         anErr = DoCommand(pWindow, commandID, menuResult);
  3730.         }
  3731.     
  3732. // FALL THROUGH EXCEPTION HANDLING
  3733. FailedToLoadCommandTable:
  3734.  
  3735.     return anErr;
  3736.     
  3737. } // DoMenuCommand
  3738.  
  3739.  
  3740. // --------------------------------------------------------------------------------------------------------------
  3741. #pragma segment Main
  3742.  
  3743. static void DoKeyPageDown(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3744. {
  3745.  
  3746.     if (GetControlValue(pData->vScroll) < GetControlMaximum(pData->vScroll))
  3747.         VActionProc(pData->vScroll, kControlPageDownPart);
  3748.     else
  3749.         {
  3750.         if ( (processPageControls) && (IsCommandEnabled(cNextPage)) )
  3751.             {
  3752.             short amount;
  3753.  
  3754.             if (DoCommand(pWindow, cNextPage, 0) == eActionAlreadyHandled)
  3755.                 {
  3756.                 amount = GetControlValue(pData->vScroll);
  3757.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3758.                 if (amount != 0)
  3759.                     DoScrollContent(pWindow, pData, 0, amount);
  3760.                 }
  3761.             
  3762.             AdjustMenus(pWindow, true, false);
  3763.             }
  3764.         }
  3765.     
  3766. } // DoKeyPageDown
  3767.  
  3768. // --------------------------------------------------------------------------------------------------------------
  3769. #pragma segment Main
  3770.  
  3771. static void DoKeyPageUp(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3772. {
  3773.     if (GetControlValue(pData->vScroll) > GetControlMinimum(pData->vScroll))
  3774.         VActionProc(pData->vScroll, kControlPageUpPart);
  3775.     else
  3776.         {
  3777.         if ( (processPageControls) && (IsCommandEnabled(cPreviousPage)) )
  3778.             {
  3779.             short amount;
  3780.             
  3781.             if (DoCommand(pWindow, cPreviousPage, 0) == eActionAlreadyHandled)
  3782.                 {
  3783.                 amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3784.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3785.                 if (amount != 0)
  3786.                     DoScrollContent(pWindow, pData, 0, amount);
  3787.                 }
  3788.             
  3789.             AdjustMenus(pWindow, true, false);
  3790.             }
  3791.         }
  3792.         
  3793. } // DoKeyPageUp
  3794.  
  3795. // --------------------------------------------------------------------------------------------------------------
  3796. #pragma segment Main
  3797.  
  3798. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls)
  3799. {
  3800.     OSErr            anErr = noErr;
  3801.     WindowDataPtr     pData = nil;
  3802.     Boolean            passToObject = false;
  3803.     Boolean         isMotionKey = false;
  3804.     long            menuResult = 0;
  3805.     
  3806.     char keyCode = (pEvent->message >> 8) & charCodeMask;
  3807.  
  3808.     if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3809.         {
  3810.         AdjustMenus(pWindow, true, false);
  3811.         menuResult = MenuKey(pEvent->message & charCodeMask);
  3812.         DoMenuCommand(pWindow, menuResult);
  3813.         pWindow = FrontWindow();
  3814.         }
  3815.  
  3816.     if (menuResult == 0)
  3817.         {
  3818.         if (pWindow)
  3819.             {
  3820.             pData = (WindowDataPtr)GetWindowInfo(pWindow);
  3821.             if ( (pData) && (pData->pKeyEvent) )
  3822.                 passToObject = true;
  3823.             SetPort((GrafPtr) GetWindowPort(pWindow));
  3824.             }
  3825.             
  3826.         if (pData)
  3827.             {
  3828.             switch (keyCode)
  3829.                 {
  3830.                 case kHome: // top of file
  3831.                     isMotionKey = true;
  3832.                     if (pData->vScroll)
  3833.                         {
  3834.                         short amount;
  3835.                         
  3836.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3837.                             DoCommand(pWindow, cGotoPage, cGotoFirst);
  3838.  
  3839.                         amount = GetControlValue(pData->vScroll);
  3840.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3841.                         if (amount != 0)
  3842.                             DoScrollContent(pWindow, pData, 0, amount);
  3843.                         passToObject = false;
  3844.                         }
  3845.                     break;
  3846.                     
  3847.                 case kEnd: // end of file
  3848.                     isMotionKey = true;
  3849.                     if (pData->vScroll)
  3850.                         {
  3851.                         short amount;
  3852.  
  3853.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3854.                             DoCommand(pWindow, cGotoPage, cGotoLast);
  3855.                             
  3856.                         amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3857.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3858.                         if (amount != 0)
  3859.                             DoScrollContent(pWindow, pData, 0, amount);
  3860.                         passToObject = false;
  3861.                         }
  3862.                     break;
  3863.                     
  3864.                 case kPageUp: // scroll bar page up
  3865.                     isMotionKey = true;
  3866.                     if (pData->vScroll)
  3867.                         {
  3868.                         DoKeyPageUp(pWindow, pData, processPageControls);
  3869.                         passToObject = false;
  3870.                         }
  3871.                     break;
  3872.                     
  3873.                 case kPageDown: // scroll bar page down
  3874.                     isMotionKey = true;
  3875.                     if (pData->vScroll)
  3876.                         {
  3877.                         DoKeyPageDown(pWindow, pData, processPageControls);
  3878.                         passToObject = false;
  3879.                         }
  3880.                     break;
  3881.                             
  3882.                 case kUpArrow:        // scroll bar up arrow
  3883.                     isMotionKey = true;
  3884.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3885.                         {
  3886.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3887.                             DoKeyPageUp(pWindow, pData, processPageControls);
  3888.                         else
  3889.                             VActionProc(pData->vScroll, kControlUpButtonPart);
  3890.                         passToObject = false;
  3891.                         }
  3892.                     break;
  3893.                     
  3894.                 case kDownArrow:    // scroll bar down arrow
  3895.                     isMotionKey = true;
  3896.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3897.                         {
  3898.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3899.                             DoKeyPageDown(pWindow, pData, processPageControls);
  3900.                         else
  3901.                             VActionProc(pData->vScroll, kControlDownButtonPart);
  3902.                         passToObject = false;
  3903.                         }
  3904.                     break;
  3905.         
  3906.                 case kLeftArrow:    // scroll bar left arrow
  3907.                     isMotionKey = true;
  3908.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3909.                         {
  3910.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3911.                             HActionProc(pData->hScroll, kControlPageUpPart);
  3912.                         else
  3913.                             HActionProc(pData->hScroll, kControlUpButtonPart);
  3914.                         passToObject = false;
  3915.                         }
  3916.                     break;
  3917.                     
  3918.                 case kRightArrow:    // scroll bar right arrow
  3919.                     isMotionKey = true;
  3920.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3921.                         {
  3922.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3923.                             HActionProc(pData->hScroll, kControlPageDownPart);
  3924.                         else
  3925.                             HActionProc(pData->hScroll, kControlDownButtonPart);
  3926.                         passToObject = false;
  3927.                         }
  3928.                     break;
  3929.                     }
  3930.         
  3931.             if (passToObject)
  3932.                 anErr = (*(pData->pKeyEvent)) (pWindow, pData, pEvent, isMotionKey);
  3933.             else
  3934.                 {
  3935.                 if ( (pData->documentAcceptsText == false) && !( pEvent->modifiers & cmdKey ) && !(isMotionKey) )
  3936.                     anErr = eDocumentNotModifiable;
  3937.                 }
  3938.             }
  3939.  
  3940.         if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) )
  3941.             ConductErrorDialog(anErr, cTypingCommand, ok);
  3942.             
  3943.         } // (menuResult == 0)
  3944.     
  3945.         
  3946.     return anErr;
  3947.     
  3948. } // DoKeyEvent
  3949.  
  3950. // --------------------------------------------------------------------------------------------------------------
  3951. #pragma segment Main
  3952.  
  3953. static OSErr DoAdjustCursor(WindowRef pWindow)
  3954. {
  3955.     OSErr        anErr = noErr;
  3956.     Point        mouse;
  3957.     Boolean        didAdjust = false;
  3958.     
  3959.     if (pWindow)
  3960.         {
  3961.         // not one of our windows?  don't do anything
  3962.         if (GetWindowKind(pWindow) != userKind)
  3963.             didAdjust = true;
  3964.             
  3965.         SetPort((GrafPtr) GetWindowPort(pWindow));
  3966.         
  3967.         if ( (!didAdjust) && (gMachineInfo.haveTSM) )
  3968.             {
  3969.             GetMouse(&mouse);
  3970.             LocalToGlobal(&mouse);
  3971.             if (SetTSMCursor(mouse))
  3972.                 didAdjust = true;
  3973.             }
  3974.             
  3975.         if (!didAdjust)
  3976.             {
  3977.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  3978.             RgnHandle        content = NewRgn();
  3979.             Point            globalMouse;
  3980.             
  3981.             GetMouse(&mouse);
  3982.             globalMouse = mouse;
  3983.             LocalToGlobal(&globalMouse);
  3984.             
  3985.             GetWindowContentRgn(pWindow, content);
  3986.             if ((pData) && (PtInRgn(globalMouse, content)) && (PtInRect(mouse, &pData->contentRect)))
  3987.                 {
  3988.                 Rect            tempRect;
  3989.                 
  3990.                 tempRect = pData->contentRect;
  3991.                 LocalToGlobal(&TopLeft(tempRect));
  3992.                 LocalToGlobal(&BotRight(tempRect));
  3993.                 
  3994.                 if (pData->pAdjustCursor)
  3995.                     anErr = (*(pData->pAdjustCursor)) (pWindow, pData, &mouse, &tempRect);
  3996.     
  3997.                 RectRgn(gCursorRgn, &tempRect);
  3998.                 }
  3999.             DisposeRgn(content);
  4000.             }
  4001.         else
  4002.             anErr = eActionAlreadyHandled;
  4003.         }
  4004.     
  4005.     // nobody set the cursor, we do it ourselves
  4006.     if (anErr != eActionAlreadyHandled)
  4007.         SetCursor(&qd.arrow);
  4008.         
  4009.     return anErr;
  4010.     
  4011. } // DoAdjustCursor
  4012.  
  4013. // --------------------------------------------------------------------------------------------------------------
  4014. #pragma segment Main
  4015.  
  4016. static long DetermineWaitTime(WindowRef pWindow)
  4017. {
  4018.     long    waitTime = kMaxWaitTime;
  4019.     
  4020.     while (pWindow)
  4021.         {
  4022.         long            newWaitTime;
  4023.         WindowDataPtr    pData = GetWindowInfo(pWindow);
  4024.         
  4025.         if ((pData) && (pData->pCalculateIdleTime))
  4026.             newWaitTime = (*(pData->pCalculateIdleTime)) (pWindow, pData);
  4027.         else
  4028.             newWaitTime = kMaxWaitTime;
  4029.         
  4030.         if (newWaitTime < waitTime)
  4031.             waitTime = newWaitTime;
  4032.             
  4033.         pWindow = GetNextWindow(pWindow);
  4034.         }
  4035.     
  4036.     return(waitTime);
  4037.     
  4038. } // DetermineWaitTime
  4039.  
  4040. // --------------------------------------------------------------------------------------------------------------
  4041. #pragma segment Main
  4042.  
  4043. void HandleEvent(EventRecord * pEvent)
  4044. {
  4045.     WindowRef pWindow = FrontWindow();
  4046.     
  4047.     switch (pEvent->what)
  4048.         {
  4049.         case kHighLevelEvent:
  4050.             AEProcessAppleEvent(pEvent);
  4051.             break;
  4052.             
  4053.         case osEvt:
  4054.             switch ((pEvent->message >> 24) & 0xFF) /* high byte of message */
  4055.                 {        
  4056.                 case mouseMovedMessage:
  4057.                     DoAdjustCursor(pWindow);
  4058.                     break;
  4059.                     
  4060.                 case suspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  4061.                     gMachineInfo.amInBackground = (pEvent->message & 1) == 0;
  4062.                     if (pWindow)
  4063.                         DoActivate(pWindow, !gMachineInfo.amInBackground);
  4064.                         
  4065.                     // on resume, we must call GXUpdateJob for all active jobs in
  4066.                     // order to get instant activation of extensions and to properly
  4067.                     // handle potential shifting of driver names
  4068.                     if ( (gMachineInfo.haveGX) && (!gMachineInfo.amInBackground) )
  4069.                         {
  4070.                         WindowRef    walkWindows = pWindow;
  4071.                         
  4072.                         while (walkWindows)
  4073.                             {
  4074.                             WindowDataPtr    pData = GetWindowInfo(walkWindows);
  4075.                             if (pData)
  4076.                                 {
  4077.                                 gxJob    theJob = pData->hPrint;
  4078.                                 if (theJob)
  4079.                                     {
  4080.                                     GXUpdateJob(theJob);
  4081.                                     }
  4082.                                 }
  4083.                             walkWindows = GetNextWindow(walkWindows);
  4084.                             }
  4085.                         }
  4086.                     break;
  4087.                 }
  4088.             break;
  4089.             
  4090.         case activateEvt:
  4091.             pWindow = (WindowRef) pEvent->message;
  4092.             DoActivate(pWindow, (pEvent->modifiers & activeFlag) != 0);
  4093.             break;
  4094.                             
  4095.         // disk inserted events must be handled, or uninitialized floppies 
  4096.         // won't be recognized.
  4097.         case diskEvt:
  4098.             if ( HiWord(pEvent->message) != noErr ) 
  4099.                 {
  4100.                 Point    where;
  4101.             
  4102.                 SetPt(&where, 70, 50);
  4103.                 ShowCursor();
  4104.                 (void) DIBadMount(where, pEvent->message);
  4105.                 }        
  4106.             break;
  4107.                 
  4108.         case mouseUp:
  4109.             break;
  4110.             
  4111.         case mouseDown:
  4112.             {
  4113.             short part = FindWindow(pEvent->where, &pWindow);                    
  4114.             
  4115.             switch ( part ) 
  4116.                 {
  4117.                 case inContent:
  4118.                     if (pWindow != FrontWindow())
  4119.                         SelectWindow(pWindow);
  4120.                     else
  4121.                         DoContentClick(pWindow);
  4122.                     break;
  4123.                     
  4124.                 case inGoAway:
  4125.                     if (TrackGoAway(pWindow, pEvent->where) )
  4126.                         DoCommand(pWindow, cClose, 0);
  4127.                     break;
  4128.                     
  4129.                 case inGrow:
  4130.                     DoGrowWindow(pWindow, pEvent);
  4131.                     break;
  4132.                     
  4133.                 case inZoomIn:
  4134.                 case inZoomOut:
  4135.                     if ( TrackBox(pWindow, pEvent->where, part) )
  4136.                         DoZoomWindow(pWindow, part);
  4137.                     break;
  4138.                     
  4139.                 case inDrag:
  4140.                     {
  4141.                     WindowDataPtr    pData = GetWindowInfo(pWindow);
  4142.                     
  4143.                     if ( (pData) && (pData->dragWindowAligned) )
  4144.                         DragAlignedWindow((WindowPtr) pWindow, pEvent->where, &qd.screenBits.bounds, nil, nil);
  4145.                     else
  4146.                         DragWindow(pWindow, pEvent->where, &qd.screenBits.bounds);
  4147.                     }
  4148.                     break;
  4149.                     
  4150.                 case inMenuBar:                /* process a mouse menu command (if any) */
  4151.                     {
  4152.                     long    menuResult;
  4153.                     
  4154.                     // force these threads to run to completion so the
  4155.                     // contents of the menus are fully initialized
  4156.                     
  4157.                     if (gFontThread != kNoThreadID)
  4158.                         {
  4159.                         gDontYield = true;
  4160.                         SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4161.                         YieldToThread(gFontThread);
  4162.                         gDontYield = false;
  4163.                         }
  4164.                     if (gAGThread != kNoThreadID)
  4165.                         {
  4166.                         gDontYield = true;
  4167.                         SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4168.                         YieldToThread(gAGThread);
  4169.                         gDontYield = false;
  4170.                         }
  4171.                     
  4172.                     pWindow = FrontWindow();
  4173.                     AdjustMenus(pWindow, true, false);
  4174.                     menuResult = MenuSelect(pEvent->where);
  4175.                     if ( (gMachineInfo.haveTSM) && (TSMMenuSelect(menuResult)) )
  4176.                         HiliteMenu(0);
  4177.                     else
  4178.                         DoMenuCommand(pWindow, menuResult);
  4179.                     }
  4180.                     break;
  4181.                     
  4182.                 case inSysWindow:            /* let the system handle the mouseDown */
  4183.                     SystemClick(pEvent, pWindow);
  4184.                     break;
  4185.                     
  4186.                 } // switch(part)
  4187.             }
  4188.             break;
  4189.             
  4190.         case keyDown:
  4191.         case autoKey:                        /* check for menukey equivalents */
  4192.             DoKeyEvent(pWindow, pEvent, true);
  4193.             break;
  4194.             
  4195.         case updateEvt:
  4196.             pWindow = (WindowRef) pEvent->message;
  4197.             DoUpdateWindow(pWindow);
  4198.             break;
  4199.  
  4200.         } // switch (pEvent->what)
  4201.     
  4202. } // HandleEvent
  4203.  
  4204. // --------------------------------------------------------------------------------------------------------------
  4205. #pragma segment Main
  4206.  
  4207. static OSErr    DoEventLoop(void)
  4208. {
  4209.     OSErr        anErr = noErr;
  4210.     Boolean        gotEvent;
  4211.     Boolean        trueGotEvent;
  4212.     WindowRef    pWindow;
  4213.     
  4214.     do     {
  4215.         pWindow = LMGetFirstWindow();        // walk all of our windows, even invisible ones
  4216.         
  4217.         DoAdjustCursor(pWindow);
  4218.         gotEvent = WaitNextEvent(everyEvent, &gEvent, DetermineWaitTime(pWindow), gCursorRgn);
  4219.         trueGotEvent = gotEvent;
  4220.  
  4221.         // WNE may close the window if it's owned by some silly extension.
  4222.         pWindow = LMGetFirstWindow();        
  4223.         
  4224.         // let text services handle the event first if it wishes to do so
  4225.         if ( gMachineInfo.haveTSM )
  4226.             {
  4227.             ScriptCode    keyboardScript;
  4228.             WindowRef    theFront = FrontWindow();
  4229.             
  4230.             if (theFront)
  4231.                 {
  4232.                 SetPort((GrafPtr) GetWindowPort(theFront));
  4233.                 
  4234.                 keyboardScript = GetScriptManagerVariable(smKeyScript);
  4235.                 if (FontToScript(qd.thePort->txFont) != keyboardScript)
  4236.                     TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));
  4237.                 }
  4238.             
  4239.             if (TSMEvent(&gEvent))
  4240.                 gotEvent = false;
  4241.             }
  4242.             
  4243.         // let all windows filter this event, and get time if they wish to
  4244.         while (pWindow)
  4245.             {
  4246.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4247.             Boolean            finishedEvent = false;
  4248.             
  4249.             // help manager for the front window
  4250.             if ( (pWindow == FrontWindow()) && (pData) && (!gMachineInfo.amInBackground) && (HMGetBalloons()) )
  4251.                 {
  4252.                 Point    theMouse, tipLocation;
  4253.                 short    newBalloon = iNoBalloon;
  4254.                 Rect    tempRect;
  4255.                 
  4256.                 // find out where the mouse is                
  4257.                 SetPort((GrafPtr) GetWindowPort(pWindow));
  4258.                 GetMouse(&theMouse);
  4259.                 
  4260.                 // and only do something if we are within the window itself
  4261.                 if (PtInRect(theMouse, &GetWindowPort(pWindow)->portRect))
  4262.                     {
  4263.                     // is it in the vertical scroll bar?
  4264.                     if (pData->vScroll)
  4265.                         {
  4266.                         tempRect = (**(pData->vScroll)).contrlRect;
  4267.                         if (PtInRect(theMouse, &tempRect))
  4268.                             {
  4269.                             newBalloon = iHelpActiveScroll;
  4270.                             if (GetControlMinimum(pData->vScroll) == GetControlMaximum(pData->vScroll))
  4271.                                 newBalloon = iHelpDimVertScroll;
  4272.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4273.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4274.                             }
  4275.                         }
  4276.                         
  4277.                     // is it in the horizontal scroll bar?
  4278.                     if (pData->hScroll)
  4279.                         {
  4280.                         tempRect = (**(pData->hScroll)).contrlRect;
  4281.                         if (PtInRect(theMouse, &tempRect))
  4282.                             {
  4283.                             newBalloon = iHelpActiveScroll;
  4284.                             if (GetControlMinimum(pData->hScroll) == GetControlMaximum(pData->hScroll))
  4285.                                 newBalloon = iHelpDimHorizScroll;
  4286.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4287.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4288.                             }
  4289.                         }
  4290.                     
  4291.                     // is it in the grow box?
  4292.                     if (pData->hasGrow)
  4293.                         {
  4294.                         CalculateGrowIcon(pData, &tempRect);
  4295.                         if (PtInRect(theMouse, &tempRect))
  4296.                             {
  4297.                             newBalloon = iHelpGrowBox;
  4298.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  4299.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  4300.                             }
  4301.                         }
  4302.                     
  4303.                     // none of the above, must be the content
  4304.                     if (newBalloon == iNoBalloon)
  4305.                         {
  4306.                         newBalloon = iHelpGenericContent;
  4307.                         tempRect = pData->contentRect;
  4308.                         if (pData->pGetBalloon)
  4309.                             (*(pData->pGetBalloon)) (pWindow, pData, &theMouse, &newBalloon, &tempRect);
  4310.                             
  4311.                         tipLocation.h = tempRect.left + kFromTopTipOffset;
  4312.                         tipLocation.v = tempRect.top + kFromTopTipOffset;
  4313.                         }
  4314.                         
  4315.                     // show our new balloon, or remove the old one
  4316.                     if (newBalloon != iNoBalloon)
  4317.                         {
  4318.                         if ( (gMachineInfo.lastBalloonIndex != newBalloon) || (!HMIsBalloon()) )
  4319.                             {
  4320.                             HMMessageRecord    message;
  4321.                             
  4322.                             if (newBalloon != iDidTheBalloon)
  4323.                                 {
  4324.                                 message.hmmHelpType = khmmString;
  4325.                                 GetIndString(message.u.hmmString, kWindowHelpID, newBalloon);
  4326.                                 LocalToGlobal(&tipLocation);
  4327.                                 (void) HMShowBalloon(&message, tipLocation, nil, nil, 0, kDefaultBalloonVariant, 0);
  4328.                                 }
  4329.                             gMachineInfo.lastBalloonIndex = newBalloon;
  4330.                             }
  4331.                         }
  4332.                     else
  4333.                         HMRemoveBalloon();
  4334.                     }
  4335.                     
  4336.                     
  4337.                 }
  4338.                 
  4339.             // if we hit a window we know about, then do filtering
  4340.             if (pData)
  4341.                 {
  4342.                 if (pData->pFilterEvent)
  4343.                     finishedEvent = (*(pData->pFilterEvent)) (pWindow, pData, &gEvent);
  4344.                 }
  4345.  
  4346.             // if filtering indicates complete handling of event, then stop, and
  4347.             // do no regular processing.
  4348.             if (finishedEvent)
  4349.                 {
  4350.                 gotEvent = false;
  4351.                 pWindow = nil;
  4352.                 }
  4353.             else
  4354.                 pWindow = GetNextWindow(pWindow);
  4355.             }
  4356.             
  4357.         if (gotEvent)
  4358.             HandleEvent(&gEvent);
  4359.             
  4360.         // close request?
  4361.         if (gAllDone)
  4362.             {
  4363.             pWindow = FrontWindow();
  4364.             while ((gAllDone) && (pWindow) )
  4365.                 {
  4366.                 WindowRef    nextWindow = GetNextWindow(pWindow);
  4367.                 OSErr        closeError = DoCloseWindow(pWindow, true);
  4368.                 
  4369.                 // window didn't close?  then don't quit
  4370.                 if (pWindow == FrontWindow())
  4371.                     gAllDone = false;
  4372.                     
  4373.                 // something bad happened, then don't quit
  4374.                 if ( (closeError != noErr) /* && (closeError != eUserCanceled) */ )
  4375.                     gAllDone = false;
  4376.                     
  4377.                 pWindow = nextWindow;
  4378.                 }
  4379.             }
  4380.         
  4381.         // our threads are low-priority, so we only give time to them on idle
  4382.         if (gMachineInfo.haveThreads && !trueGotEvent && !gAllDone)
  4383.             YieldToAnyThread();
  4384.         
  4385.         } while (!gAllDone);
  4386.         
  4387.     return anErr;
  4388.     
  4389. } // DoEventLoop
  4390.  
  4391.  
  4392. // --------------------------------------------------------------------------------------------------------------
  4393. // DRAG MANAGEMENT GLOBAL SUPPORT ROUTINES
  4394. // --------------------------------------------------------------------------------------------------------------
  4395.  
  4396. // Globals for our drag handlers
  4397.  
  4398. Boolean                gCanAccept;                // if we can receive the item(s) being dragged
  4399.  
  4400. // --------------------------------------------------------------------------------------------------------------
  4401. #pragma segment Drag
  4402.  
  4403. static pascal OSErr GlobalTrackingHandler(short message, WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4404. {
  4405.     #pragma unused(handlerRefCon)
  4406.  
  4407.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4408.  
  4409.     // Call the tracking handler associated with this type of window. Only allow messages referencing
  4410.     // a specific window to be passed to the handler.
  4411.  
  4412.     if (pData)
  4413.         {    
  4414.         if (pData->pDragTracking)
  4415.             return ((*(pData->pDragTracking)) (pWindow, pData, theDragRef, message));
  4416.         }
  4417.     
  4418.     return noErr;
  4419.  
  4420. } // GlobalTrackingHandler
  4421.  
  4422. DragTrackingHandlerUPP gGlobalTrackingHandler;
  4423.  
  4424. // --------------------------------------------------------------------------------------------------------------
  4425. #pragma segment Drag
  4426.  
  4427. static pascal OSErr GlobalReceiveHandler(WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  4428. {
  4429.     #pragma unused(handlerRefCon)
  4430.  
  4431.     WindowDataPtr pData = GetWindowInfo(pWindow);
  4432.     
  4433.     if (pData)
  4434.         {
  4435.         if (pData->pDragTracking)
  4436.             return ((*(pData->pDragReceive)) (pWindow, pData, theDragRef));
  4437.         }
  4438.  
  4439.     return noErr;
  4440.  
  4441. } // GlobalReceiveHandler
  4442.  
  4443. DragReceiveHandlerUPP gGlobalReceiveHandler;
  4444.  
  4445. // --------------------------------------------------------------------------------------------------------------
  4446. //
  4447. // IsOnlyThisFlavor - Given a DragReference and a FlavorType, we iterate through the drag items to determine if
  4448. //                      all are of flavor theType. If this is so, we return true. If any of the items are not
  4449. //                      theType, we return false, indicating that we should not accept the drag.
  4450. //
  4451. #pragma segment Drag
  4452.  
  4453. Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType)
  4454. {
  4455.     unsigned short    items, index;
  4456.     FlavorFlags        theFlags;
  4457.     ItemReference    itemID;
  4458.     OSErr            anErr = noErr;
  4459.  
  4460.     CountDragItems(theDragRef, &items);
  4461.     
  4462.     for(index = 1; index <= items; index++)
  4463.         {
  4464.         GetDragItemReferenceNumber(theDragRef, index, &itemID);
  4465.  
  4466.         anErr = GetFlavorFlags(theDragRef, itemID, theType, &theFlags);
  4467.         if(anErr == noErr)
  4468.             continue;    // it's okay, this flavor is cool
  4469.  
  4470.         return false;    // this item has at least one flavor we don't like
  4471.         }
  4472.  
  4473.     return true;        // all flavors in this item were cool
  4474.  
  4475. } // IsOnlyThisFlavor
  4476.  
  4477. // --------------------------------------------------------------------------------------------------------------
  4478. //
  4479. // IsDropInFinderTrash - Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  4480. //
  4481. #pragma segment Drag
  4482.  
  4483. Boolean IsDropInFinderTrash(AEDesc *dropLocation)
  4484. {
  4485.     OSErr            result;
  4486.     AEDesc            dropSpec;
  4487.     FSSpec            *theSpec;
  4488.     CInfoPBRec        thePB;
  4489.     short            trashVRefNum;
  4490.     long            trashDirID;
  4491.  
  4492.     //    Coerce the dropLocation descriptor into an FSSpec. If there's no dropLocation or
  4493.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  4494.  
  4495.     if ((dropLocation->descriptorType != typeNull) &&
  4496.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) 
  4497.         {
  4498.         unsigned char flags = HGetState(dropSpec.dataHandle);
  4499.         
  4500.         HLock(dropSpec.dataHandle);
  4501.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  4502.  
  4503.         //    Get the directory ID of the given dropLocation object.
  4504.  
  4505.         thePB.dirInfo.ioCompletion = 0L;
  4506.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  4507.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  4508.         thePB.dirInfo.ioFDirIndex = 0;
  4509.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  4510.  
  4511.         result = PBGetCatInfoSync(&thePB);
  4512.  
  4513.         HSetState(dropSpec.dataHandle, flags);
  4514.         AEDisposeDesc(&dropSpec);
  4515.  
  4516.         if (result != noErr)
  4517.             return false;
  4518.  
  4519.         //    If the result is not a directory, it must not be the Trash.
  4520.  
  4521.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  4522.             return false;
  4523.  
  4524.         //    Get information about the Trash folder.
  4525.  
  4526.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  4527.  
  4528.         //    If the directory ID of the dropLocation object is the same as the directory ID
  4529.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  4530.  
  4531.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  4532.             return true;
  4533.         }
  4534.  
  4535.     return false;
  4536.  
  4537. } // IsDropInFinderTrash
  4538.  
  4539. // --------------------------------------------------------------------------------------------------------------
  4540. // APPLE EVENT SUPPORT ROUTINES
  4541. // --------------------------------------------------------------------------------------------------------------
  4542. #pragma segment Main
  4543.  
  4544. static OSErr    MissingParameterCheck(
  4545.     AppleEvent     *inputEvent)
  4546. /*
  4547.     This routine checks an input AppleEvent for the missing keyword.
  4548.     If the missing keyword is found, that means that some required
  4549.     parameters were missing (ie, an error). 
  4550.     
  4551.     However, if the missing keyword isn't found, that means that we aren't missing 
  4552.     any required parameters (that is to say, all REQUIRED parameters were supplied
  4553.     by the person who created the event).
  4554.     
  4555.     SOME DAY, THE ABOVE COMMENT WILL MAKE SENSE TO YOU.  IT STILL DOESN'T
  4556.     TO ME AND I WAS THE ONE WHO WROTE IT.
  4557. */
  4558. {
  4559.     OSErr        anErr;
  4560.     AEKeyword    missingKeyword;
  4561.     DescType    ignoredActualType;
  4562.     Size        ignoredActualSize;
  4563.     
  4564.     anErr = AEGetAttributePtr(
  4565.         inputEvent, 
  4566.         keyMissedKeywordAttr,
  4567.         typeWildCard,
  4568.         &ignoredActualType,
  4569.         (Ptr) &missingKeyword,
  4570.         sizeof(AEKeyword),
  4571.         &ignoredActualSize);
  4572.             
  4573.     if (anErr == noErr)
  4574.         anErr = errAEParamMissed;
  4575.     else
  4576.         if (anErr == errAEDescNotFound)
  4577.             anErr = noErr;
  4578.         
  4579.     return anErr;
  4580.     
  4581. } // MissingParameterCheck
  4582.  
  4583. // --------------------------------------------------------------------------------------------------------------
  4584. // Globals for our handlers
  4585. Boolean gQuitAfterPrint = true;
  4586.  
  4587. // --------------------------------------------------------------------------------------------------------------
  4588. #pragma segment Main
  4589.  
  4590. static pascal OSErr    DoOpenApp(
  4591.     AppleEvent     *inputEvent,
  4592.     AppleEvent     *outputEvent,
  4593.     long        handlerRefCon)
  4594. {
  4595. #pragma unused (outputEvent, handlerRefCon)
  4596.  
  4597.     DoCommand(nil, cNew, 0);
  4598.     gQuitAfterPrint = false;
  4599.     
  4600.     // so that the initial document opens more quickly, we don't start
  4601.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4602.     if (gStarterThread != kNoThreadID)
  4603.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4604.     
  4605.     return(MissingParameterCheck(inputEvent));
  4606.     
  4607. } // DoAppOpen
  4608.  
  4609. // --------------------------------------------------------------------------------------------------------------
  4610. #pragma segment Main
  4611.  
  4612. static pascal OSErr    DoQuitApp(
  4613.     AppleEvent     *inputEvent,
  4614.     AppleEvent     *outputEvent,
  4615.     long        handlerRefCon)
  4616. {
  4617. #pragma unused (outputEvent, handlerRefCon)
  4618.  
  4619.     DoCommand(nil, cQuit, 0);
  4620.  
  4621.     return(MissingParameterCheck(inputEvent));
  4622.     
  4623. } // DoQuitApp
  4624.  
  4625. // --------------------------------------------------------------------------------------------------------------
  4626. #pragma segment Main
  4627.  
  4628. static pascal OSErr    DoOpenOrPrint(
  4629.     AppleEvent     *inputEvent,
  4630.     StringPtr    pPrinterName)    // nil == 0, zero length == print to default, other == printer name
  4631. {
  4632.  
  4633.     OSErr        anErr, anErr2;
  4634.     AEDescList    docList;                // list of docs passed in
  4635.     long        index, itemsInList;
  4636.     void*        hPrint;
  4637.     Boolean        wasAlreadyOpen;
  4638.     
  4639.     anErr = AEGetParamDesc( inputEvent, keyDirectObject, typeAEList, &docList);
  4640.     nrequire(anErr, GetFileList);
  4641.  
  4642.     anErr = AECountItems( &docList, &itemsInList);            // how many files passed in
  4643.     nrequire(anErr, CountDocs);
  4644.     for (index = 1; index <= itemsInList; index++)            // handle each file passed in
  4645.         {    
  4646.         AEKeyword    keywd;
  4647.         DescType    returnedType;
  4648.         Size        actualSize;
  4649.         FSSpec        theFSS;    
  4650.  
  4651.         anErr = AEGetNthPtr( &docList, index, typeFSS, &keywd, &returnedType,    // get file's info
  4652.                             (Ptr)(&theFSS), sizeof(theFSS), &actualSize);
  4653.         nrequire(anErr, AEGetNthPtr);
  4654.         
  4655.         {
  4656.         FInfo    theFileInfo;
  4657.         
  4658.         anErr = FSpGetFInfo(&theFSS, &theFileInfo);
  4659.         if (anErr == noErr)
  4660.             anErr = DetermineWindowTypeOrOpen(&theFSS, theFileInfo.fdType, nil, nil, &wasAlreadyOpen);
  4661.             
  4662.         if (anErr == eDocumentWrongKind)
  4663.             {
  4664.             if (pPrinterName)
  4665.                 ConductErrorDialog(anErr, cPrint, cancel);
  4666.             else
  4667.                 ConductErrorDialog(anErr, cOpen, cancel);
  4668.  
  4669.             anErr = noErr;
  4670.             break;
  4671.             }
  4672.             
  4673.         nrequire(anErr, DetermineWindowTypeOrOpen);
  4674.         }
  4675.         
  4676.         if (pPrinterName)
  4677.             {
  4678.             WindowRef        pWindow = FrontWindow();
  4679.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4680.             
  4681.             if (pData->pPrintPage)
  4682.                 {
  4683.                 if (index == 1)
  4684.                     {
  4685.                     anErr = DoPrintSetup(pWindow, pPrinterName);
  4686.                     if (anErr == noErr)
  4687.                         hPrint = pData->hPrint;
  4688.                     }
  4689.                     
  4690.                 if (anErr == noErr)
  4691.                     anErr = DoPrint(pWindow, hPrint, false);
  4692.                     
  4693.                 if (index != itemsInList)
  4694.                     pData->hPrint = nil;
  4695.                 }
  4696.             
  4697.             if (!wasAlreadyOpen)
  4698.                 DoCloseWindow(pWindow, false);
  4699.  
  4700.             if (anErr != noErr)
  4701.                 break;
  4702.             }
  4703.         }
  4704.  
  4705.     // finally, make sure we didn't miss any parameters
  4706.     anErr2 = MissingParameterCheck(inputEvent);
  4707.     if (anErr == noErr)
  4708.         anErr = anErr2;
  4709.         
  4710. // FALL THROUGH EXCEPTION HANDLING
  4711. DetermineWindowTypeOrOpen:
  4712. AEGetNthPtr:
  4713. CountDocs:
  4714.     // done with doc list
  4715.     (void) AEDisposeDesc( &docList);                        
  4716.     
  4717. GetFileList:
  4718.  
  4719.     // don't report cancels from prints
  4720.     if (pPrinterName)
  4721.         {
  4722.         if ( (anErr == iPrAbort) || (anErr == gxPrUserAbortErr) )
  4723.             anErr = noErr;
  4724.         }
  4725.     
  4726.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  4727.         {
  4728.         if (pPrinterName)
  4729.             ConductErrorDialog(anErr, cPrint, cancel);
  4730.         else
  4731.             ConductErrorDialog(anErr, cOpen, cancel);
  4732.         }
  4733.         
  4734.     return anErr;
  4735.     
  4736. } // DoOpenOrPrint
  4737.  
  4738. // --------------------------------------------------------------------------------------------------------------
  4739. #pragma segment Main
  4740.  
  4741. static pascal OSErr    DoOpenDocument(
  4742.     AppleEvent     *inputEvent,
  4743.     AppleEvent     *outputEvent,
  4744.     long        handlerRefCon)
  4745. {
  4746. #pragma unused (outputEvent, handlerRefCon)
  4747.  
  4748.     OSErr        anErr;
  4749.     
  4750.     if (IsCommandEnabled(cOpen))
  4751.         {
  4752.         gQuitAfterPrint = false;
  4753.         anErr = DoOpenOrPrint(inputEvent, nil);
  4754.         }
  4755.     else
  4756.         {
  4757.         anErr = errAEEventNotHandled;
  4758.         ConductErrorDialog(anErr, cOpen, cancel);
  4759.         }
  4760.         
  4761.     // so that the initial document opens more quickly, we don't start
  4762.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4763.     if (gStarterThread != kNoThreadID)
  4764.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4765.     
  4766.     return anErr;
  4767.     
  4768. } // DoOpenDocument
  4769.  
  4770. // --------------------------------------------------------------------------------------------------------------
  4771. #pragma segment Main
  4772.  
  4773. static pascal OSErr    DoPrintDocument(
  4774.     AppleEvent     *inputEvent,
  4775.     AppleEvent     *outputEvent,
  4776.     long        handlerRefCon)
  4777. {
  4778. #pragma unused (outputEvent, handlerRefCon)
  4779.     OSErr        anErr;
  4780.     FSSpec        printerFSS;
  4781.     AEDescList    dtpList;                // list of docs passed in
  4782.     
  4783.     if (IsCommandEnabled(cOpen))
  4784.         {
  4785.         // try to find out if this doc was dropped onto a printer
  4786.         anErr = AEGetAttributeDesc( inputEvent, keyOptionalKeywordAttr, typeAEList, &dtpList);
  4787.     
  4788.         if (anErr == noErr)                                            // doc dragged to dtp?
  4789.             {
  4790.             AEKeyword    keywd;
  4791.             DescType    returnedType;
  4792.             Size        actualSize;
  4793.     
  4794.             anErr = AEGetNthPtr( &dtpList, 1, typeFSS, &keywd, &returnedType,    // get dtp info
  4795.                             (Ptr)(&printerFSS), sizeof(printerFSS), &actualSize);
  4796.             }
  4797.             
  4798.         // if it wasn't, that's not an error, just print normally
  4799.         if (anErr != noErr)
  4800.             {
  4801.             printerFSS.name[0] = 0;
  4802.             anErr = noErr;
  4803.             }
  4804.             
  4805.         anErr = DoOpenOrPrint(inputEvent, &printerFSS.name[0]);
  4806.         
  4807.         // if we are opened just for printing -- quit afterwards
  4808.         if (gQuitAfterPrint)
  4809.             DoCommand(nil, cQuit, 0);
  4810.         }
  4811.     else
  4812.         {
  4813.         anErr = errAEEventNotHandled;
  4814.         ConductErrorDialog(anErr, cPrint, cancel);
  4815.         }
  4816.         
  4817.     return anErr;
  4818.     
  4819. } // DoPrintDocument
  4820.  
  4821. #if GENERATINGCFM
  4822.     static RoutineDescriptor    gDoOpenAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenApp);
  4823.     static AEEventHandlerUPP    gDoOpenApp = &gDoOpenAppRD;
  4824.  
  4825.     static RoutineDescriptor    gDoQuitAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoQuitApp);
  4826.     static AEEventHandlerUPP    gDoQuitApp = &gDoQuitAppRD;
  4827.  
  4828.     static RoutineDescriptor    gDoOpenDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenDocument);
  4829.     static AEEventHandlerUPP    gDoOpenDocument = &gDoOpenDocumentRD;
  4830.  
  4831.     static RoutineDescriptor    gDoPrintDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoPrintDocument);
  4832.     static AEEventHandlerUPP    gDoPrintDocument = &gDoPrintDocumentRD;
  4833. #else
  4834.     static AEEventHandlerUPP    gDoOpenApp = (AEEventHandlerUPP) DoOpenApp;
  4835.     static AEEventHandlerUPP    gDoQuitApp = (AEEventHandlerUPP) DoQuitApp;
  4836.     static AEEventHandlerUPP    gDoOpenDocument = (AEEventHandlerUPP) DoOpenDocument;
  4837.     static AEEventHandlerUPP    gDoPrintDocument = (AEEventHandlerUPP) DoPrintDocument;
  4838. #endif
  4839. // --------------------------------------------------------------------------------------------------------------
  4840. #pragma segment Main
  4841.  
  4842. static pascal OSErr SimpleTextCoachHandler(Rect *pRect, Ptr name, long refCon)
  4843. {
  4844. #pragma unused (refCon)
  4845.  
  4846.     OSErr            anErr = noErr;
  4847.     WindowRef        pWindow = FrontWindow();
  4848.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  4849.     
  4850.     if ((pData) && (pData->pGetCoachRectangle))
  4851.         anErr = (*(pData->pGetCoachRectangle)) (pWindow, pData, pRect, name);
  4852.         
  4853.     return(anErr);
  4854.     
  4855. } // SimpleTextCoachHandler
  4856.  
  4857. // --------------------------------------------------------------------------------------------------------------
  4858. // MAIN INITIALIZE/SHUTDOWN/LOOP ROUTINES
  4859. // --------------------------------------------------------------------------------------------------------------
  4860. #pragma segment Main
  4861.  
  4862. static pascal void* StarterThread(void* threadParam)
  4863. {
  4864.     #pragma unused(threadParam)
  4865.     
  4866.     /*
  4867.         All threads, including the starter thread, are initially created
  4868.         in a suspended state. The starter thread is made ready to run when
  4869.         we get an open application or open document event. It runs until
  4870.         there are no activate or update events pending, and then starts the
  4871.         font menu and Apple Guide threads. This gives much better performance
  4872.         for the initial creation and update of a document, because the threads
  4873.         (especially the font thread) chew up a lot of time at first.
  4874.         
  4875.         The starter thread isn't really necessary - we could get the same
  4876.         effect by just checking in the event loop for activate/update events -
  4877.         but hey, it's a lot easier to do it this way, doesn't cost much, and
  4878.         isn't that what threads are for?
  4879.     */
  4880.     
  4881.     for (;;)
  4882.         {
  4883.         EventRecord        er;
  4884.     
  4885.         YieldToAnyThread();
  4886.         
  4887.         if (!EventAvail(activMask | updateMask, &er))
  4888.             {
  4889.             if (gFontThread != kNoThreadID)
  4890.                 SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4891.             if (gAGThread != kNoThreadID)
  4892.                 SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4893.             
  4894.             break;
  4895.             }
  4896.         }
  4897.     
  4898.     gStarterThread = kNoThreadID;
  4899.     return NULL;
  4900.     
  4901. } // StarterThread
  4902.  
  4903. // --------------------------------------------------------------------------------------------------------------
  4904. #pragma segment Initialize
  4905.  
  4906. static OSErr CreateThread(ThreadEntryProcPtr pThread, void* threadParam, ThreadID* ptid)
  4907. {
  4908.     OSErr    anErr;
  4909.     
  4910.     anErr = NewThread(kCooperativeThread, pThread, threadParam, 0, kNewSuspend,
  4911.         &gThreadResults, ptid);
  4912.         
  4913.     if (anErr == noErr && gStarterThread == kNoThreadID)
  4914.         {
  4915.         anErr = NewThread(kCooperativeThread, StarterThread, NULL, 0, kNewSuspend,
  4916.             &gThreadResults, &gStarterThread);
  4917.         if (anErr != noErr)
  4918.             DisposeThread(*ptid, &gThreadResults, false);
  4919.             // anErr remains != noErr
  4920.         }
  4921.         
  4922.     return anErr;
  4923. }
  4924.         
  4925. // --------------------------------------------------------------------------------------------------------------
  4926. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4927. // while some other thread is running
  4928. #pragma segment Main
  4929.  
  4930. static OSErr FindSimpleTextGuideFile(FSSpec *pSpec)
  4931. {
  4932.     OSErr    anErr = fnfErr;
  4933.     short    numDBs, index;
  4934.     short    folderIndex;
  4935.     
  4936.     for (folderIndex = 0; folderIndex < 3; ++folderIndex)
  4937.         {
  4938.         switch (folderIndex)
  4939.             {
  4940.             case 0:
  4941.                 FindFolder(-1, kPreferencesFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4942.                 break;
  4943.  
  4944.             case 1:
  4945.                 FindFolder(-1, kExtensionFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4946.                 break;
  4947.  
  4948.             case 2:
  4949.                 FindFolder(-1, kSystemFolderType, false, &pSpec->vRefNum, &pSpec->parID);
  4950.                 break;
  4951.             }
  4952.         
  4953.         numDBs = AGFileGetDBCount(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false);
  4954.         
  4955.         if (!gDontYield)
  4956.             YieldToAnyThread();
  4957.         
  4958.         for (index = 0; index < numDBs; ++index)
  4959.             {
  4960.             if (!gDontYield)
  4961.                 YieldToAnyThread();
  4962.             
  4963.             if (AGFileGetIndDB(pSpec->vRefNum, pSpec->parID, kAGFileDBTypeAny, false, index+1, pSpec) == noErr)
  4964.                 {
  4965.                 OSType    creator;
  4966.                 
  4967.                 if ((AGFileGetHelpMenuAppCreator(pSpec, &creator) == noErr) && (creator == 'ttxt'))
  4968.                     return(noErr);
  4969.                 }
  4970.             }
  4971.         }
  4972.     
  4973.     return(anErr);
  4974.     
  4975. } // FindSimpleTextGuideFile
  4976.  
  4977. // --------------------------------------------------------------------------------------------------------------
  4978. // must be in Main because it runs in a thread and we don't want the segment unloaded
  4979. // while some other thread is running
  4980. #pragma segment Main
  4981.  
  4982. static pascal void* AGThread(void *threadParam)
  4983. {
  4984.     #pragma unused(threadParam)
  4985.     
  4986.     if ( !(AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  4987.         {
  4988.         // if we find one, add it to the help menu and add the right command
  4989.         if (FindSimpleTextGuideFile(&gAGSpec) == noErr)
  4990.             {
  4991.             MenuHandle    helpMenu;
  4992.                         
  4993.             if (HMGetHelpMenuHandle(&helpMenu) == noErr)
  4994.                 {
  4995.                 Str255    tempString;
  4996.                 short    rnSave;
  4997.                 
  4998.                 AGFileGetDBMenuName(&gAGSpec, tempString);
  4999.                 AppendMenu(helpMenu, tempString);
  5000.                 
  5001.                 // since we're in a separate thread, so other resource file may
  5002.                 // be open in front of SimpleText's resource fork
  5003.                 rnSave = CurResFile();
  5004.                 UseResFile(gApplicationResFile);
  5005.                 
  5006.                 GetIndString(tempString, kMiscStrings, iHelpMenuCommand);
  5007.                 
  5008.                 UseResFile(rnSave);
  5009.                 
  5010.                 if (tempString[0] != 0)
  5011.                     SetItemCmd(helpMenu, CountMItems(helpMenu), tempString[1]);
  5012.                 }
  5013.             }
  5014.         }
  5015.     
  5016.     // if we have a database, then install a coach handler
  5017.     if ( (AGGetAvailableDBTypes() & kAGDBTypeBitAny) )
  5018.         {
  5019.         AGInstallCoachHandler(NewCoachReplyProc(SimpleTextCoachHandler), 0, &gAGCoachRefNum);
  5020.         }
  5021.     
  5022.     gAGThread = kNoThreadID;
  5023.     return NULL;
  5024.     
  5025. } // AGThread
  5026.  
  5027. // --------------------------------------------------------------------------------------------------------------
  5028. #pragma segment Initialize
  5029.  
  5030. static void FindAndInstallSimpleTextGuide(void)
  5031. {
  5032.     OSErr    anErr = fnfErr;
  5033.     
  5034.     if (gMachineInfo.haveThreads)
  5035.         anErr = CreateThread(AGThread, NULL, &gAGThread);
  5036.     
  5037.     if (anErr != noErr)
  5038.         AGThread(NULL);
  5039.         
  5040. } // FindAndinstallSimpleTextGuide
  5041.  
  5042. // --------------------------------------------------------------------------------------------------------------
  5043. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5044. // while some other thread is running
  5045. #pragma segment Main
  5046.  
  5047. static long SortAndAddMenu(MenuHandle menu, Str255 newItem)
  5048. {
  5049.     short    numInMenu = CountMItems(menu);
  5050.     short    i;
  5051.     Str255    oldItem;
  5052.     
  5053.     for (i = 1; i <= numInMenu; ++i)
  5054.         {
  5055.         GetMenuItemText(menu, i, oldItem);
  5056.         switch(IUCompString(newItem, oldItem))
  5057.             {
  5058.             // already in?  Return index
  5059.             case 0:
  5060.                 return(i);
  5061.                 break;
  5062.                 
  5063.             // less than, keep scanning
  5064.             case 1:
  5065.                 break;
  5066.                 
  5067.             // greater than, add back one
  5068.             case -1:
  5069.                 InsertMenuItem(menu, "\pTom Dowdy", i-1);
  5070.                 SetMenuItemText(menu, i, newItem);
  5071.                 return(i);
  5072.                 break;
  5073.             }
  5074.         }
  5075.         
  5076.     // fall off the end?  add at the end
  5077.     InsertMenuItem(menu, "\pTom Dowdy", numInMenu);
  5078.     SetMenuItemText(menu, numInMenu+1, newItem);
  5079.             
  5080.     return(numInMenu+1);
  5081.     
  5082. } // SortAndAddMenu
  5083.  
  5084. // --------------------------------------------------------------------------------------------------------------
  5085. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5086. // while some other thread is running
  5087. #pragma segment Main
  5088.  
  5089. static void AddToGlobalList(gxFont fontID, short fond, Style styleBits)
  5090. {
  5091.     if (!gFontMappingList)
  5092.         gFontMappingList = (FontMappingHandle)NewHandle(0);
  5093.         
  5094.     if (gFontMappingList)
  5095.         {
  5096.         Size            oldSize = GetHandleSize((Handle) gFontMappingList);
  5097.         FontMappingPtr    pList;
  5098.         
  5099.         SetHandleSize((Handle)gFontMappingList, oldSize + sizeof(FontMappingRecord));
  5100.         pList = &(*gFontMappingList)[oldSize/sizeof(FontMappingRecord)];
  5101.         pList->fontID = fontID;
  5102.         pList->qdFont = fond;
  5103.         pList->qdStyle = styleBits;
  5104.         }
  5105.         
  5106. } // AddToGlobalList
  5107.  
  5108. // --------------------------------------------------------------------------------------------------------------
  5109. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5110. // while some other thread is running
  5111. #pragma segment Main
  5112.  
  5113. static void AddEachEntry(Handle fond, short* sp, short entries, gxStyle theStyle, MenuHandle menu, short* pStylesUsed)
  5114. {
  5115.     gxFont            fontID;
  5116.     long            length;
  5117.     short             resID;
  5118.     OSType             resType;
  5119.     Str255             resName;
  5120.     Style            styleBits;
  5121.     
  5122.     GetResInfo(fond, &resID, &resType, resName);
  5123.     
  5124.     for (; entries >= 0; --entries)
  5125.         {   
  5126.         if (*sp == 0)
  5127.             {   
  5128.             styleBits = sp[1];
  5129.  
  5130.             // map the font ID and the style bits for this style into GX space
  5131.             (void)GXConvertQDFont(theStyle, resID, styleBits);
  5132.             
  5133.             if (!gDontYield)
  5134.                 YieldToAnyThread();
  5135.     
  5136.             fontID = GXGetStyleFont(theStyle);
  5137.             if (fontID)
  5138.                 length = GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5139.             else
  5140.                 length = 0;
  5141.                 
  5142.             if (!gDontYield)
  5143.                 YieldToAnyThread();
  5144.     
  5145.             if (length)
  5146.                 {
  5147.                 unsigned char     * name;
  5148.                 short            where;
  5149.                 
  5150.                 name = (unsigned char*) NewPtr(length+1);
  5151.                 if (name)
  5152.                     {
  5153.                     MenuHandle    subMenu;
  5154.                     short        mark;
  5155.  
  5156.                     // add this font to the list we know about
  5157.                     AddToGlobalList(fontID, resID, styleBits);
  5158.  
  5159.                     // find the family name for this font
  5160.                     GXFindFontName(fontID, gxFamilyFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5161.                     name[0] = length;
  5162.                     
  5163.                     // add the name if new. Then add sub menus
  5164.                     where = SortAndAddMenu(menu, name);
  5165.                     
  5166.                     // do we already have a sub menu?  Or does this font have multiple styles?
  5167.                     GetItemMark(menu, where, &mark);
  5168.                     if ( (mark != 0) || (GXFindFonts(fontID, 0, 0, 0, 0, 0, nil, 1, gxSelectToEnd, nil) > 1) )
  5169.                         {
  5170.                         if (!gDontYield)
  5171.                             YieldToAnyThread();
  5172.     
  5173.                         // make a new menu or grab the old one
  5174.                         if (mark == 0)
  5175.                             subMenu = NewMenu(mFontSubMenusStart + *pStylesUsed, name);
  5176.                         else
  5177.                             subMenu = GetMenuHandle(mark);
  5178.                         DisposePtr((Ptr) name);
  5179.                         
  5180.                         length = GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, nil, nil);
  5181.                         if (length)
  5182.                             {
  5183.                             name = (unsigned char*) NewPtr(length+1);
  5184.                             if (name)
  5185.                                 {
  5186.                                 GXFindFontName(fontID, gxStyleFontName, gxMacintoshPlatform, gxRomanScript, gxEnglishLanguage, &name[1], nil);
  5187.                                 name[0] = length;
  5188.                                 (void) SortAndAddMenu(subMenu, name);
  5189.                                 DisposePtr((Ptr) name);
  5190.                                 }
  5191.                             }
  5192.                         
  5193.                         // if new menu, add to the master
  5194.                         if (mark == 0)
  5195.                             {
  5196.                             InsertMenu(subMenu, -1);
  5197.                             SetItemCmd(menu, where, hMenuCmd);
  5198.                             SetItemMark(menu, where, mFontSubMenusStart + *pStylesUsed);
  5199.                             (*pStylesUsed)++;
  5200.                             }
  5201.                         }
  5202.                     else
  5203.                         DisposePtr((Ptr) name);
  5204.  
  5205.                     }
  5206.                 }
  5207.                 
  5208.             } // if (*sp == 0)
  5209.             
  5210.         sp += 3;        /* three elements in the FAT */
  5211.         
  5212.         if (!gDontYield)
  5213.             YieldToAnyThread();
  5214.  
  5215.         } // for (# entries)
  5216. } // AddEachEntry
  5217.  
  5218. // --------------------------------------------------------------------------------------------------------------
  5219. // must be in Main because it runs in a thread and we don't want the segment unloaded
  5220. // while some other thread is running
  5221. #pragma segment Main
  5222.  
  5223. static pascal void* FontsThread(void *threadParam)
  5224. {
  5225.     MenuHandle    menu = (MenuHandle) threadParam;
  5226.     long        numberFonts;
  5227.     long        i;
  5228.     short        stylesUsed = 0;
  5229.     gxStyle        theStyle;
  5230.     Boolean        menusAdjusted = false;
  5231.     
  5232.     theStyle = GXNewStyle();
  5233.     
  5234.     numberFonts = CountResources('FOND');
  5235.     for (i = 1; i <= numberFonts; ++i)
  5236.         {
  5237.         Handle     fond = GetIndResource('FOND', i);
  5238.         
  5239.         if (!ResError() && fond && *fond)
  5240.             do
  5241.             {   
  5242.             short* sp = (short*)(*fond + sizeof(FamRec));
  5243.             short entries = *sp++;
  5244.             
  5245.             AddEachEntry(fond, sp, entries, theStyle, menu, &stylesUsed);
  5246.             
  5247.             // now that there are some fonts in the font menu, make sure the menu's enabled
  5248.             if (!menusAdjusted && CountMItems(menu) > 0)
  5249.                 {
  5250.                 AdjustMenus(FrontWindow(), true, false);
  5251.                 menusAdjusted = true;
  5252.                 }
  5253.                 
  5254.             } while ((fond = GetNextFOND(fond)) != 0);
  5255.             
  5256.         } // for (# fonts)
  5257.  
  5258.     GXDisposeStyle(theStyle);
  5259.     
  5260.     gFontThread = kNoThreadID;
  5261.     return 0;
  5262. } // FontsThread
  5263.  
  5264. // --------------------------------------------------------------------------------------------------------------
  5265. #pragma segment Initialize
  5266.  
  5267. static OSErr BuildFontMenu(MenuHandle menu)
  5268. {
  5269.     OSErr    anErr = noErr;
  5270.     
  5271.     if (gMachineInfo.haveGX)
  5272.         {
  5273.         (void) DoStartupGX();
  5274.         
  5275.         // prime the font cache so we don't spend time doing this in the font thread
  5276.         GXGetDefaultFont();
  5277.         
  5278.         anErr = paramErr;
  5279.         if (gMachineInfo.haveThreads)
  5280.             anErr = CreateThread(FontsThread, menu, &gFontThread);
  5281.  
  5282.         if (anErr != noErr)
  5283.             {
  5284.             FontsThread(menu);
  5285.             anErr = noErr;
  5286.             }
  5287.         }
  5288.     else
  5289.         AppendResMenu(menu, 'FONT');
  5290.     
  5291.     return(anErr);
  5292.     
  5293. } // BuildFontMenu
  5294.  
  5295. // --------------------------------------------------------------------------------------------------------------
  5296. #pragma segment Initialize
  5297.  
  5298. static OSErr    DoInitialize(void)
  5299. {
  5300.     short                count;            // loop counter
  5301.     Handle                menuBar;        // for loading our menus in
  5302.     gxGraphicsError        anErr = noErr;    // any errors we get, none so far
  5303.     long                version;        // version for Gestalt calls
  5304.     
  5305.     InitGraf((Ptr) &qd.thePort);
  5306.     InitFonts();
  5307.     InitWindows();
  5308.     InitMenus();
  5309.     TEInit();
  5310.     InitDialogs(nil);
  5311.     InitCursor();
  5312.     
  5313.     gAllDone = false;
  5314.     
  5315.     // check that the system is correct to handle things
  5316.     SysEnvirons(1, &gMachineInfo.theEnvirons);
  5317.     if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  5318.         {
  5319.         // Wait for app to come to front
  5320.         for (count = 1; count <= 3; ++count)
  5321.             EventAvail(everyEvent, &gEvent);
  5322.             
  5323.         anErr = eMachineToOld;
  5324.         nrequire(anErr, SysEnvirons);
  5325.         }
  5326.  
  5327.     gMachineInfo.lastBalloonIndex = iNoBalloon;
  5328.     gMachineInfo.amInBackground = false;
  5329.     gMachineInfo.documentCount  = 1;
  5330.     gMachineInfo.haveQuickTime     = (Gestalt(gestaltQuickTime, &version) == noErr);
  5331.     gMachineInfo.haveRecording     = (Gestalt(gestaltSoundAttr, &version) == noErr) && ((version & (1<<gestaltHasSoundInputDevice)) != 0);
  5332.     gMachineInfo.haveTTS         = (Gestalt(gestaltSpeechAttr, &version) == noErr) && ((version & (1<<gestaltSpeechMgrPresent)) != 0);
  5333.     gMachineInfo.haveGX            = (Gestalt(gestaltGXVersion, &version) == noErr);
  5334.     gMachineInfo.haveTSM         = (Gestalt(gestaltTSMgrVersion, &version) == noErr) && (version >= 1);
  5335.     gMachineInfo.haveTSMTE         = (Gestalt(gestaltTSMTEAttr, &version) == noErr) && ((version & (1<<gestaltTSMTE)) != 0);
  5336.     gMachineInfo.haveDragMgr    = (Gestalt(gestaltDragMgrAttr, &version) == noErr) && ((version & (1<<gestaltDragMgrPresent)) != 0) &&
  5337.                                     (Gestalt(gestaltTEAttr, &version) == noErr) && ((version & (1<<gestaltTEHasGetHiliteRgn)) != 0);
  5338.     gMachineInfo.haveThreeD        = false;
  5339.     gMachineInfo.haveAppleGuide    = (Gestalt(gestaltHelpMgrAttr, &version) == noErr) && ((version & (1<<gestaltAppleGuidePresent)) != 0);
  5340.     gMachineInfo.haveThreads    = (Gestalt(gestaltThreadMgrAttr, &version) == noErr) && ((version & (1<<gestaltThreadMgrPresent)) != 0);
  5341.     
  5342.     gMachineInfo.haveNavigationServices  = NavServicesAvailable();
  5343.     
  5344.     if ( gMachineInfo.haveNavigationServices ) 
  5345.     {
  5346.         NavLoad();
  5347.     }
  5348.     
  5349.     #if GENERATINGPOWERPC
  5350.         {
  5351.         CFragConnectionID    connID;
  5352.         Ptr                 mainAddr;
  5353.         Str255                errName;
  5354.         
  5355.         if ( (gMachineInfo.haveQuickTime)     && (GetSharedLibrary("\pQuickTimeLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5356.             gMachineInfo.haveQuickTime = false;
  5357.         if ( (gMachineInfo.haveTTS)         && (GetSharedLibrary("\pSpeechLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5358.             gMachineInfo.haveTTS = false;
  5359.         if ( (gMachineInfo.haveGX)             && (GetSharedLibrary("\pQuickDrawGXLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5360.             gMachineInfo.haveGX = false;
  5361.         if ( (gMachineInfo.haveDragMgr)        && (GetSharedLibrary("\pDragLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5362.             gMachineInfo.haveDragMgr = false;
  5363.         if ( (gMachineInfo.haveThreads)     && (GetSharedLibrary("\pThreadsLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  5364.             gMachineInfo.haveThreads = false;
  5365.         }
  5366.     #endif
  5367.     
  5368.  
  5369.     // initialize text services if they exist
  5370.     if (gMachineInfo.haveTSMTE)
  5371.         {
  5372.         if (InitTSMAwareApplication() != noErr)
  5373.             {
  5374.             gMachineInfo.haveTSM = false;
  5375.             gMachineInfo.haveTSMTE = false;
  5376.             }
  5377.         }
  5378.         
  5379.     // save away info we need from the get-go    
  5380.     gApplicationResFile = CurResFile();
  5381.     gCursorRgn = NewRgn();
  5382.  
  5383.     // load up the menus
  5384.     menuBar = (Handle) GetNewMBar(rMenuBar);            /* read menus into menu bar */
  5385.     anErr = ResError();
  5386.     if ( (anErr == noErr) && (menuBar == nil) )
  5387.         anErr = resNotFound;
  5388.     nrequire(anErr, GetNewMBar);
  5389.     
  5390.     // install menus
  5391.     SetMenuBar(menuBar);    
  5392.     DisposeHandle(menuBar);
  5393.  
  5394.     // build the Apple menu
  5395.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  5396.     
  5397.     // haven't yet done a startup of QuickDraw GX
  5398.     if (gMachineInfo.haveGX)
  5399.         gMachineInfo.haveStartedGX = false;
  5400.  
  5401.     // Build the font menu
  5402.     anErr = BuildFontMenu(GetMenuHandle(mFont));
  5403.     nrequire(anErr, BuildFontMenu);
  5404.     
  5405.     // insert our heirarchical menus
  5406.     {
  5407.     MenuHandle     menu = GetMenu( mVoices );
  5408.     short        menuID, itemID;
  5409.     
  5410.     InsertMenu( menu, hierMenu );
  5411.     
  5412.     CommandToIDs(cSelectVoice, &menuID, &itemID);
  5413.     menu = GetMenuHandle(menuID);
  5414.  
  5415.     SetItemCmd( menu, itemID, hMenuCmd );
  5416.     SetItemMark( menu, itemID, mVoices );
  5417.     }
  5418.  
  5419.     AdjustMenus(nil, true, false);
  5420.     DrawMenuBar();
  5421.     
  5422.     // start up QuickTime, but problems result in us pretending not to have it
  5423.     if (gMachineInfo.haveQuickTime)
  5424.         if (EnterMovies() != noErr)
  5425.             gMachineInfo.haveQuickTime = false;
  5426.         
  5427.     // Install AppleEvent handlers for the base classes
  5428.  
  5429.     #define INSTALL(event, handler) \
  5430.             AEInstallEventHandler(kCoreEventClass, event, handler, 0, false)
  5431.  
  5432.     INSTALL (kAEOpenApplication, gDoOpenApp);
  5433.     INSTALL (kAEQuitApplication, gDoQuitApp);
  5434.     INSTALL (kAEOpenDocuments,   gDoOpenDocument);
  5435.     INSTALL (kAEPrintDocuments,  gDoPrintDocument);
  5436.  
  5437.     #undef INSTALL
  5438.  
  5439.     // Install our global dragging procs, but only if we have Drag and Drop. An error results
  5440.     // in us pretending that we don't have drag support. Notice that in the test above, we also
  5441.     // require TextEdit to have TEGetHiliteRgn avalilable, which is always the case with the
  5442.     // present Drag Manager.
  5443.  
  5444.     if (gMachineInfo.haveDragMgr)
  5445.         {
  5446.         gGlobalTrackingHandler = NewDragTrackingHandlerProc(GlobalTrackingHandler);
  5447.         gGlobalReceiveHandler = NewDragReceiveHandlerProc(GlobalReceiveHandler);
  5448.         
  5449.         anErr = InstallTrackingHandler(gGlobalTrackingHandler, nil, nil);
  5450.  
  5451.         if (anErr == noErr)
  5452.             {
  5453.             anErr = InstallReceiveHandler(gGlobalReceiveHandler, nil, nil);
  5454.  
  5455.             if (anErr != noErr)
  5456.                 {
  5457.                 RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5458.                 gMachineInfo.haveDragMgr = false;
  5459.                 }
  5460.             }
  5461.         else
  5462.             gMachineInfo.haveDragMgr = false;
  5463.         }
  5464.  
  5465.  
  5466.     // verify that the AppleGuide database is available, and if it isn't, try to find
  5467.     // it from other places -- but don't bother on 2.1, because they made changes
  5468.     // to break our location finding (sigh!)
  5469.     if  (
  5470.         (gMachineInfo.haveAppleGuide) && 
  5471.         ( (Gestalt('ag_v', &version) != noErr) || (version < 0x00000210) )
  5472.         )
  5473.         FindAndInstallSimpleTextGuide();
  5474.         
  5475.  
  5476.     return noErr;
  5477.     
  5478.     
  5479. // EXCEPTION HANDLING
  5480. BuildFontMenu:
  5481. GetNewMBar:
  5482. SysEnvirons:
  5483.     ConductErrorDialog(anErr, cNull, cancel);
  5484.     
  5485.     return anErr;
  5486.  
  5487. } // DoInitialize
  5488.  
  5489. // --------------------------------------------------------------------------------------------------------------
  5490. #pragma segment Terminate
  5491.  
  5492. static OSErr    DoTerminate(void)
  5493. {
  5494.     OSErr    anErr = noErr;
  5495.     
  5496.     if (gFontThread != kNoThreadID)
  5497.         DisposeThread(gFontThread, &gThreadResults, false);
  5498.     if (gAGThread != kNoThreadID)
  5499.         DisposeThread(gAGThread, &gThreadResults, false);
  5500.     if (gStarterThread != kNoThreadID)
  5501.         DisposeThread(gStarterThread, &gThreadResults, false);
  5502.  
  5503.     if (gMachineInfo.haveQuickTime)
  5504.         ExitMovies();
  5505.  
  5506.     if ( (gMachineInfo.haveGX) && (GXGetGraphicsClient() != nil) )
  5507.         {
  5508.         GXExitPrinting();
  5509.         GXExitGraphics();
  5510.         }
  5511.         
  5512.     if (gMachineInfo.haveTSMTE)
  5513.         CloseTSMAwareApplication();
  5514.  
  5515.     if (gMachineInfo.haveDragMgr)
  5516.         {
  5517.         RemoveReceiveHandler(gGlobalReceiveHandler, nil);
  5518.         RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  5519.         }
  5520.         
  5521.     if (gMachineInfo.haveAppleGuide)
  5522.         {
  5523.         if ((gAGRefNum != -1) && AGIsDatabaseOpen(gAGRefNum))
  5524.             {
  5525.             AGClose(&gAGRefNum);
  5526.             gAGRefNum = -1;
  5527.             }
  5528.         if (gAGCoachRefNum != -1)
  5529.             AGRemoveCoachHandler(&gAGCoachRefNum);
  5530.         }
  5531.  
  5532.     if ( gMachineInfo.haveNavigationServices ) 
  5533.     {
  5534.         NavUnload();
  5535.     }
  5536.  
  5537.     return anErr;
  5538.     
  5539. } // DoTerminate
  5540.  
  5541. // --------------------------------------------------------------------------------------------------------------
  5542. #pragma segment Main
  5543.  
  5544. main(void)
  5545. {
  5546.     OSErr    anErr;
  5547.     
  5548. #ifndef __MWERKS__
  5549.     UnloadSeg((Ptr) _DataInit);                        /* note that _DataInit must not be in Main! */
  5550. #endif
  5551.     MaxApplZone();                                    /* expand the heap so code segments load at the top */
  5552.     MoreMasters(); MoreMasters(); MoreMasters();     /* we love handles */
  5553.     anErr = DoInitialize();
  5554.     UnloadSeg((Ptr) DoInitialize);                    
  5555.     if (anErr == noErr)
  5556.         {
  5557.         DoEventLoop();
  5558.  
  5559. // REVIEW: don't want to unload the segment we're in!!
  5560. //        UnloadSeg((Ptr) DoEventLoop);
  5561.         DoTerminate();                    
  5562.         }
  5563.     return 0;
  5564. } // main
  5565.