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

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** File:        Window.c
  5. ** Written by:    Eric Soldan
  6. **
  7. ** Copyright © 1990-1993 Apple Computer, Inc.
  8. ** All rights reserved.
  9. */
  10.  
  11. /* You may incorporate this sample code into your applications without
  12. ** restriction, though the sample code has been provided "AS IS" and the
  13. ** responsibility for its operation is 100% yours.  However, what you are
  14. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  15. ** after having made changes. If you're going to re-distribute the source,
  16. ** we require that you make it clear in the source that the code was
  17. ** descended from Apple Sample Code, but that you've made changes. */
  18.  
  19. /* This file contains the code for the document procedure pointers for the main DTS.Draw
  20. ** document.  DTS.Draw currently only supports one type of file-based documents,
  21. ** type 'DDOC'. */
  22.  
  23. /* For more information on this file, please read the read.me file "=How to write your app". */ 
  24.  
  25.  
  26.  
  27. /*****************************************************************************/
  28.  
  29.  
  30.  
  31. #include "App.h"            /* Get the application includes/typedefs, etc.    */
  32. #include "App.defs.h"        /* Get various application definitions.            */
  33. #include "App.protos.h"        /* Get the prototypes for application.            */
  34.  
  35. #ifndef __ERRORS__
  36. #include <Errors.h>
  37. #endif
  38.  
  39. #ifndef __FONTS__
  40. #include <Fonts.h>
  41. #endif
  42.  
  43. #ifndef __RESOURCES__
  44. #include <Resources.h>
  45. #endif
  46.  
  47. #ifndef __TOOLUTILS__
  48. #include <ToolUtils.h>
  49. #endif
  50.  
  51. #ifndef __TREEOBJ2__
  52. #include "TreeObj2.h"
  53. #endif
  54.  
  55. #ifndef __UTILITIES__
  56. #include "Utilities.h"
  57. #endif
  58.  
  59.  
  60.  
  61. /*****************************************************************************/
  62.  
  63.  
  64.  
  65. typedef struct {
  66.     TreeObjHndl    root;
  67.     short        cnum;
  68. } LayerDrawInfo;
  69.  
  70. Boolean            gNoDefaultDocument = false;
  71.                     /* Set to true if app should boot with no default document. */
  72.                     /* This tells DTS.Lib..framework what you want. */
  73.  
  74. OSType            gAppWindowType = kDocFileType;    /* Main document type. */
  75. long            gAppWindowAttr = kwAppWindow;    /* Main window attributes. */
  76. long            gToolPaletteMenuFlags;            /* Remember what items are enabled at startup. */
  77.  
  78. short            gMinVersion = kMinVersion;    /* Minimum document version app can support. */
  79. short            gMaxVersion = kMaxVersion;    /* Maximum document version app can support. */
  80.                                             /* More informing DTS.Lib..framework. */
  81.  
  82. extern short    gPrintPage;                    /* Non-zero means we are printing. */
  83.                                             /* DTS.Lib..framework global. */
  84.  
  85. extern RgnHandle    gCursorRgn;                /* We handle cursors here, so we need */
  86. extern CursPtr        gCursorPtr;                /* to know about these things. */
  87.                                             /* Above are DTS.Lib..framework globals. */
  88.  
  89. static void        AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click);
  90. static void        SlideSelection(FileRecHndl frHndl, ClickInfo *click);
  91. static OSErr    WindowLayerProc(LayerObj theLayer, short message);
  92. static OSErr    WorkLayerProc(LayerObj theLayer, short message);
  93. static OSErr    BackLayerProc(LayerObj theLayer, short message);
  94. static void        DrawPageGrid(void);
  95.  
  96. #define kDataAreaPadding 0
  97.  
  98.  
  99. /*****************************************************************************/
  100. /*****************************************************************************/
  101.  
  102.  
  103.  
  104. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  105.  
  106. /* Calculate application specific frame area (Called by DoCalcFrameRgn).
  107. ** You are passed an empty region.  You are supposed to add any custom frame
  108. ** parts that this document uses.  Typically there are no frame portions, as
  109. ** they are accounted for in other ways.  The scrollbars and grow icon will
  110. ** automatically be contributed to the calculation of the frame region.
  111. ** If you use sidebars, these are also added in automatically.  This is only
  112. ** used if the frame region is more complicated than can automatically be
  113. ** handled.  So, almost always, you will simply leave the region empty. */
  114.  
  115. #pragma segment TheDoc
  116. void    CalcFrameRgn(FileRecHndl frHndl, WindowPtr window, RgnHandle rgn)
  117. {
  118. #ifndef __MWERKS__
  119. #pragma unused (frHndl, window, rgn)
  120. #endif
  121. }
  122.  
  123.  
  124.  
  125. /*****************************************************************************/
  126.  
  127.  
  128.  
  129. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  130.  
  131. /* This is called (by DoContentClick()) when a mouse-down event occurs in the content of
  132. ** a window.  Other applications might want to call FindControl, TEClick, etc., to
  133. ** further process the click. */
  134.  
  135. #pragma segment TheDoc
  136. void    ContentClick(WindowPtr window, EventRecord *event, Boolean firstClick)
  137. {
  138. #ifndef __MWERKS__
  139. #pragma unused (firstClick)
  140. #endif
  141.  
  142.     FileRecHndl        frHndl;
  143.     ControlHandle    ctl;
  144.     short            dblClick, shiftMod;
  145.     Point            contOrg;
  146.     Boolean            selected;
  147.     TreeObjHndl        hitObj, root;
  148.     ClickInfo        click;
  149.  
  150.     SetPort(window);
  151.  
  152.     if (IsCtlEvent(window, event, &ctl, &dblClick)) return;
  153.         /* That was easy.  Scrolling was just handled.  Other stuff would be handled
  154.         ** by IsCtlEvent, if we had other stuff to do.  We don't have any other
  155.         ** controls in the content besides the document scrollbars. */
  156.  
  157.     frHndl = (FileRecHndl)GetWRefCon(window);
  158.     if ((*frHndl)->fileState.readOnly) return;
  159.         /* Don't allow changes if read-only. */
  160.  
  161.     /* If none of the above resolved why we are here, then the click was in the
  162.     ** actual drawing area of the content, and we have some serious work to do. */
  163.  
  164.     GetContentOrigin(window, &contOrg);
  165.     SetOrigin(contOrg.h, contOrg.v);
  166.         /* This sets the origin of the document area of the content to the position
  167.         ** indicated by the document scrollbars. */
  168.  
  169.     click.localEvent = *event;
  170.     GlobalToLocal(&click.localEvent.where);
  171.         /* Who wants to work in global coordinates, anyway... */
  172.  
  173.     if (!GetTool()) {            /* If no specific tool (arrow tool)... */
  174.  
  175.         click.message = HITTESTGRABBER;
  176.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  177.             /* See if the user clicked on a grabber for a selected object. */
  178.  
  179.         if (!hitObj) {        /* If user didn't click on a grabber, see if they hit an object. */
  180.             click.message = HITTESTOBJ;
  181.             hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  182.         }
  183.  
  184.         shiftMod = click.localEvent.modifiers & shiftKey;
  185.             /* Find out if the user was holding down the shift key. */
  186.  
  187.         if (click.message == HITTESTGRABBER)
  188.             shiftMod = 0;
  189.                 /* Pretent that the shift key isn't held down if they are on a grabber.  If
  190.                 ** they are on a grabber, you want to do a constrain with the shift key. */
  191.  
  192.         if (hitObj) {        /* User hit either a grabber or the object itself. */
  193.  
  194.             selected = mDerefCommon(hitObj)->selected;
  195.                 /* See if the object hit, independent of type, was selected. */
  196.  
  197.             if ((!selected) && (!shiftMod))        /* The object isn't selected, and */
  198.                 DoTreeSelect(root, SELECTOFF);    /* the shift key isn't held down, */
  199.                                                 /* so deselect all other objects. */
  200.  
  201.             click.message = CLICKSELECT;
  202.             if ((!selected) || (shiftMod))
  203.                 DoTreeObjMethodClipped(hitObj, CLICKMESSAGE, (long)&click);
  204.                     /* If the object isn't selected, or if the shift key is down, change
  205.                     ** the select state of the object that was clicked on. */
  206.  
  207.             click.localEvent.modifiers ^= shiftMod;
  208.                 /* Turn off the shift modifier, which makes the rest of the
  209.                 ** handling of the object easier. */
  210.  
  211.             if (mDerefCommon(hitObj)->selected) {    /* If object selected, do something with it. */
  212.                 if (click.grabber == -1)
  213.                     SlideSelection(frHndl, &click);
  214.                 else
  215.                     AddOrSizeObj(frHndl, hitObj, &click);
  216.             }            /* The click could have been used to shift-click deselect an object,
  217.                         ** so only do something with the object if it is selected.  We  either
  218.                         ** resize the object (if a grabber was clicked on) or slide the selected
  219.                         ** objects (if an object was clicked on). */
  220.  
  221.         }
  222.         else {                                    /* No object was clicked on... */
  223.  
  224.             if (!shiftMod)                            /* If not a shift-click, */
  225.                 DoTreeSelect(root, SELECTOFF);        /* deselect everything.  */
  226.  
  227.             AddOrSizeObj(frHndl, hitObj, &click);    /* Do extend select. */
  228.         }
  229.     }
  230.     else {
  231.         click.message = HITTESTGRABBER;
  232.         hitObj        = DoTreeHitTest(root = (*frHndl)->d.doc.root, &click);
  233.             /* See if the user clicked on a grabber for a selected object. */
  234.         AddOrSizeObj(frHndl, hitObj, &click);
  235.             /* No object clicked on, so add an object. */
  236.     }
  237. }
  238.  
  239.  
  240.  
  241. /*****************************************************************************/
  242.  
  243.  
  244.  
  245. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  246.  
  247. /* DoKeyDown() is first called by the application.  Then if the key isn't a menu
  248. ** key, DoKeyDown() calls this code.  Here are the rules for this function:
  249. **
  250. ** 1) If you handle the key, return(true).  This completes the key handling.
  251. ** 2) If you don't handle the key, you return false.  However, there are two
  252. **    situations for not handling the key:
  253. **      a) You want someone else to.
  254. **      b) You want nobody else to look at the key.
  255. **    This is what the boolean passThrough is for.  If you wish the next window
  256. **    to have a look at the key, set the boolean passThrough to true.  passThrough
  257. **    is already initialized to false, which is the common case, so you only have
  258. **    to worry about setting it true.
  259. **
  260. ** If you have a window that never processes keys and always passes them through,
  261. ** just set the contentKeyProc to nil.  This will indicate to the application
  262. ** framework that all keys should be passed through this window.  DTS.Draw has
  263. ** such a window.  Its palette window doesn't accept keys.  They are passed through
  264. ** to document windows. */
  265.  
  266. #pragma segment TheDoc
  267. Boolean    ContentKey(WindowPtr window, EventRecord *event, Boolean *passThrough)
  268. {
  269. #ifndef __MWERKS__
  270. #pragma unused (passThrough)
  271. #endif
  272.  
  273.     char        key;
  274.     FileRecHndl    frHndl;
  275.  
  276.     key = event->message & charCodeMask;
  277.     if (key != 8) return(false);
  278.         /* The only key we handle is the delete key.  leave for all the rest. */
  279.  
  280.     SetPort(window);
  281.  
  282.     frHndl = (FileRecHndl)GetWRefCon(window);
  283.     if ((*frHndl)->fileState.readOnly) return(false);
  284.         /* Don't allow changes if read-only. */
  285.  
  286.     DoDelete(frHndl);
  287.  
  288.     return(true);
  289. }
  290.  
  291.  
  292.  
  293. /*****************************************************************************/
  294.  
  295.  
  296.  
  297. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  298.  
  299. /* Draw application specific content (Called by DoDrawFrame).
  300. **
  301. ** If your application has any custom frame areas, or if it uses sidebars,
  302. ** this is the function that you would put the frame drawing code.  The
  303. ** document scrollbars and grow icon drawing is handled by DTS.framework.
  304. ** Just do the sidebar and custom areas here. */
  305.  
  306. #pragma segment TheDoc
  307. void    DrawFrame(FileRecHndl frHndl, WindowPtr window, Boolean activate)
  308. {
  309.     MoveTo(0, (*frHndl)->fileState.topSidebar - 1);
  310.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, (*frHndl)->fileState.topSidebar - 1);
  311.     LineTo((*frHndl)->fileState.leftSidebar - 1 - 16384, 16383);
  312.  
  313.     BeginFrame(window);
  314.     DoDrawControls(window, activate);
  315.     EndFrame(window);
  316. }
  317.  
  318.  
  319.  
  320. /*****************************************************************************/
  321.  
  322.  
  323.  
  324. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  325.  
  326. /* Frees up any application-specific memory in the document.  This is called by
  327. ** DoFreeDocument, which is called by DisposeDocument().  The application would
  328. ** call DisposeDocument(), not DoFreeDocument() or FreeDocument() directly.
  329. **
  330. ** The document may have a bunch of handles off the main handle of the document.
  331. ** This is where they are freed.  DisposeDocument calls this prior to releasing
  332. ** the ram for the main handle of the document, so release everything else
  333. ** here, or you will have a memory leak.
  334. **
  335. ** NOTE:  Calling DefaultFreeDocument() frees up all memory used by a
  336. ** hierarchical document (see TreeObj package). */
  337.  
  338. #pragma segment TheDoc
  339. OSErr    FreeDocument(FileRecHndl frHndl)
  340. {
  341.     return(DefaultFreeDocument(frHndl));
  342. }
  343.  
  344.  
  345.  
  346. /*****************************************************************************/
  347.  
  348.  
  349.  
  350. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  351.  
  352. /* Any additional window disposal tasks can be handled here. */
  353.  
  354. #pragma segment TheDoc
  355. OSErr    FreeWindow(FileRecHndl frHndl, WindowPtr window)
  356. {
  357. #ifndef __MWERKS__
  358. #pragma unused (window)
  359. #endif
  360.  
  361.     WindowPtr    ww;
  362.     FileRecHndl    ff;
  363.  
  364.     if ((*frHndl)->fileState.sfType == kDocFileType) {
  365.         ManageDragHandlers(window, false);
  366.         for (ww = nil; (ww = GetNextWindow(ww, 0)) != nil;) {
  367.             ff = (FileRecHndl)GetWRefCon(ww);
  368.             if ((*ff)->fileState.sfType == kViewHierFileType) {
  369.                 if ((*frHndl)->d.doc.root == (*ff)->d.doc.root) {
  370.                     DisposeOneWindow(ww, kClose);
  371.                     ww = nil;
  372.                 }
  373.             }
  374.         }
  375.     }
  376.  
  377.     return(noErr);
  378. }
  379.  
  380.  
  381.  
  382. /*****************************************************************************/
  383.  
  384.  
  385.  
  386. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  387.  
  388. /* Image the document into the current port.
  389. **
  390. ** The only thing tricky about this function is that it needs to key off of
  391. ** the global variable gPrintPage.  gPrintPage is the current page that is
  392. ** being printed.  If gPrintPage is 0, then you are drawing to the window.
  393. **
  394. ** For when printing:
  395. **
  396. ** If gPrintPage is non-0, that is the page to be printed.  If after imaging
  397. ** the page there are no more pages, you should set gPrintPage to 0.  This
  398. ** indicates to the print loop that the end of the document has been reached.
  399. ** Even if the user indicated in the job dialog to print more pages, setting
  400. ** gPrintPage to 0 states that the last page has been printed.  This is necessary
  401. ** because the print loop can't know when printing is done.  The imaging procedure
  402. ** is the logical one to state when everything has been imaged. */
  403.  
  404. #pragma segment TheDoc
  405. OSErr    ImageDocument(FileRecHndl frHndl)
  406. {
  407.     LayerDrawInfo    drawInfo;
  408.     LayerObj        wlyr, blyr;
  409.  
  410.     drawInfo.root = (*frHndl)->d.doc.root;
  411.         /* If there isn't a background layer, then all objects are drawn into
  412.         ** the work layer, and therefore we don't need to fill in the cnum field
  413.         ** of the drawInfo.  The WorkLayerProc will notice that there isn't a
  414.         ** layer behind it and will automatically do the right thing. */
  415.  
  416.     if (gPrintPage) {
  417.         DoTreeDraw(drawInfo.root, DRAWOBJ);        /* Draw the page. */
  418.         gPrintPage = 0;                            /* We only support one page in this sample. */
  419.         return(noErr);
  420.     }
  421.  
  422.     NewLayer(&wlyr, nil, WindowLayerProc, (*frHndl)->fileState.window, 0, (long)&drawInfo);
  423.         /* Create a layer object for the window. */
  424.  
  425.     NewLayer(&blyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  426.         /* Create a background layer for drawing of all of the objects. */
  427.  
  428.     InvalLayer(wlyr, GetEffectiveDstRect(wlyr), false);
  429.         /* We want to draw the entire contents. */
  430.  
  431.     UpdateLayer(wlyr);
  432.         /* Update what's invalid, which is everything.  All the objects are drawn into
  433.         ** the background layer, and then the background layer is transferred into the
  434.         ** window. */
  435.  
  436.     DisposeThisAndBelowLayers(wlyr);
  437.         /* Clean up what we created. */
  438.  
  439.     return(noErr);
  440. }
  441.  
  442.  
  443.  
  444. /*****************************************************************************/
  445.  
  446.  
  447.  
  448. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  449.  
  450. /* This function does the remaining window initialization.
  451. **
  452. ** There may be additional content initialization for the window.  At this point,
  453. ** you have a window, but it is currently invisible.  If you return noErr, then
  454. ** the window will be set to the state indicated for that window.  Why this function?
  455. ** You may wish to add controls to the content of the window.  You may have a
  456. ** TextEdit record in the content.  All of these sort of things can't be created
  457. ** until there is a window to contain them.  First a document is read in, and then
  458. ** if the document creation succeeds, a window is created for that document.
  459. ** At this point we have a document, and we are on our way to having a window.
  460. ** All that remains is any additional content initialization.  Do it, return
  461. ** noErr, and everybody's happy.  If something goes wrong here, return the error,
  462. ** and the incomplete window will be disposed of. */
  463.  
  464. #pragma segment TheDoc
  465. OSErr    InitContent(FileRecHndl frHndl, WindowPtr window)
  466. {
  467.     OSErr        err;
  468.     Rect        rct;
  469.     TreeObjHndl    root;
  470.  
  471.     err = AddControlSet(window, (*frHndl)->fileState.sfType, kwStandardVis, 0, 0, nil);
  472.  
  473.     rct = GetDataArea(root = (*frHndl)->d.doc.root);
  474.     SetDataArea(root, rct.right, rct.bottom);
  475.     ManageDragHandlers(window, true);
  476.  
  477.     return(err);
  478. }
  479.  
  480.  
  481.  
  482. /*****************************************************************************/
  483.  
  484.  
  485.  
  486. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  487.  
  488. /* The code below assumes that you are using the hierarchical document package.
  489. ** If you are, the entire hierarchical document is read in with just these two
  490. ** calls.  If you don't use it, you are on your own.  See DTS.Chat for an
  491. ** example of an application that uses the DTS.framework without the hierarchical
  492. ** document package. */
  493.  
  494. #pragma segment TheDoc
  495. OSErr    ReadDocument(FileRecHndl frHndl)
  496. {
  497.     OSErr        err;
  498.     TreeObjHndl    root;
  499.  
  500.     err = DefaultReadDocument(frHndl);
  501.     root = (*frHndl)->d.doc.root;
  502.     mDerefRoot(root)->numSelected = 0;        /* User may have saved when objects were selected. */
  503.     if (!err)
  504.         DefaultReadDocumentFixup(frHndl);
  505.  
  506.     return(err);
  507. }
  508.  
  509.  
  510.  
  511. /*****************************************************************************/
  512.  
  513.  
  514.  
  515. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  516.  
  517. /* Resize application specific content (Called by ResizeWindow).
  518. **
  519. ** This gets called when a user does a zoom or window resizing operation.
  520. ** It is possible that things in the content need to be resized in conjunction
  521. ** with the resizing of the window. */
  522.  
  523. #pragma segment TheDoc
  524. void    ResizeContent(WindowPtr window, short oldh, short oldv)
  525. {
  526. #ifndef __MWERKS__
  527. #pragma unused (window, oldh, oldv)
  528. #endif
  529.  
  530.     /* See DTS.Chat for a sample usage of this function. */
  531. }
  532.  
  533.  
  534.  
  535. /*****************************************************************************/
  536.  
  537.  
  538.  
  539. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  540.  
  541. /* Scroll application specific frame (Called by DoScrollFrame).
  542. **
  543. ** Some applications may need to scroll the "frame" of the document along
  544. ** with the document contents.  This is common for applications with rulers,
  545. ** or other similar sidebar items. */
  546.  
  547. #pragma segment TheDoc
  548. void    ScrollFrame(FileRecHndl frHndl, WindowPtr window, long dh, long dv)
  549. {
  550. #ifndef __MWERKS__
  551. #pragma unused (frHndl, window, dh, dv)
  552. #endif
  553. }
  554.  
  555.  
  556.  
  557. /*****************************************************************************/
  558.  
  559.  
  560.  
  561. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  562.  
  563. /* This gets called just prior to and just after an undo/redo operation is done. */
  564.  
  565. #pragma segment TheDoc
  566. void    UndoFixup(FileRecHndl frHndl, Point contOrg, Boolean afterUndo)
  567. {
  568.     WindowPtr    window;
  569.     TreeObjHndl    root;
  570.     Rect        rct;
  571.  
  572.     window = (*frHndl)->fileState.window;
  573.     root   = (*frHndl)->d.doc.root;
  574.  
  575.     if (!afterUndo)                            /* Before an undo operation we deselect everything. */
  576.         DoTreeSelect(root, SELECTOFF);        /* Only what was undone should end up selected. */
  577.  
  578.     if (afterUndo) {
  579.         rct = GetDataArea(root);
  580.         SetDataArea(root, rct.right, rct.bottom);
  581.         SetContentOrigin(window, contOrg.h, contOrg.v);
  582.             /* Undo may have a different document origin than where the user was.
  583.             ** Scroll the document back to where the edit we are undoing took place. */
  584.  
  585.         BeginContent(window);            /* Redraw the document to display */
  586.         DoImageDocument(frHndl);        /* the changes due to undo. */
  587.         EndContent(window);
  588.     }
  589.  
  590.     DoSetCursor(nil);
  591. }
  592.  
  593.  
  594.  
  595. /*****************************************************************************/
  596.  
  597.  
  598.  
  599. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  600.  
  601. /* This function is where you adjust the cursor to reflect the location in the
  602. ** document or window.  You have the additional input of gCursorRgn to deal
  603. ** with.  The way that the cursor handling works is as follows:
  604. ** 1) The application calls DoWindowCursor().
  605. ** 2) DoWindowCursor() works its way through the windows/documents, front to back.
  606. **    It looks at the document's windowCursorProc and checks to see if the document
  607. **    has one.  If the document doesn't have one, then it assumes that that window
  608. **    always wants an arrow.  If the cursor is over that window, the cursor is set
  609. **    to an arrow, and we're done.  If the cursor isn't over the window, then the next
  610. **    window is tried.  If all documents don't have a windowCursorProc, then the cursor
  611. **    is set to an arrow (for the non-document area of the screen).
  612. ** 3) If a document has a windowCursorProc, then the proc is called.  The proc's
  613. **    job is as follows:
  614. **    a) If the cursor is over a position that is determined by the window, then
  615. **       the proc removes other areas from gCursorRgn.  Note that it should not
  616. **       simply set the area to what it "thinks" is the correct area.  This window
  617. **       may not be the front-most.  Other windows will have already been subtracted
  618. **       from gCursorRgn.  The resultant gCursorRgn is the correct cursor area,
  619. **       and should be passed to WaitNextEvent calls in the application (already the case
  620. **       in EventLoop.c).  Also, the cursor should be set to the correct cursor, of course.
  621. **       You should also return true, as the cursor has been determined.
  622. **    b) If the cursor is not over a position for this window, then you should
  623. **       return.  You will either pass back true or false.  If you don't wish
  624. **       windows behind this window to have a shot at cursor determination, then
  625. **       return true.  This states that the cursor is "determined".  It is, in the
  626. **       sense that no further determination will occur.  If you return false, then
  627. **       other windows get a shot at determining the cursor.
  628. **
  629. ** Setting the cursor to the correct cursor isn't as easy as you would expect.
  630. ** DTS.Lib..framework uses the global gCursorPtr as the reference to the cursor.  This is
  631. ** fine if the cursor is pointer-based, but if the cursor is resource-based, it is a bit
  632. ** more of a problem.  What you will need to do is to call DoSetResCursor() to make the
  633. ** resource cursor pointer-based.  DoSetResCursor() will set gCursorPtr to nil, and it
  634. ** also returns the pointer to the permanent copy of the cursor resource.  Just set gCursorPtr
  635. ** to the return result of DoSetResCursor(), and you will be set. */
  636.  
  637. #pragma segment TheDoc
  638. Boolean    WindowCursor(FileRecHndl frHndl, WindowPtr window, Point globalPt)
  639. {
  640. #ifndef __MWERKS__
  641. #pragma unused (frHndl)
  642. #endif
  643.  
  644.     RgnHandle    frameRgn, contRgn;
  645.  
  646.     if (GetTool()) {
  647.         frameRgn = DoCalcFrameRgn(window);
  648.         contRgn  = NewRgn();
  649.         DiffRgn(((WindowPeek)window)->contRgn, frameRgn, contRgn);
  650.         DisposeRgn(frameRgn);
  651.         if (PtInRgn(globalPt, contRgn)) {
  652.             gCursorPtr = DoSetResCursor(addObjCursor);
  653.             SectRgn(gCursorRgn, contRgn, gCursorRgn);
  654.             DisposeRgn(contRgn);
  655.             return(true);
  656.         }
  657.         DiffRgn(((WindowPeek)window)->strucRgn, contRgn, contRgn);
  658.         SectRgn(gCursorRgn, contRgn, gCursorRgn);
  659.         DisposeRgn(contRgn);
  660.     }
  661.  
  662.     SetCursor(gCursorPtr = &qd.arrow);
  663.     return(true);
  664. }
  665.  
  666.  
  667.  
  668. /*****************************************************************************/
  669.  
  670.  
  671.  
  672. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  673.  
  674. /* After the DTS.Lib framework disposes of a window, it calls here.  This is
  675. ** to give the application a chance to do any additional tasks related to
  676. ** a window closing.  DTS.Chat doesn't have anything else extra to do. */
  677.  
  678. #pragma segment TheDoc
  679. void    WindowGoneFixup(WindowPtr window)
  680. {
  681. #ifndef __MWERKS__
  682. #pragma unused (window)
  683. #endif
  684. }
  685.  
  686.  
  687.  
  688. /*****************************************************************************/
  689.  
  690.  
  691.  
  692. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  693.  
  694. /* The reverse function of ReadDocument. */
  695.  
  696. #pragma segment TheDoc
  697. OSErr    WriteDocument(FileRecHndl frHndl)
  698. {
  699.     return(DefaultWriteDocument(frHndl));
  700. }
  701.  
  702.  
  703.  
  704. /*****************************************************************************/
  705.  
  706.  
  707.  
  708. /* •• You don't call this.  DTS.Lib..framework does at open-application time. •• */
  709.  
  710. #pragma segment TheDoc
  711. OSErr    DoOpenApplication(void)
  712. {
  713.     MenuHandle    menu;
  714.  
  715.     menu = GetMenuHandle(mToolPalette);
  716.     if (menu)
  717.         gToolPaletteMenuFlags = (*menu)->enableFlags;
  718.  
  719.     return(noErr);
  720. }
  721.  
  722.  
  723.  
  724. /*****************************************************************************/
  725.  
  726.  
  727.  
  728. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  729.  
  730. #pragma segment TheDoc
  731. Boolean    AdjustMenuItems(WindowPtr window, short menuID)
  732. {
  733.     Boolean        redrawMenuBar;
  734.     MenuHandle    menu;
  735.  
  736.     redrawMenuBar = false;
  737.  
  738.     switch (menuID) {
  739.         case mFile:
  740.             redrawMenuBar = DoAdjustFileMenu(window);
  741.             break;
  742.         case mEdit:
  743.             redrawMenuBar = DoAdjustEditMenu(window);
  744.             break;
  745.         case mArrange:
  746.             redrawMenuBar = DoAdjustArrangeMenu(window);
  747.             break;
  748.         case mToolPalette:
  749.             menu = GetMenuHandle(menuID);
  750.             if (menu)
  751.                 (*menu)->enableFlags = gToolPaletteMenuFlags;
  752.             break;
  753.         case mOther:
  754.             redrawMenuBar = DoAdjustOtherMenu(window);
  755.             break;
  756.         default:
  757.             menu = GetMenuHandle(menuID);
  758.             if (menu)
  759.                 (*menu)->enableFlags |= 0xFFFFFFFEL;
  760.             break;
  761.     }
  762.  
  763.     return(redrawMenuBar);
  764. }
  765.  
  766.  
  767.  
  768. /*****************************************************************************/
  769.  
  770.  
  771.  
  772. /* •• You don't call this.  DTS.Lib..framework does for appropriate document type(s). •• */
  773.  
  774. #pragma segment TheDoc
  775. Boolean    DoMenuItem(WindowPtr window, short menuID, short menuItem)
  776. {
  777. #ifndef __MWERKS__
  778. #pragma unused (window)
  779. #endif
  780.  
  781.     return(DoMenuCommand(menuID, menuItem));
  782. }
  783.  
  784.  
  785.  
  786. /*****************************************************************************/
  787. /*****************************************************************************/
  788. /*****************************************************************************/
  789.  
  790.  
  791.  
  792. #pragma segment DrawDoc
  793. OSErr    DuplicateDocument(FileRecHndl oldFrHndl, FileRecHndl *newFrHndl)
  794. {
  795.     OSErr        err;
  796.     TreeObjHndl    root;
  797.  
  798.     err = NewDocument(newFrHndl, (*oldFrHndl)->fileState.sfType, true);
  799.         /* Create a document and root object to copy the file data into. */
  800.  
  801.     if (!err)
  802.         err = CopyChildren((*oldFrHndl)->d.doc.root, (**newFrHndl)->d.doc.root);
  803.             /* Copy the hierarchical data into the new file. */
  804.  
  805.     if (!err) {
  806.         root = (**newFrHndl)->d.doc.root;
  807.         DoFTreeMethod(root, UNDOMESSAGE, UNDOFROMDOC);
  808.         mDerefRoot(root)->numSelected = 0;
  809.     }        /* We have to be careful to use methods that don't try to draw
  810.             ** anything, as we don't have a window to draw into yet. */
  811.  
  812.     return(err);
  813. }
  814.  
  815.  
  816.  
  817. /*****************************************************************************/
  818.  
  819.  
  820.  
  821. /* This is called when a key event occurs and it is determined that it isn't
  822. ** a menu key. */
  823.  
  824. #pragma segment DrawDoc
  825. void    DoDelete(FileRecHndl frHndl)
  826. {
  827.     WindowPtr    window;
  828.     TreeObjHndl    root;
  829.     short        cnum;
  830.     Boolean        didDelete;
  831.  
  832.     root = (*frHndl)->d.doc.root;
  833.         /* All of the selected objects are children of the root, so all we have to do
  834.         ** is walk the children of the root looking for selected objects.  If the
  835.         ** document has grouping objects, which have sub-children, only the group
  836.         ** object is selected. */
  837.  
  838.     for (didDelete = false, cnum = (*root)->numChildren; cnum;) {
  839.         if (mDerefCommon(GetChildHndl(root, --cnum))->selected) {
  840.             if (!didDelete)
  841.                 NewDocumentUndo(frHndl);
  842.                     /* Make sure this delete event is posted in a separate undo from
  843.                     ** a possible previous delete event. */
  844.             DisposeChild(DELETE_EDIT, root, cnum);
  845.                 /* Post the delete of the child into the undo hierarchy. */
  846.             didDelete = true;
  847.                 /* Yes, we did change the document. */
  848.         }
  849.     }
  850.  
  851.     if (didDelete) {                            /* If something got deleted... */
  852.         window = (*frHndl)->fileState.window;
  853.         SetWindowDirty(window);                    /* Flag the document as dirty. */
  854.         BeginContent(window);                    /* Redraw the contents. */
  855.         DoImageDocument(frHndl);
  856.         EndContent(window);
  857.     }
  858. }
  859.  
  860.  
  861.  
  862. /*****************************************************************************/
  863.  
  864.  
  865.  
  866. #pragma segment DrawDoc
  867. void    DoArrange(FileRecHndl frHndl, short menuItem)
  868. {
  869.     TreeObjHndl    root;
  870.     short        cnum, cdir, cend;
  871.     WindowPtr    window;
  872.  
  873.     if (menuItem >= kGroup) {
  874.         switch (menuItem) {
  875.             case kGroup:
  876.                 DoGroup(frHndl);
  877.                 break;
  878.             case kUngroup:
  879.                 DoUngroup(frHndl);
  880.                 break;
  881.         }
  882.     }
  883.     else {
  884.         root = (*frHndl)->d.doc.root;
  885.  
  886.         cnum = 0;
  887.         cdir = 1;
  888.         cend = (*root)->numChildren;
  889.  
  890.         if (cend)
  891.             NewDocumentUndo(frHndl);
  892.  
  893.         if (menuItem >= kMoveBackward) {
  894.             cnum = cend - 1;
  895.             cdir = -1;
  896.             cend = -1;
  897.         }
  898.  
  899.         for (; cnum != cend; cnum += cdir) {
  900.             if (DoTreeObjMethod(GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  901.                 switch (menuItem) {
  902.                     case kMoveForward:
  903.                         if (cnum)
  904.                             MoveChild(MOVEFORWARD_EDIT, root, cnum, root, cnum - 1);
  905.                         break;
  906.                     case kMoveToFront:
  907.                         MoveChild(MOVETOFRONT_EDIT, root, cnum, root, 0);
  908.                         break;
  909.                     case kMoveBackward:
  910.                         MoveChild(MOVEBACKWARD_EDIT, root, cnum, root, cnum + 1);
  911.                         break;
  912.                     case kMoveToBack:
  913.                         MoveChild(MOVETOBACK_EDIT, root, cnum, root, -1);
  914.                         break;
  915.                 }
  916.             }
  917.         }
  918.     }
  919.     SetWindowDirty(window = (*frHndl)->fileState.window);
  920.  
  921.     BeginContent(window);
  922.     DoImageDocument(frHndl);
  923.     EndContent(window);
  924. }
  925.  
  926.  
  927.  
  928. /*****************************************************************************/
  929.  
  930.  
  931.  
  932. #pragma segment DrawDoc
  933. Rect    GetSelectedArea(TreeObjHndl root)
  934. {
  935.     TreeObjHndl    child;
  936.     short        cnum;
  937.     Rect        selectRct, rct;
  938.     Boolean        first;
  939.  
  940.     SetRect(&selectRct, 0, 0, 0, 0);
  941.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  942.         child = GetChildHndl(root, --cnum);
  943.         if (DoTreeObjMethod(child, GETSELECTMESSAGE, 0)) {
  944.             DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  945.             if (!EmptyRect(&rct)) {
  946.                 if (first) {
  947.                     selectRct = rct;
  948.                     first = false;
  949.                 }
  950.                 else UnionRect(&selectRct, &rct, &selectRct);
  951.             }
  952.         }
  953.     }
  954.     return(selectRct);
  955. }    
  956.  
  957.  
  958.  
  959. /*****************************************************************************/
  960.  
  961.  
  962.  
  963. #pragma segment DrawDoc
  964. Rect    GetDataArea(TreeObjHndl root)
  965. {
  966.     TreeObjHndl    child;
  967.     short        cnum;
  968.     Rect        areaRct, rct;
  969.     Boolean        first;
  970.  
  971.     SetRect(&areaRct, 0, 0, 0, 0);
  972.     for (first = true, cnum = (*root)->numChildren; cnum;) {
  973.         child = GetChildHndl(root, --cnum);
  974.         DoTreeObjMethod(child, GETBBOXMESSAGE, (long)&rct);
  975.         if (!EmptyRect(&rct)) {
  976.             if (first) {
  977.                 areaRct = rct;
  978.                 first   = false;
  979.             }
  980.             else UnionRect(&areaRct, &rct, &areaRct);
  981.         }
  982.     }
  983.     return(areaRct);
  984. }    
  985.  
  986.  
  987.  
  988. /*****************************************************************************/
  989.  
  990.  
  991.  
  992. #pragma segment DrawDoc
  993. void    SetDataArea(TreeObjHndl root, short h, short v)
  994. {
  995.     if (h != kwNoChange) {
  996.         h /= (7 * 72);
  997.         ++h;
  998.         h *= (7 * 72);
  999.     }
  1000.  
  1001.     if (v != kwNoChange) {
  1002.         v /= (10 * 72);
  1003.         ++v;
  1004.         v *= (10 * 72);
  1005.     }
  1006.  
  1007.     SetDocSize(mDerefRoot(root)->frHndl, h, v);
  1008. }    
  1009.  
  1010.  
  1011.  
  1012. /*****************************************************************************/
  1013.  
  1014.  
  1015.  
  1016. #pragma segment DrawDoc
  1017. void    DoGroup(FileRecHndl frHndl)
  1018. {
  1019.     TreeObjHndl    root, group, child;
  1020.     short        gnum, cnum;
  1021.     Rect        groupRct;
  1022.  
  1023.     NewDocumentUndo(frHndl);
  1024.  
  1025.     root = (*frHndl)->d.doc.root;
  1026.     for (gnum = 0; gnum < (*root)->numChildren; ++gnum)
  1027.         if (DoTreeObjMethod(GetChildHndl(root, gnum), GETSELECTMESSAGE, 0)) break;
  1028.  
  1029.     if (!(group = NewChild(GROUP_EDIT, root, gnum, GROUPOBJ, 0))) return;
  1030.  
  1031.     groupRct = GetSelectedArea(root);
  1032.     for (cnum = (*root)->numChildren - 1; cnum > gnum; --cnum) {
  1033.         if (DoTreeObjMethod(child = GetChildHndl(root, cnum), GETSELECTMESSAGE, 0)) {
  1034.             DoTreeObjMethodClipped(child, SETSELECTMESSAGE, SELECTOFF);
  1035.             MoveChild(GROUP_EDIT, root, cnum, group, 0);
  1036.         }
  1037.     }
  1038.  
  1039.     mDerefGroup(group)->group = groupRct;
  1040. }    
  1041.  
  1042.  
  1043.  
  1044. /*****************************************************************************/
  1045.  
  1046.  
  1047.  
  1048. #pragma segment DrawDoc
  1049. void    DoUngroup(FileRecHndl frHndl)
  1050. {
  1051.     TreeObjHndl    root, group;
  1052.     short        gnum, cnum;
  1053.  
  1054.     NewDocumentUndo(frHndl);
  1055.  
  1056.     root = (*frHndl)->d.doc.root;
  1057.     for (gnum = (*root)->numChildren; gnum;) {
  1058.         if (DoTreeObjMethod(group = GetChildHndl(root, --gnum), GETSELECTMESSAGE, 0)) {
  1059.             if ((*group)->type == GROUPOBJ) {
  1060.                 DoTreeSelect(group, SELECTOFF);
  1061.                 for (cnum = (*group)->numChildren; cnum;) {
  1062.                     MoveChild(UNGROUP_EDIT, group, --cnum, root, gnum + 1);
  1063.                     DoTreeSelect(GetChildHndl(root, gnum + 1), SELECTON);
  1064.                 }
  1065.                 DisposeChild(UNGROUP_EDIT, root, gnum);
  1066.             }
  1067.         }
  1068.     }
  1069. }    
  1070.  
  1071.  
  1072.  
  1073. /*****************************************************************************/
  1074. /*****************************************************************************/
  1075. /*****************************************************************************/
  1076.  
  1077.  
  1078.  
  1079. #pragma segment DrawDoc
  1080. void    AddOrSizeObj(FileRecHndl frHndl, TreeObjHndl hndl, ClickInfo *click)
  1081. {
  1082.     TreeObjHndl        root, cobj, origHndl, oldHndl;
  1083.     WindowPtr        window;
  1084.     short            cnum, h, w, i;
  1085.     short            adding, extSelect;
  1086.     LayerObj        wlyr, wklyr, blyr;
  1087.     Rect            oldRct, newRct, rct, extRct1, extRct2, extRct3;
  1088.     OSErr            err;
  1089.     LayerDrawInfo    drawInfo;
  1090.     Boolean            tool, keepTool, newTool;
  1091.     Point            curMouse, org, oldOrg;
  1092.  
  1093.     root   = (*frHndl)->d.doc.root;
  1094.     window = (*frHndl)->fileState.window;
  1095.  
  1096.     tool      = GetTool();
  1097.     keepTool  = GetToolPersistence();
  1098.     adding    = false;
  1099.     extSelect = false;
  1100.     newTool   = false;
  1101.  
  1102.     if (!hndl) {        /* If hndl == nil, then we are adding a new object. */
  1103.         adding = true;
  1104.         if (!tool)
  1105.             extSelect = true;
  1106.         else
  1107.             DoTreeSelect(root, SELECTOFF);        /* Turn off all old selections. */
  1108.         click->offset.h = 0;
  1109.         click->offset.v = 0;
  1110.         click->grabber  = 0;
  1111.         click->oldFlip  = 0;
  1112.         click->newFlip  = 0;
  1113.  
  1114.         i = (extSelect) ? EXTSELECTOBJ : ((RECTOBJ - 1) + tool);
  1115.         hndl = NewChild(NO_EDIT, root, cnum = 0, i, 0);
  1116.             /* The child gets created selected, ready to go, except that it's rect is
  1117.             ** empty.  As the user drags the object out, this will change. */
  1118.  
  1119.         if (!keepTool) {
  1120.             tool    = 0;
  1121.             newTool = true;
  1122.         }            /* If the tool is a one-use tool, dispose it. */
  1123.  
  1124.         if (hndl) {        /* If we succeeded at creating something... */
  1125.             rct.top  = rct.bottom = click->localEvent.where.v;
  1126.             rct.left = rct.right  = click->localEvent.where.h;
  1127.             DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);
  1128.             DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1129.         }
  1130.     }
  1131.     else {
  1132.         NewDocumentUndo(frHndl);
  1133.         cnum = GetChildNum(hndl);
  1134.         if (ModifyChild(SIZE_EDIT, root, cnum, false)) return;
  1135.             /* Out of memory.  I would handle this case better if it weren't just sample code.
  1136.             ** Given that there are no complex objects, we actually can't run out of memory
  1137.             ** due to this operation.  However, there may someday be a pixmap type of object,
  1138.             ** whose memory hit could be substantial. */
  1139.     }
  1140.  
  1141.     drawInfo.root = root;        /* The tree we are operating with. */
  1142.     drawInfo.cnum = cnum;        /* The backmost object that is changing. */
  1143.  
  1144.     if (hndl) {
  1145.         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1146.         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1147.         if (!err)
  1148.             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1149.         if (err) DisposeThisAndBelowLayers(wklyr);
  1150.             /* The above code creates the necessary offscreen layers for the following editing.
  1151.             ** The window layer is bound to succeed at getting created, as it uses the window,
  1152.             ** instead of creating an offscreen GWorld.  The work layer and back layer creations
  1153.             ** may fail.  If they do, then we will only have a window layer.  The window layer
  1154.             ** is smart enough to notice that it is alone, and if it is, then it will do the
  1155.             ** drawing directly to the window.  Drawing directly to the window means that there
  1156.             ** will be a bunch of flicker, but what else is there to do when there is not enough
  1157.             ** ram?  Drawing nothing is even worse. */
  1158.  
  1159.         DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1160.             /* Bounding rect of the object getting created or resized. */
  1161.  
  1162.         if (click->grabber)
  1163.             InsetRect(&rct, -3, -3);
  1164.         InvalLayer(wlyr, rct, true);
  1165.             /* If getting resized, then the object has grabbers, and we have to make sure
  1166.             ** that the area getting redrawn is large enough for the grabbers. */
  1167.  
  1168.         origHndl = hndl;
  1169.         HandToHand((Handle *)&origHndl);
  1170.         while (StillDown()) {
  1171.             DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&oldRct);
  1172.             oldHndl = hndl;
  1173.             HandToHand((Handle *)&oldHndl);
  1174.             if (DoTreeObjMethod(hndl, SIZEMESSAGE, (long)click)) {        /* If new size... */
  1175.                 DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&newRct);
  1176.                 if (!err) {        /* If we have all offscreen layers... */
  1177.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&rct);
  1178.                     if (click->grabber)
  1179.                         InsetRect(&rct, -3, -3);
  1180.                     InvalLayer(wlyr, rct, true);
  1181.                     UpdateLayer(wlyr);
  1182.                 }
  1183.                 else {            /* If we only have wlyr... */
  1184.                     SwapTreeObjData(hndl, oldHndl);
  1185.                     if (!EqualTreeObjData(hndl, origHndl))
  1186.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1187.                     SwapTreeObjData(hndl, oldHndl);
  1188.                     if (!EqualTreeObjData(hndl, origHndl))
  1189.                         DoTreeObjMethodClipped(hndl, DRAWMESSAGE, DRAWGHOST);
  1190.                 }
  1191.             }
  1192.             DisposeHandle((Handle)oldHndl);
  1193.  
  1194.             GetMouse(&curMouse);
  1195.             GetContentRect(window, &rct);
  1196.             if (!PtInRect(curMouse, &rct)) {
  1197.  
  1198.                 GetContentOrigin(window, &org);
  1199.                 oldOrg = org;
  1200.                 if (curMouse.h < rct.left)    curMouse.h = rct.left   - 36;
  1201.                 if (curMouse.h > rct.right)   curMouse.h = rct.right  + 36;
  1202.                 if (curMouse.v < rct.top)     curMouse.v = rct.top    - 36;
  1203.                 if (curMouse.v > rct.bottom)  curMouse.v = rct.bottom + 36;
  1204.  
  1205.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1206.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1207.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1208.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1209.  
  1210.                 rct = GetDataArea(root);
  1211.                 if (rct.right < curMouse.h)
  1212.                     rct.right = curMouse.h;
  1213.                 if (rct.bottom < curMouse.v)
  1214.                     rct.bottom = curMouse.v;
  1215.                 SetDataArea(root, rct.right, rct.bottom);
  1216.  
  1217.                 SetContentOrigin(window, org.h, org.v);
  1218.                 GetContentOrigin(window, &org);
  1219.  
  1220.                 if (!err) {
  1221.                     if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1222.                         DisposeThisAndBelowLayers(wlyr);
  1223.                         DoImageDocument(frHndl);
  1224.                         NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1225.                         err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1226.                         if (!err)
  1227.                             err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1228.                         if (err) DisposeThisAndBelowLayers(wklyr);
  1229.                         GetContentRect(window, &rct);
  1230.                         InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1231.                     }
  1232.                 }
  1233.                 else
  1234.                     DoImageDocument(frHndl);
  1235.             }
  1236.             else {
  1237.                 rct = GetDataArea(root);
  1238.                 SetDataArea(root, rct.right, rct.bottom);
  1239.             }
  1240.         }
  1241.         if (origHndl)
  1242.             DisposeHandle((Handle)origHndl);
  1243.  
  1244.         rct = GetDataArea(root);
  1245.         SetDataArea(root, rct.right, rct.bottom);
  1246.  
  1247.         DoTreeObjMethod(hndl, GETOBJRECTMESSAGE, (long)&rct);
  1248.         h = rct.bottom - rct.top;
  1249.         w = rct.right  - rct.left;
  1250.             /* This is how big the final product is.  It may now be unacceptably small.
  1251.             ** If we were adding a new object and it is too small, get rid of it entirely
  1252.             ** and use this to deselect the tool.  If we were resizing an existing object,
  1253.             ** then force the object to be a minimum size. */
  1254.  
  1255.         if (((!h) || (!w)) || ((h < 8) && (w < 8))) {    /* If smaller than minimum... */
  1256.             if (adding) {
  1257.                 DisposeChild(NO_EDIT, root, 0);        /* Don't create a new object that's too small. */
  1258.                 hndl = nil;                    /* We didn't change anything, so flag this situation.  */
  1259.                 SetPaletteTool(0);
  1260.                 newTool = false;
  1261.             }
  1262.             else {
  1263.                 rct.bottom = rct.top  + 8;                /* Don't let them shrink it too small. */
  1264.                 rct.right  = rct.left + 8;
  1265.                 DoTreeObjMethod(hndl, SETOBJRECTMESSAGE, (long)&rct);    /* Make it minimum. */
  1266.             }
  1267.             DoImageDocument(frHndl);
  1268.         }
  1269.  
  1270.         if (hndl) {        /* If we added or changed something... */
  1271.  
  1272.  
  1273.             if (adding) {        /* If we are adding, we haven't handled posting the addition
  1274.                                 ** for undo.  The reason for this is that the user may not
  1275.                                 ** actually add an object.  If they just click and release,
  1276.                                 ** there is no object added.  If we add the child with undo
  1277.                                 ** posting, and then remove it if the user doesn't grow it
  1278.                                 ** out, then we may lose some old undo info.  If the user
  1279.                                 ** doesn't grow out the object, the operation is a NOP, and
  1280.                                 ** so old undo information shouldn't be lost. */
  1281.                 if (extSelect) {                            /* We only created the object for extend-select purposes. */
  1282.                     DoTreeObjMethod(hndl, GETBBOXMESSAGE, (long)&extRct1);
  1283.                     for (i = (*root)->numChildren - 1; i; --i) {
  1284.                         cobj = GetChildHndl(root, i);
  1285.                         DoTreeObjMethod(cobj, GETBBOXMESSAGE, (long)&extRct2);
  1286.                         SectRect(&extRct1, &extRct2, &extRct3);
  1287.                         if (EqualRect(&extRct2, &extRct3))
  1288.                             DoTreeObjMethodClipped(cobj, SETSELECTMESSAGE, SELECTTOGGLE);
  1289.                     }
  1290.                     DisposeChild(NO_EDIT, root, 0);            /* We're done with it, so kill it. */
  1291.                 }
  1292.                 else {
  1293.                     NewDocumentUndo(frHndl);
  1294.                     hndl = CopyChild(NEW_EDIT, root, 0, root, 0, false);
  1295.                     DisposeChild(NO_EDIT, root, 1);
  1296.                 }    /* We just created a posted copy of the child, just as if we had called
  1297.                     ** NewChild.  We also disposed of the temporary unposted copy of the child.
  1298.                     ** The only problem is that objects entering the document get flagged as
  1299.                     ** selected.  This means that when we select the object, nothing happens
  1300.                     ** on the screen because the object thinks it is already selected. */
  1301.                 if (!extSelect)
  1302.                     if (hndl)
  1303.                         DoTreeObjMethod(hndl, SETSELECTMESSAGE, (SELECTOFF | SELECTNODRAW));
  1304.                             /* Now the select grabbers can be drawn. */
  1305.             }
  1306.  
  1307.             rct = GetDataArea(root);
  1308.             SetDataArea(root, rct.right, rct.bottom);
  1309.  
  1310.             SetWindowDirty(window);
  1311.             if (err)
  1312.                 DoImageDocument(frHndl);
  1313.                     /* If we have only a wlyr, then we haven't really been generating a
  1314.                     ** complete image.  Redraw the document so that a full image is shown. */
  1315.  
  1316.             if (!extSelect)
  1317.                 DoTreeObjMethodClipped(hndl, SETSELECTMESSAGE, SELECTON);
  1318.                     /* Make sure the object is selected. */
  1319.         }
  1320.  
  1321.         DisposeThisAndBelowLayers(wlyr);        /* Clean up. */
  1322.     }
  1323.  
  1324.     if (extSelect)
  1325.         DoImageDocument(frHndl);
  1326.             /* Since we have killed the extend-select object, erase it from the screen. */
  1327.  
  1328.     if (newTool)
  1329.         SetPaletteTool(tool);
  1330. }
  1331.  
  1332.  
  1333.  
  1334. /*****************************************************************************/
  1335.  
  1336.  
  1337.  
  1338. #pragma segment DrawDoc
  1339. static OSErr    WindowLayerProc(LayerObj theLayer, short message)
  1340. {
  1341.     OSErr            err;
  1342.     WindowPtr        oldPort, window;
  1343.     Point            contOrg;
  1344.     Rect            contRct, thisUpdate;
  1345.     LayerDrawInfo    drawInfo;
  1346.     RgnHandle        oldClip, newClip;
  1347.  
  1348.     err = noErr;
  1349.  
  1350.     drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1351.  
  1352.     switch (message) {
  1353.         case kLayerInit:
  1354.             err = DefaultLayerProc(theLayer, message);
  1355.             GetPort(&oldPort);
  1356.             SetPort(window = (*theLayer)->layerPort);
  1357.             GetContentOrigin(window, &contOrg);
  1358.             SetOrigin(contOrg.h, contOrg.v);
  1359.             GetContentRect(window, &contRct);
  1360.             (*theLayer)->dstRect = contRct;
  1361.                 /* The above calculates the content rect (less tool palette and scrollbars).
  1362.                 ** By setting the dstRect to this smaller rect, work layer and back layer
  1363.                 ** will be created the size of dstRect, instead of the portRect of the
  1364.                 ** window.  This in turn will keep the GWLayers code from drawing over
  1365.                 ** the tool palette and scrollbars. */
  1366.             SetPort(oldPort);
  1367.             break;
  1368.         case kLayerUpdate:
  1369.             BeginContent((*theLayer)->layerPort);
  1370.             if ((*theLayer)->belowLayer)
  1371.                 DefaultLayerProc(theLayer, message);
  1372.             else {        /* If offscreen layers couldn't be made, then do it by hand. */
  1373.                 thisUpdate = UpdateUpdateRects(theLayer);
  1374.                 oldClip = NewRgn();
  1375.                 newClip = NewRgn();
  1376.                 RectRgn(newClip, &thisUpdate);
  1377.                 GetClip(oldClip);
  1378.                 SetClip(newClip);
  1379.                 EraseRect(&thisUpdate);
  1380.                 DoTreeDraw(drawInfo.root, DRAWOBJ);
  1381.                 DoTreeDraw(drawInfo.root, DRAWSELECT);
  1382.                 SetClip(oldClip);
  1383.                 DisposeRgn(oldClip);
  1384.                 DisposeRgn(newClip);
  1385.                 DrawPageGrid();
  1386.             }
  1387.             EndContent((*theLayer)->layerPort);
  1388.             break;
  1389.  
  1390.         default:
  1391.             err = DefaultLayerProc(theLayer, message);
  1392.                 /* Default behavior for everything else. */
  1393.             break;
  1394.     }
  1395.  
  1396.     return(err);
  1397. }
  1398.  
  1399.  
  1400.  
  1401. /*****************************************************************************/
  1402.  
  1403.  
  1404.  
  1405. #pragma segment DrawDoc
  1406. static OSErr    WorkLayerProc(LayerObj theLayer, short message)
  1407. {
  1408.     OSErr            err;
  1409.     Rect            rct;
  1410.     LayerDrawInfo    drawInfo;
  1411.     short            cnum;
  1412.     TreeObjHndl        chndl;
  1413.  
  1414.     err = noErr;
  1415.  
  1416.     switch (message) {
  1417.         case kLayerInit:
  1418.             err = DefaultLayerProc(theLayer, message);
  1419.             break;
  1420.         case kLayerUpdate:
  1421.             SetLayerWorld(theLayer);
  1422.             drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1423.             if ((*theLayer)->belowLayer)
  1424.                 DefaultLayerProc(theLayer, message);
  1425.             else {
  1426.                 rct = GetEffectiveDstRect(theLayer);
  1427.                 EraseRect(&rct);
  1428.                 drawInfo.cnum = (*drawInfo.root)->numChildren - 1;
  1429.             }
  1430.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1431.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1432.                 DoTreeDraw(chndl, DRAWOBJ);
  1433.             }
  1434.             for (cnum = drawInfo.cnum; cnum > -1; --cnum) {
  1435.                 chndl = GetChildHndl(drawInfo.root, cnum);
  1436.                 DoTreeDraw(chndl, DRAWSELECT);
  1437.             }
  1438.             DrawPageGrid();
  1439.             ResetLayerWorld(theLayer);
  1440.             break;
  1441.         default:
  1442.             err = DefaultLayerProc(theLayer, message);
  1443.                 /* Default behavior for everything else. */
  1444.             break;
  1445.     }
  1446.  
  1447.     return(err);
  1448. }
  1449.  
  1450.  
  1451.  
  1452. /*****************************************************************************/
  1453.  
  1454.  
  1455.  
  1456. #pragma segment DrawDoc
  1457. static OSErr    BackLayerProc(LayerObj theLayer, short message)
  1458. {
  1459.     OSErr            err;
  1460.     Rect            rct;
  1461.     LayerDrawInfo    drawInfo;
  1462.     short            cnum;
  1463.     TreeObjHndl        chndl;
  1464.  
  1465.     err = noErr;
  1466.  
  1467.     switch (message) {
  1468.         case kLayerInit:
  1469.             err = DefaultLayerProc(theLayer, message);
  1470.             if (!err) {
  1471.                 SetLayerWorld(theLayer);
  1472.                 rct = GetEffectiveDstRect(theLayer);
  1473.                 EraseRect(&rct);
  1474.                 drawInfo = *(LayerDrawInfo *)(*theLayer)->layerData;
  1475.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1476.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1477.                     DoTreeDraw(chndl, DRAWOBJ);
  1478.                 }
  1479.                 for (cnum = (*drawInfo.root)->numChildren - 1;  cnum > drawInfo.cnum; --cnum) {
  1480.                     chndl = GetChildHndl(drawInfo.root, cnum);
  1481.                     DoTreeDraw(chndl, DRAWSELECT);
  1482.                 }
  1483.                 ResetLayerWorld(theLayer);
  1484.             }
  1485.             break;
  1486.         default:
  1487.             err = DefaultLayerProc(theLayer, message);
  1488.                 /* Default behavior for everything else. */
  1489.             break;
  1490.     }
  1491.  
  1492.     return(err);
  1493. }
  1494.  
  1495.  
  1496.  
  1497. /*****************************************************************************/
  1498.  
  1499.  
  1500.  
  1501. #pragma segment DrawDoc
  1502. void    DrawPageGrid(void)
  1503. {
  1504.     WindowPtr    curPort;
  1505.     short        x1, x2, y1, y2, i;
  1506.  
  1507.     if (!gPrintPage) {
  1508.         GetPort(&curPort);
  1509.         PenPat((ConstPatternParam)&qd.gray);
  1510.         x1  = curPort->portRect.left   / (7 * 72);
  1511.         x1 *= (7 * 72);
  1512.         x2  = curPort->portRect.right  / (7 * 72);
  1513.         x2 *= (7 * 72);
  1514.         y1  = curPort->portRect.top    / (10 * 72);
  1515.         y1 *= (10 * 72);
  1516.         y2  = curPort->portRect.bottom / (10 * 72);
  1517.         y2 *= (10 * 72);
  1518.         for (i = x1; i <= x2; i += (7 * 72)) {
  1519.             MoveTo(i - 1, -1);
  1520.             Line  (0, 16383);
  1521.         }
  1522.         for (i = y1; i <= y2; i += (10 * 72)) {
  1523.             MoveTo(-1, i - 1);
  1524.             Line  (16383, 0);
  1525.         }
  1526.         PenNormal();
  1527.     }
  1528. }
  1529.  
  1530.  
  1531.  
  1532. /*****************************************************************************/
  1533.  
  1534.  
  1535.  
  1536. #pragma segment DrawDoc
  1537. void    SlideSelection(FileRecHndl frHndl, ClickInfo *click)
  1538. {
  1539.     LayerDrawInfo    drawInfo;
  1540.     WindowPtr        window;
  1541.     LayerObj        wlyr, wklyr, blyr;
  1542.     Point            lastMouse, curMouse;
  1543.     short            cnum, dh, dv;
  1544.     TreeObjHndl        root, chndl;
  1545.     Rect            newLoc, selectRct, rct;
  1546.     Point            oldOrg, org;
  1547.     Boolean            changed, autoScroll, dragDrop;
  1548.     OSErr            err;
  1549.     EventRecord        event;
  1550.     long            as, autoScrollTick;
  1551.  
  1552.     autoScrollTick = 0;
  1553.  
  1554.     changed = false;
  1555.     NewDocumentUndo(frHndl);
  1556.  
  1557.     root = (*frHndl)->d.doc.root;
  1558.     for (cnum = (*root)->numChildren;;) {
  1559.         if (DoTreeObjMethod(GetChildHndl(root, --cnum), GETSELECTMESSAGE, 0)) break;
  1560.         if (!cnum) return;
  1561.     }
  1562.  
  1563.     drawInfo.root = root;
  1564.     drawInfo.cnum = cnum;
  1565.     window        = (*frHndl)->fileState.window;
  1566.  
  1567.                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1568.               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1569.     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1570.  
  1571.     selectRct = GetSelectedArea(root);
  1572.     InsetRect(&selectRct, -3, -3);
  1573.     InvalLayer(wlyr, selectRct, false);
  1574.     lastMouse = click->localEvent.where;
  1575.  
  1576.     autoScroll = dragDrop = false;
  1577.     while (StillDown()) {
  1578.         GetMouse(&curMouse);
  1579.         GetContentRect(window, &rct);
  1580.         rct.top  += 16;
  1581.         rct.left += 16;
  1582.         as = autoScroll;
  1583.         autoScroll = !PtInRect(curMouse, &rct);
  1584.         if (autoScroll) {
  1585.             if (!autoScrollTick) autoScrollTick = TickCount();
  1586.             dh = lastMouse.h - curMouse.h;
  1587.             if (dh < 0) dh = -dh;
  1588.             dv = lastMouse.v - curMouse.v;
  1589.             if (dv < 0) dv = -dv;
  1590.             if ((dh + dv > 6) && (!as)) autoScrollTick = TickCount();
  1591.             if (autoScrollTick + 15 > TickCount()) autoScroll = false;
  1592.         }
  1593.         if (DragDropAvailable()) {
  1594.             InsetRect(&rct, -16, -16);
  1595.             dragDrop = !PtInRect(curMouse, &rct);
  1596.             InsetRect(&rct,  16,  16);
  1597.         }
  1598.  
  1599.         if ((dragDrop) || (click->localEvent.modifiers & optionKey)) {
  1600.             if (changed) RevertEdit(root, true);
  1601.             event = click->localEvent;
  1602.             LocalToGlobal(&event.where);
  1603.             if (DoDrag(frHndl, &event) != paramErr) break;
  1604.         }
  1605.  
  1606.         click->offset.h = (curMouse.h - lastMouse.h);
  1607.         click->offset.v = (curMouse.v - lastMouse.v);
  1608.         click->message  = CLICKDRAG;
  1609.  
  1610.         if ((autoScroll) || (click->offset.h) || (click->offset.v)) {
  1611.             changed = true;
  1612.             if ((click->offset.h) || (click->offset.v)) {
  1613.                 for (cnum = 0; cnum < (*root)->numChildren; ++cnum) {
  1614.                     chndl = GetChildHndl(root, cnum);
  1615.                     if (mDerefCommon(chndl)->selected) {
  1616.                         ModifyChild(MOVE_EDIT, root, cnum, true);
  1617.                         DoFTreeMethod(chndl, CLICKMESSAGE, (long)click);
  1618.                         DoTreeObjMethod(chndl, GETBBOXMESSAGE, (long)&newLoc);
  1619.                         InsetRect(&newLoc, -3, -3);
  1620.                         InvalLayer(wlyr, newLoc, true);
  1621.                     }
  1622.                 }
  1623.                 UpdateLayer(wlyr);
  1624.             }
  1625.             lastMouse = curMouse;
  1626.  
  1627.             if (autoScroll) {
  1628.                 GetContentOrigin(window, &org);
  1629.                 oldOrg = org;
  1630.                 if (curMouse.h < rct.left)    curMouse.h = rct.left   - 36;
  1631.                 if (curMouse.h > rct.right)   curMouse.h = rct.right  + 36;
  1632.                 if (curMouse.v < rct.top)     curMouse.v = rct.top    - 36;
  1633.                 if (curMouse.v > rct.bottom)  curMouse.v = rct.bottom + 36;
  1634.  
  1635.                 if (curMouse.h < rct.left)   org.h += (curMouse.h - rct.left);
  1636.                 if (curMouse.h > rct.right)  org.h += (curMouse.h - rct.right);
  1637.                 if (curMouse.v < rct.top)    org.v += (curMouse.v - rct.top);
  1638.                 if (curMouse.v > rct.bottom) org.v += (curMouse.v - rct.bottom);
  1639.  
  1640.                 rct = GetDataArea(root);
  1641.                 if (rct.right < curMouse.h)
  1642.                     rct.right = curMouse.h;
  1643.                 if (rct.bottom < curMouse.v)
  1644.                     rct.bottom = curMouse.v;
  1645.                 SetDataArea(root, rct.right, rct.bottom);
  1646.  
  1647.                 SetContentOrigin(window, org.h, org.v);
  1648.                 GetContentOrigin(window, &org);
  1649.                 if ((org.h != oldOrg.h) || (org.v != oldOrg.v)) {
  1650.                     DisposeThisAndBelowLayers(wlyr);
  1651.                     DoImageDocument(frHndl);
  1652.                                     NewLayer(&wlyr, nil, WindowLayerProc, window, 0, (long)&drawInfo);
  1653.                               err = NewLayer(&wklyr, wlyr, WorkLayerProc, nil, 0, (long)&drawInfo);
  1654.                     if (!err) err = NewLayer(&blyr, wklyr, BackLayerProc, nil, 0, (long)&drawInfo);
  1655.                     if (err) DisposeThisAndBelowLayers(wklyr);
  1656.                     GetContentRect(window, &rct);
  1657.                     InvalLayer(wlyr, rct, false);    /* Force full redraw. */
  1658.                 }
  1659.             }
  1660.             else {
  1661.                 rct = GetDataArea(root);
  1662.                 SetDataArea(root, rct.right, rct.bottom);
  1663.             }
  1664.         }
  1665.     }
  1666.  
  1667.     rct = GetDataArea(root);
  1668.     SetDataArea(root, rct.right, rct.bottom);
  1669.  
  1670.     if (changed)
  1671.         SetWindowDirty(window);
  1672.  
  1673.     DisposeThisAndBelowLayers(wlyr);
  1674. }
  1675.  
  1676.  
  1677.  
  1678. /*****************************************************************************/
  1679.  
  1680.  
  1681.  
  1682. #pragma segment DrawDoc
  1683. void    NewDocumentUndo(FileRecHndl frHndl)
  1684. {
  1685.     NewUndo((*frHndl)->d.doc.root);
  1686. }
  1687.  
  1688.  
  1689.  
  1690.