home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / scnote / cplussmp.014 / TEDocument.cp < prev    next >
Encoding:
Text File  |  1989-10-01  |  23.3 KB  |  819 lines

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    CPlusTESample
  8. #
  9. #    TEDocument.cp    -    C++ source
  10. #
  11. #    Copyright ⌐ 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    
  15. #            1.20                    10/89
  16. #            1.10                     07/89
  17. #            1.00                     04/89
  18. #    
  19. #    Components:
  20. #            CPlusTESample.make        October 1, 1989
  21. #            TApplicationCommon.h    October 1, 1989
  22. #            TApplication.h            October 1, 1989
  23. #            TDocument.h                October 1, 1989
  24. #            TECommon.h                October 1, 1989
  25. #            TESample.h                October 1, 1989
  26. #            TEDocument.h            October 1, 1989
  27. #            TApplication.cp            October 1, 1989
  28. #            TDocument.cp            October 1, 1989
  29. #            TESample.cp                October 1, 1989
  30. #            TEDocument.cp            October 1, 1989
  31. #            TESampleGlue.a            October 1, 1989
  32. #            TApplication.r            October 1, 1989
  33. #            TESample.r                October 1, 1989
  34. #
  35. #    CPlusTESample is an example application that demonstrates
  36. #    how to initialize the commonly used toolbox managers,
  37. #    operate successfully under MultiFinder, handle desk
  38. #    accessories and create, grow, and zoom windows. The
  39. #    fundamental TextEdit toolbox calls and TextEdit autoscroll
  40. #    are demonstrated. It also shows how to create and maintain
  41. #    scrollbar controls. 
  42. #
  43. #    This version of TESample has been substantially reworked in
  44. #    C++ to show how a "typical" object oriented program could
  45. #    be written. To this end, what was once a single source code
  46. #    file has been restructured into a set of classes which
  47. #    demonstrate the advantages of object-oriented programming.
  48. #
  49. ------------------------------------------------------------------------------*/
  50.  
  51.  
  52. /*
  53. Segmentation strategy:
  54.  
  55.     This program has only one segment, since the issues
  56.     surrounding segmentation within a class's methods have
  57.     not been investigated yet. We DO unload the data
  58.     initialization segment at startup time, which frees up
  59.     some memory 
  60.  
  61. SetPort strategy:
  62.  
  63.     Toolbox routines do not change the current port. In
  64.     spite of this, in this program we use a strategy of
  65.     calling SetPort whenever we want to draw or make calls
  66.     which depend on the current port. This makes us less
  67.     vulnerable to bugs in other software which might alter
  68.     the current port (such as the bug (feature?) in many
  69.     desk accessories which change the port on OpenDeskAcc).
  70.     Hopefully, this also makes the routines from this
  71.     program more self-contained, since they don't depend on
  72.     the current port setting. 
  73.  
  74. Clipboard strategy:
  75.  
  76.     This program does not maintain a private scrap.
  77.     Whenever a cut, copy, or paste occurs, we import/export
  78.     from the public scrap to TextEdit's scrap right away,
  79.     using the TEToScrap and TEFromScrap routines. If we did
  80.     use a private scrap, the import/export would be in the
  81.     activate/deactivate event and suspend/resume event
  82.     routines. 
  83. */
  84.  
  85. // Mac Includes
  86. #include <Types.h>
  87. #include <QuickDraw.h>
  88. #include <Fonts.h>
  89. #include <Events.h>
  90. #include <Controls.h>
  91. #include <Windows.h>
  92. #include <Menus.h>
  93. #include <TextEdit.h>
  94. #include <Dialogs.h>
  95. #include <Desk.h>
  96. #include <Scrap.h>
  97. #include <ToolUtils.h>
  98. #include <Memory.h>
  99. #include <SegLoad.h>
  100. #include <Files.h>
  101. #include <OSUtils.h>
  102. #include <Traps.h>
  103.  
  104. #include "TEDocument.h"
  105. #include "TESample.h"
  106.  
  107. extern "C" { 
  108.     // prototypes for functions that don't belong to any one class
  109.     pascal ClikLoopProcPtr GetOldClikLoop();
  110.     pascal void PascalClikLoop();
  111.     void CommonAction(ControlHandle control,short* amount);
  112.     pascal void VActionProc(ControlHandle control,short part);
  113.     pascal void HActionProc(ControlHandle control,short part);
  114.     // this routine is written in Assembler, since it needs to tweak registers
  115.     pascal void ASMCLIKLOOP();
  116. };
  117.  
  118. // kTextMargin is the number of pixels we leave blank at the edge of the window.
  119. const short kTextMargin = 2;
  120.  
  121. // kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
  122. // destination rectangle so that word wrap and horizontal scrolling can be
  123. // demonstrated.
  124. const short    kMaxDocWidth = 576;
  125.     
  126. // kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
  127. // is called.
  128. const short    kMinDocDim = 64;
  129.     
  130. // kMaxTELength is an arbitrary number used to limit the length of text in the TERec
  131. // so that various errors won't occur from too many characters being in the text.
  132. const short    kMaxTELength = 32000;
  133.  
  134. // kControlInvisible is used the same way to 'turn on' the control.
  135. const short kControlVisible = 0xFF;
  136.  
  137. // ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  138. // values for control positioning and sizing.
  139. const short kScrollbarAdjust = 15;
  140. const short kGrowboxAdjust = 15;
  141. const short kScrollbarWidth = 16;
  142.  
  143. // kTESlop provides some extra security when pre-flighting edit commands.
  144. const short kTESlop = 1024;
  145.  
  146. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  147. // to have borders coincide with the growbox.
  148. const short kScrollTweek = 2;
  149.     
  150. // kCrChar is used to match with a carriage return when calculating the
  151. // number of lines in the TextEdit record. kDelChar is used to check for
  152. // delete in keyDowns.
  153. const short kCrChar = 13;
  154. const short kDelChar = 8;
  155.  
  156. // notice that we pass the resID parameter up to our base class,
  157. // which actually creates the window for us
  158. TEDocument::TEDocument(short resID)    : (resID)
  159. {
  160.     Boolean good;
  161.     Rect destRect, viewRect;
  162.  
  163.     good = false;
  164.     SetPort(fDocWindow);
  165.     GetTERect(&viewRect);
  166.     destRect = viewRect;
  167.     destRect.right = destRect.left + kMaxDocWidth;
  168.     fDocTE = TENew(&destRect, &viewRect);
  169.     
  170.     good = fDocTE != nil;    // if TENew succeeded, we have a good document 
  171.  
  172.     if ( good )
  173.       {
  174.         // set up TE record
  175.         AdjustViewRect();
  176.         TEAutoView(true, fDocTE);
  177.         fDocClik = (*fDocTE)->clikLoop;
  178.         (*fDocTE)->clikLoop = (ClikLoopProcPtr) ASMCLIKLOOP;
  179.  
  180.         // get vertical scrollbar
  181.         fDocVScroll = GetNewControl(rVScroll, fDocWindow);
  182.         good = (fDocVScroll != nil);
  183.       }
  184.     if ( good)
  185.       {
  186.         fDocHScroll = GetNewControl(rHScroll, fDocWindow);
  187.         good = (fDocHScroll != nil);
  188.       }
  189.     
  190.     if ( good )                // good? ╤ adjust & draw the controls, draw the window
  191.       {
  192.         AdjustScrollValues(true);
  193.         ShowWindow(fDocWindow);
  194.       }
  195.     else
  196.       {
  197.         // tell user we failed
  198.         AlertUser(kTEDocErrStrings,eNoWindow); 
  199.       }
  200. }
  201.  
  202. // At this point, if there was a document associated with a
  203. // window, you could do any document saving processing if it is 'dirty'.
  204. // DoCloseWindow would return true if the window actually closed, i.e.,
  205. // the user didn╒t cancel from a save dialog. This result is handy when
  206. // the user quits an application, but then cancels the save of a document
  207. // associated with a window.
  208.  
  209. TEDocument::~TEDocument()
  210. {
  211.     HideWindow(fDocWindow);
  212.     if ( fDocTE != nil )
  213.       {
  214.         TEDispose(fDocTE);            // dispose the TEHandle if we got far enough to make one 
  215.       }
  216.     if ( fDocVScroll != nil )
  217.       {
  218.         DisposeControl(fDocVScroll);
  219.       }
  220.     if ( fDocHScroll != nil )
  221.       {
  222.         DisposeControl(fDocHScroll);
  223.       }
  224.     // base class destructor will dispose of window
  225. }
  226.  
  227. void TEDocument::DoZoom(short partCode)
  228. {
  229.     Rect tRect;
  230.  
  231.     tRect = fDocWindow->portRect;
  232.     EraseRect(&tRect);
  233.     ZoomWindow(fDocWindow, partCode, fDocWindow == FrontWindow());
  234.     AdjustScrollbars(true);        // adjust, redraw anyway 
  235.     AdjustTE();
  236.     InvalRect(&tRect);            // invalidate the whole content 
  237.     // the scrollbars were taken care of by AdjustScrollbars, so validate ╒em 
  238.     tRect = (*fDocVScroll)->contrlRect;
  239.     ValidRect(&tRect);
  240.     tRect = (*fDocHScroll)->contrlRect;
  241.     ValidRect(&tRect);
  242. }
  243.  
  244. // Called when a mouseDown occurs in the grow box of an active window. 
  245.  
  246. void TEDocument::DoGrow(EventRecord* theEvent)
  247. {
  248.     long growResult;
  249.     Rect tRect, tRect2;
  250.     
  251.     tRect = qd.screenBits.bounds;
  252.     tRect.left = kMinDocDim;
  253.     tRect.top = kMinDocDim;
  254.     growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
  255.     // see if it really changed size 
  256.     if ( growResult != 0 )
  257.       {
  258.         tRect = (*fDocTE)->viewRect;
  259.         SizeWindow(fDocWindow, LoWrd(growResult), HiWrd(growResult), true);
  260.         AdjustScrollbars(true);
  261.         AdjustTE();
  262.         // calculate & validate the region that hasn╒t changed so it won╒t get redrawn
  263.         // Note: we copy rectangles so that we don't take address of object fields.
  264.         tRect2 = (*fDocTE)->viewRect;
  265.         (void) SectRect(&tRect, &tRect2, &tRect);
  266.         tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
  267.         ValidRect(&tRect);
  268.         tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
  269.         tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
  270.       }
  271. }
  272.  
  273. void TEDocument::DoContent(EventRecord* theEvent)
  274. {
  275.     Point mouse;
  276.     ControlHandle control;
  277.     short part, value;
  278.     Boolean shiftDown;
  279.     Rect teRect;
  280.  
  281.     SetPort(fDocWindow);
  282.     mouse = theEvent->where;                            // get the click position 
  283.     GlobalToLocal(&mouse);
  284.     GetTERect(&teRect);
  285.     if ( PtInRect(mouse, &teRect) )
  286.       {
  287.         // see if we need to extend the selection
  288.         shiftDown = (theEvent->modifiers & shiftKey) != 0;    // extend if Shift is down
  289.         TEClick(mouse, shiftDown, fDocTE);
  290.       }
  291.     else
  292.       {
  293.         part = FindControl(mouse, fDocWindow, &control);
  294.         switch ( part )
  295.           {
  296.             case 0:
  297.                 // do nothing if not in a control
  298.                 break;
  299.             case inThumb:
  300.                 value = GetCtlValue(control);
  301.                 part = TrackControl(control, mouse, nil);
  302.                 if ( part != 0 )
  303.                   {
  304.                     value -= GetCtlValue(control);
  305.                     // value now has CHANGE in value; if value changed, scroll 
  306.                     if ( value != 0 )
  307.                         if ( control == fDocVScroll )
  308.                             TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
  309.                         else TEScroll(value, 0, fDocTE);
  310.                   }
  311.                 break;
  312.             default:                        // they clicked in an arrow, so track & scroll 
  313.                 if ( control == fDocVScroll )
  314.                     value = TrackControl(control, mouse, (ProcPtr) VActionProc);
  315.                 else value = TrackControl(control, mouse, (ProcPtr) HActionProc);
  316.                 break;
  317.           }
  318.       }
  319. }
  320.  
  321. void TEDocument::DoKeyDown(EventRecord* theEvent)
  322. {
  323.     char key;
  324.  
  325.     if (theEvent->modifiers & cmdKey)    // don't process command characters
  326.       return;
  327.     key = (char) (theEvent->message & charCodeMask);
  328.     // we have a char. for our window; see if we are still below TextEdit╒s
  329.     // limit for the number of characters
  330.     if ((key == kDelChar) ||
  331.         ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart) + 1 < kMaxTELength) )
  332.       {
  333.         TEKey(key, fDocTE);
  334.         AdjustScrollbars(false);
  335.         AdjustTE();
  336.       }
  337.     else AlertUser(kTEDocErrStrings,eExceedChar);
  338. }
  339.  
  340. void TEDocument::DoActivate(Boolean becomingActive)
  341. {
  342.     if ( becomingActive )
  343.       {
  344.         RgnHandle    tempRgn;
  345.         RgnHandle    clipRgn;
  346.         Rect        growRect;
  347.         Rect        tRect;
  348.  
  349.         // since we don╒t want TEActivate to draw a selection in an area where
  350.         // we╒re going to erase and redraw, we╒ll clip out the update region
  351.         // before calling it.
  352.         tempRgn = NewRgn();
  353.         clipRgn = NewRgn();
  354.         // save old update region
  355.         CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn);
  356.         // put it in local coords
  357.         OffsetRgn(tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top);
  358.         GetClip(clipRgn);
  359.         // subtract updateRgn from clipRgn
  360.         DiffRgn(clipRgn, tempRgn, tempRgn);
  361.         // make it the new clipRgn
  362.         SetClip(tempRgn);
  363.         TEActivate(fDocTE);
  364.         // restore the full-blown clipRgn
  365.         SetClip(clipRgn);
  366.         // get rid of temp regions
  367.         DisposeRgn(tempRgn);
  368.         DisposeRgn(clipRgn);
  369.  
  370.         // the controls must be redrawn on activation:
  371.         (*fDocVScroll)->contrlVis = kControlVisible;
  372.         (*fDocHScroll)->contrlVis = kControlVisible;
  373.         // copy rectangles to avoid unsafe object field references!
  374.         tRect = (*fDocVScroll)->contrlRect; InvalRect(&tRect);
  375.         tRect = (*fDocHScroll)->contrlRect; InvalRect(&tRect);
  376.         // the growbox needs to be redrawn on activation:
  377.         growRect = fDocWindow->portRect;
  378.         // adjust for the scrollbars
  379.         growRect.top = growRect.bottom - kScrollbarAdjust;
  380.         growRect.left = growRect.right - kScrollbarAdjust;
  381.         InvalRect(&growRect);
  382.       }
  383.     else
  384.       {        
  385.         TEDeactivate(fDocTE);
  386.         // the controls must be hidden on deactivation:
  387.         HideControl(fDocVScroll);
  388.         HideControl(fDocHScroll);
  389.         // we draw grow icon immediately, since we deactivate controls
  390.         // immediately, and the update delay looks funny
  391.         DrawGrowIcon(fDocWindow);
  392.       }
  393. }
  394.  
  395. void TEDocument::DoUpdate()
  396. {
  397.     BeginUpdate(fDocWindow);                // this sets up the visRgn 
  398.     if ( ! EmptyRgn(fDocWindow->visRgn) )    // draw if updating needs to be done 
  399.       {
  400.         DrawWindow();
  401.       }
  402.     EndUpdate(fDocWindow);
  403. }
  404.  
  405. // calculate how much idle time we need
  406.  
  407. unsigned long TEDocument::CalcIdle()
  408. {
  409.     if (HaveSelection())
  410.       return GetCaretTime();
  411.     else return kMaxSleepTime;    // if we don't have a selection, we don't need to idle
  412. }
  413.  
  414. // This is called whenever we get a null event et al.
  415. // It takes care of necessary periodic actions. For this program,
  416. // it calls TEIdle.
  417.  
  418. void TEDocument::DoIdle()
  419. {
  420.     TEIdle(fDocTE);
  421. } // DoIdle
  422.  
  423. // Draw the contents of an application window. 
  424.  
  425. void TEDocument::DrawWindow()
  426. {
  427.     Rect tRect;
  428.  
  429.     SetPort(fDocWindow);
  430.     tRect = fDocWindow->portRect;
  431.     EraseRect(&tRect);
  432.     TEUpdate(&tRect, fDocTE);
  433.     DrawControls(fDocWindow);
  434.     DrawGrowIcon(fDocWindow);
  435. } // DrawWindow
  436.  
  437. // Return a rectangle that is inset from the portRect by the size of
  438. // the scrollbars and a little extra margin.
  439.  
  440. void TEDocument::GetTERect(Rect* teRect)
  441. {
  442.     *teRect = fDocWindow->portRect;
  443.     InsetRect(teRect, kTextMargin, kTextMargin);            // adjust for margin 
  444.     teRect->bottom = teRect->bottom - kScrollbarAdjust;    // and for the scrollbars 
  445.     teRect->right = teRect->right - kScrollbarAdjust;
  446. } // GetTERect
  447.  
  448. // setup a region which contains the visible text
  449.  
  450. void TEDocument::GetVisTERgn(RgnHandle rgn)
  451. {
  452.     Rect teRect;
  453.  
  454.     teRect = (*fDocTE)->viewRect;    // get a local copy of viewRect
  455.     SetPort(fDocWindow);            // make sure we have right port
  456.     LocalToGlobal(&TopLeft(teRect));
  457.     LocalToGlobal(&BotRight(teRect));
  458.     RectRgn(rgn, &teRect);
  459.     // we temporarily change the port╒s origin to ╥globalfy╙ the visRgn
  460.     SetOrigin(-(fDocWindow->portBits.bounds.left),
  461.               -(fDocWindow->portBits.bounds.top));
  462.     SectRgn(rgn, fDocWindow->visRgn, rgn);
  463.     SetOrigin(0, 0);
  464. } // GetTERgn
  465.  
  466. // Return boolean value indicating that there is or is not a
  467. // selection in the document
  468.  
  469. Boolean TEDocument::HaveSelection()
  470. {
  471.     if ( (*fDocTE)->selStart < (*fDocTE)->selEnd )
  472.       return true;
  473.     else return false;
  474. }
  475.  
  476. // Update the TERec's view rect so that it is the greatest multiple of
  477. // the lineHeight that still fits in the old viewRect.
  478.  
  479. void TEDocument::AdjustViewRect()
  480. {
  481.     TEPtr te;
  482.     
  483.     te = *fDocTE;
  484.     te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  485.                             * te->lineHeight) + te->viewRect.top;
  486. } // AdjustViewRect
  487.  
  488. // Scroll the TERec around to match up to the potentially updated scrollbar
  489. // values. This is really useful when the window has been resized such that the
  490. // scrollbars became inactive but the TERec was already scrolled.
  491.  
  492. void TEDocument::AdjustTE()
  493. {
  494.     TEPtr te;
  495.     
  496.     te = *fDocTE;
  497.     TEScroll((te->viewRect.left - te->destRect.left) - GetCtlValue(fDocHScroll),
  498.              (te->viewRect.top - te->destRect.top) - 
  499.                  (GetCtlValue(fDocVScroll) * te->lineHeight),
  500.              fDocTE);
  501. } // AdjustTE
  502.  
  503. // Re-calculate the position and size of the viewRect and the scrollbars.
  504. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  505. // to have borders coincide with the growbox.
  506.  
  507. void TEDocument::AdjustScrollSizes()
  508. {
  509.     Rect teRect;
  510.     
  511.     GetTERect(&teRect);
  512.     (*fDocTE)->viewRect = teRect;
  513.     AdjustViewRect();
  514.     MoveControl(fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1);
  515.     SizeControl(fDocVScroll, kScrollbarWidth,
  516.                 fDocWindow->portRect.bottom - fDocWindow->portRect.top -
  517.                     kGrowboxAdjust + kScrollTweek);
  518.     MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
  519.     SizeControl(fDocHScroll,
  520.                 fDocWindow->portRect.right - fDocWindow->portRect.left -
  521.                     kGrowboxAdjust + kScrollTweek,
  522.                 kScrollbarWidth);
  523. } // AdjustScrollSizes
  524.  
  525. // Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
  526. // and we don't want that). If the controls are to be resized as well, call the procedure to do that,
  527. // then call the procedure to adjust the maximum and current values. Finally re-enable the controls
  528. // by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be
  529. // necessary).
  530.  
  531. void TEDocument::AdjustScrollbars(Boolean needsResize)
  532. {
  533.     // First, turn visibility of scrollbars off so we won╒t get unwanted redrawing 
  534.     (*fDocVScroll)->contrlVis = 0;
  535.     (*fDocHScroll)->contrlVis = 0;
  536.     if ( needsResize )
  537.       AdjustScrollSizes();
  538.     AdjustScrollValues(needsResize);
  539.     // Now, restore visibility in case we never had to draw during adjustment 
  540.     (*fDocVScroll)->contrlVis = 0xff;
  541.     (*fDocHScroll)->contrlVis = 0xff;
  542. } // AdjustScrollbars 
  543.  
  544. // Calculate the new control maximum value and current value, whether it is the horizontal or
  545. // vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
  546. // vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
  547. // width to the width of the viewRect. The current values are set by comparing the offset between
  548. // the view and destination rects. If necessary, redraw the control by calling ShowControl.
  549.  
  550. void TEDocument::AdjustHV(Boolean isVert,Boolean mustRedraw)
  551. {
  552.     short value, lines, max;
  553.     short oldValue, oldMax;
  554.     TEPtr te;
  555.     ControlHandle control;
  556.  
  557.     if (isVert)
  558.       control = fDocVScroll;
  559.     else control = fDocHScroll;
  560.     oldValue = GetCtlValue(control);
  561.     oldMax = GetCtlMax(control);
  562.     te = *fDocTE;                            // point to TERec for convenience 
  563.     if ( isVert )
  564.       {
  565.         lines = te->nLines;
  566.         // since nLines isn╒t right if the last character is a return, check for that case
  567.         if ( *(*te->hText + te->teLength - 1) == kCrChar )
  568.           lines += 1;
  569.         max = lines - ((te->viewRect.bottom - te->viewRect.top) /
  570.                        te->lineHeight);
  571.       }
  572.     else max = kMaxDocWidth - (te->viewRect.right - te->viewRect.left);
  573.     
  574.     if ( max < 0 )
  575.       max = 0;
  576.     SetCtlMax(control, max);
  577.     
  578.     // Must deref. after SetCtlMax since, technically, it could draw and therefore move
  579.     // memory. This is why we don╒t just do it once at the beginning.
  580.     te = *fDocTE;
  581.     if ( isVert )
  582.       value = (te->viewRect.top - te->destRect.top) / te->lineHeight;
  583.     else value = te->viewRect.left - te->destRect.left;
  584.     
  585.     if ( value < 0 )
  586.       value = 0;
  587.     else if ( value >  max )
  588.       value = max;
  589.     
  590.     SetCtlValue(control, value);
  591.     // now redraw the control if asked to or if a setting changed 
  592.     if ( mustRedraw || (max != oldMax) || (value != oldValue) )
  593.         ShowControl(control);
  594. } // AdjustHV
  595.  
  596. // Simply call the common adjust routine for the vertical and horizontal scrollbars. 
  597.  
  598. void TEDocument::AdjustScrollValues(Boolean mustRedraw)
  599. {
  600.     AdjustHV(true, mustRedraw);
  601.     AdjustHV(false, mustRedraw);
  602. } // AdjustScrollValues
  603.  
  604. ClikLoopProcPtr TEDocument::GetClikLoop()
  605. {
  606.     return fDocClik;
  607. }
  608.  
  609. TEHandle TEDocument::GetTEHandle()
  610. {
  611.     return fDocTE;
  612. }
  613.  
  614. void TEDocument::DoCut()
  615. {
  616.     long total, contig;
  617.  
  618.     if (ZeroScrap() == noErr)
  619.       {
  620.         PurgeSpace(&total, &contig);
  621.         if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig)
  622.           AlertUser(kTEDocErrStrings,eNoSpaceCut);
  623.         else
  624.           {
  625.             TECut(fDocTE);
  626.             if (TEToScrap() != noErr)
  627.               {
  628.                 AlertUser(kTEDocErrStrings,eNoCut);
  629.                 (void) ZeroScrap();
  630.               }
  631.           }
  632.       }
  633.     AdjustScrollbars(false);
  634.     AdjustTE();
  635. }
  636.  
  637. void TEDocument::DoCopy()
  638. {
  639.     if ( ZeroScrap() == noErr )
  640.       {
  641.         TECopy(fDocTE);                // after copying, export the TE scrap
  642.         if ( TEToScrap() != noErr )
  643.           {
  644.             AlertUser(kTEDocErrStrings,eNoCopy);
  645.             ZeroScrap();
  646.           }
  647.       }
  648.     AdjustScrollbars(false);
  649.     AdjustTE();
  650. }
  651.  
  652. void TEDocument::DoPaste()
  653. {
  654.     Handle aHandle;
  655.     long oldSize, newSize;
  656.     OSErr saveErr;
  657.  
  658.     if ( TEFromScrap() == noErr )
  659.       {
  660.         if ( TEGetScrapLen() + ((*fDocTE)->teLength -
  661.              ((*fDocTE)->selEnd - (*fDocTE)->selStart)) > kMaxTELength )
  662.           AlertUser(kTEDocErrStrings,eExceedPaste);
  663.         else
  664.           {
  665.             aHandle = (Handle) TEGetText(fDocTE);
  666.             oldSize = GetHandleSize(aHandle);
  667.             newSize = oldSize + TEGetScrapLen() + kTESlop;
  668.             SetHandleSize(aHandle, newSize);
  669.             saveErr = MemError();
  670.             SetHandleSize(aHandle, oldSize);
  671.             if (saveErr != noErr)
  672.               AlertUser(kTEDocErrStrings,eNoSpacePaste);
  673.             else TEPaste(fDocTE);
  674.           }
  675.       }
  676.     else AlertUser(kTEDocErrStrings,eNoPaste);
  677.     AdjustScrollbars(false);
  678.     AdjustTE();
  679. }
  680.  
  681. void TEDocument::DoClear()
  682. {
  683.     TEDelete(fDocTE);
  684.     AdjustScrollbars(false);
  685.     AdjustTE();
  686. }
  687.  
  688. /*
  689.     Routines used by this class, which don't belong to the class since we use
  690.     them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  691. */
  692.  
  693. // we refer back to the owning application so that we can get access to
  694. // the document list, to find the current document object.
  695. extern TESample* gTheApplication;
  696.  
  697. // Common algorithm for pinning the value of a control. It returns the actual amount
  698. // the value of the control changed.
  699.  
  700. void CommonAction(ControlHandle control,short* amount)
  701. {
  702.     short        value, max;
  703.     
  704.     value = GetCtlValue(control);
  705.     max = GetCtlMax(control);
  706.     *amount = value - *amount;
  707.     if ( *amount <= 0 )
  708.         *amount = 0;
  709.     else if ( *amount >= max )
  710.         *amount = max;
  711.     SetCtlValue(control, *amount);
  712.     *amount = value - *amount;
  713. } // CommonAction 
  714.  
  715.  
  716. // Determines how much to change the value of the vertical scrollbar by and how
  717. // much to scroll the TE record.
  718.  
  719. pascal void VActionProc(ControlHandle control,short part)
  720. {
  721.     short        amount;
  722.     WindowPtr    window;
  723.     TEPtr        te;
  724.     TEDocument* doc;
  725.  
  726.     if ( part != 0 )
  727.       {
  728.         window = (*control)->contrlOwner;
  729.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  730.         te = *(doc->GetTEHandle());
  731.         switch ( part )
  732.           {
  733.             case inUpButton:
  734.             case inDownButton:        // one line 
  735.                 amount = 1;
  736.                 break;
  737.             case inPageUp:            // one page 
  738.             case inPageDown:
  739.                 amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
  740.                 break;
  741.           }
  742.         if ( (part == inDownButton) || (part == inPageDown) )
  743.             amount = -amount;        // reverse direction for a downer 
  744.         CommonAction(control, &amount);
  745.         if ( amount != 0 )
  746.             TEScroll(0, amount * te->lineHeight, doc->GetTEHandle());
  747.       }
  748. } // VActionProc 
  749.  
  750. // Determines how much to change the value of the horizontal scrollbar by and how
  751. // much to scroll the TE record.
  752.  
  753. pascal void HActionProc(ControlHandle control,short part)
  754. {
  755.     short        amount;
  756.     WindowPtr    window;
  757.     TEPtr        te;
  758.     TEDocument* doc;
  759.  
  760.     if ( part != 0 )
  761.       {
  762.         window = (*control)->contrlOwner;
  763.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  764.         te = *(doc->GetTEHandle());
  765.         switch ( part )
  766.           {
  767.             case inUpButton:
  768.             case inDownButton:        // a few pixels 
  769.                 amount = 4;
  770.                 break;
  771.             case inPageUp:            // a page 
  772.             case inPageDown:
  773.                 amount = te->viewRect.right - te->viewRect.left;
  774.                 break;
  775.           }
  776.         if ( (part == inDownButton) || (part == inPageDown) )
  777.             amount = -amount;        // reverse direction 
  778.         CommonAction(control, &amount);
  779.         if ( amount != 0 )
  780.             TEScroll(amount, 0, doc->GetTEHandle());
  781.       }
  782. } // VActionProc 
  783.  
  784. // Gets called from our assembly language routine, AsmClikLoop, which is in
  785. // turn called by the TEClick toolbox routine. Saves the windows clip region,
  786. // sets it to the portRect, adjusts the scrollbar values to match the TE scroll
  787. // amount, then restores the clip region.
  788.  
  789. pascal void PascalClikLoop()
  790. {
  791.     RgnHandle    region;
  792.     WindowPtr     wind;
  793.     TEDocument* doc;
  794.  
  795.     wind = FrontWindow();
  796.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(wind);
  797.     region = NewRgn();
  798.     GetClip(region);    // save clip 
  799.     ClipRect(&wind->portRect);
  800.     doc->AdjustScrollValues(false);
  801.     SetClip(region);    // restore clip 
  802.     DisposeRgn(region);
  803. } // PascalClikLoop 
  804.  
  805. // Gets called from our assembly language routine, AsmClikLoop, which is in
  806. // turn called by the TEClick toolbox routine. It returns the address of the
  807. // default clikLoop routine that was put into the TERec by TEAutoView to
  808. // AsmClikLoop so that it can call it.
  809.  
  810. pascal ClikLoopProcPtr GetOldClikLoop()
  811. {
  812.     TEDocument* doc;
  813.  
  814.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(FrontWindow());
  815.     if (doc == nil)
  816.       return nil;
  817.     return doc->GetClikLoop();
  818. } // GetOldClikLoop
  819.