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

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    CPlusTESample
  8. #
  9. #    TESample.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. #include <Types.h>
  86. #include <QuickDraw.h>
  87. #include <Fonts.h>
  88. #include <Events.h>
  89. #include <Controls.h>
  90. #include <Windows.h>
  91. #include <Menus.h>
  92. #include <TextEdit.h>
  93. #include <Dialogs.h>
  94. #include <Desk.h>
  95. #include <Scrap.h>
  96. #include <ToolUtils.h>
  97. #include <Memory.h>
  98. #include <SegLoad.h>
  99. #include <Files.h>
  100. #include <OSUtils.h>
  101. #include <Traps.h>
  102.  
  103. // our class definitions
  104. #include "TEDocument.h"
  105. #include "TESample.h"
  106.  
  107. // ExtremeNeg and ExtremePos are used to set up wide open rectangles and regions.
  108. const short kExtremeNeg = -32768;
  109. const short kExtremePos = 32767 - 1; // required to address an old region bug
  110.  
  111. // kMaxOpenDocuments is used to determine whether a new document can be opened
  112. // or created. We keep track of the number of open documents, and disable the
  113. // menu items that create a new document when the maximum is reached. If the
  114. // number of documents falls below the maximum, the items are enabled again. */
  115. const short    kMaxOpenDocuments = 1;
  116.     
  117. // Define max and min macros for efficiency.
  118. #define max(a,b)        ((a) > (b) ? (a) : (b))
  119. #define min(a,b)        ((a) < (b) ? (a) : (b))
  120.  
  121. // Our application object, initialized in main(). We make it
  122. // global so our functions which don't belong to any class
  123. // can find the active document.
  124. TESample* gTheApplication;
  125.  
  126. // main is the entrypoint to the program
  127. int main()
  128. {
  129.     // Create our application object. This MUST be the FIRST thing
  130.     // done in main(), since it initializes the Toolbox for us.
  131.     gTheApplication = new TESample;
  132.     // Start our main event loop running. This won't return until user quits
  133.     if (gTheApplication != nil)    
  134.       gTheApplication->EventLoop();    
  135.     
  136.     // We always return a value, like good little ANSI worshippers
  137.     return 0;
  138. }
  139.  
  140. // the constructor for our class, called automatically when we create
  141. // an instance of this class. In this particular case, we only want
  142. // one instance since the constructor does all the menu setups and
  143. // creates our (untitled) document.
  144. TESample::TESample()
  145. {
  146.     Handle    menuBar;
  147.  
  148.     // read menus into menu bar
  149.     menuBar = GetNewMBar(rMenuBar);
  150.     // install menus
  151.     SetMenuBar(menuBar);
  152.     DisposHandle(menuBar);
  153.     // add DA names to Apple menu
  154.     AddResMenu(GetMHandle(mApple), 'DRVR');
  155.     DrawMenuBar();
  156.  
  157.     // create empty mouse region
  158.     fMouseRgn = NewRgn();
  159.     // create a single empty document
  160.     DoNew();
  161.     // make sure we have a valid cursor region
  162.     AdjustCursor();
  163. }
  164.  
  165. // Tell TApplication class how much heap we need
  166. long TESample::HeapNeeded()
  167. {
  168.     return (kMinSize * 1024);
  169. }
  170.  
  171. // Calculate a sleep value for WaitNextEvent. This takes into account the things
  172. // that DoIdle does with idle time.
  173.  
  174. unsigned long TESample::SleepVal()
  175. {
  176.     unsigned long sleep;
  177.  
  178.     sleep = kMaxSleepTime;                // default value for sleep
  179.     // if we aren't in background, let document tell us how long to sleep
  180.     if ((!fInBackground) && (fCurDoc != nil))
  181.       sleep = min(sleep,fCurDoc->CalcIdle());
  182.     return sleep;
  183. }
  184.  
  185. // This is called whenever we get a null event et al.
  186. // It takes care of necessary periodic actions. For this program,
  187. // it calls TEIdle.
  188.  
  189. void TESample::DoIdle()
  190. {
  191.     TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
  192.  
  193.     if ( fTECurDoc != nil )
  194.       fTECurDoc->DoIdle();
  195. } // DoIdle
  196.  
  197. // Change the cursor's shape, depending on its position. This also calculates a
  198. // region that includes the cursor for WaitNextEvent.
  199.  
  200. void TESample::AdjustCursor()
  201. {
  202.     TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
  203.  
  204.     // notice that we don't change cursor if front window isn't ours
  205.     if ( (!fInBackground) && (fTECurDoc != nil) )
  206.       {
  207.         RgnHandle    arrowRgn;
  208.         RgnHandle    iBeamRgn;
  209.         Point        mouse;
  210.         
  211.         // get mouse location and convert to global coordinates
  212.         GetMouse(&mouse);
  213.         LocalToGlobal(&mouse);
  214.  
  215.         // calculate regions for different cursor shapes
  216.         arrowRgn = NewRgn();
  217.         iBeamRgn = NewRgn();
  218.         // start arrowRgn wide open
  219.         SetRectRgn(arrowRgn, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
  220.         // calculate iBeamRgn
  221.         fTECurDoc->GetVisTERgn(iBeamRgn);
  222.         // subtract iBeamRgn from arrowRgn 
  223.         DiffRgn(arrowRgn, iBeamRgn, arrowRgn);
  224.  
  225.         // change the cursor and the region parameter 
  226.         if (PtInRgn(mouse, iBeamRgn))
  227.           {
  228.             SetCursor(*GetCursor(iBeamCursor));
  229.             CopyRgn(iBeamRgn, fMouseRgn);
  230.           }
  231.         else
  232.           {
  233.             SetCursor(&qd.arrow);
  234.             CopyRgn(arrowRgn, fMouseRgn);
  235.           }
  236.         // get rid of regions we don't need anymore
  237.         DisposeRgn(arrowRgn);
  238.         DisposeRgn(iBeamRgn);
  239.       }
  240. } // AdjustCursor
  241.  
  242. // Enable and disable menus based on the current state. The
  243. // user can only select enabled menu items. We set up all the
  244. // menu items before calling MenuSelect or MenuKey, since
  245. // these are the only times that a menu item can be selected.
  246. // Note that MenuSelect is also the only time the user will
  247. // see menu items. This approach to deciding what enable/
  248. // disable state a menu item has the advantage of
  249. // concentrating all the decision-making in one routine, as
  250. // opposed to being spread throughout the application. Other
  251. // application designs may take a different approach that may
  252. // or may not be as valid. 
  253.  
  254. void TESample::AdjustMenus()
  255. {
  256.     WindowPtr    frontmost;
  257.     MenuHandle    menu;
  258.     long        offset;
  259.     Boolean        undo;
  260.     Boolean        cutCopyClear;
  261.     Boolean        paste;
  262.     TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
  263.  
  264.     frontmost = FrontWindow();
  265.  
  266.     menu = GetMHandle(mFile);
  267.     if ( fDocList->NumDocs() < kMaxOpenDocuments )
  268.       EnableItem(menu, iNew);            // New is enabled when we can open more documents 
  269.     else DisableItem(menu, iNew);
  270.     if ( frontmost != (WindowPtr) nil )    // Close is enabled when there is a window to close 
  271.       EnableItem(menu, iClose);
  272.     else DisableItem(menu, iClose);
  273.  
  274.     menu = GetMHandle(mEdit);
  275.     undo = false;
  276.     cutCopyClear = false;
  277.     paste = false;
  278.     if (frontmost != nil)
  279.       {
  280.         if (fTECurDoc == nil)
  281.           {
  282.             undo = true;                // all editing is enabled for DA windows 
  283.             cutCopyClear = true;
  284.             paste = true;
  285.           }
  286.         else
  287.           {
  288.             // Cut, Copy, and Clear is enabled for app. windows with selections 
  289.             if ( fTECurDoc->HaveSelection() )
  290.               cutCopyClear = true;
  291.             // If we have any TEXT in the scrap, enable paste
  292.             if ( GetScrap(nil, 'TEXT', &offset) )
  293.                 paste = true; 
  294.           }
  295.       }
  296.     if ( undo )
  297.       EnableItem(menu, iUndo);
  298.     else DisableItem(menu, iUndo);
  299.     if ( cutCopyClear )
  300.       {
  301.         EnableItem(menu, iCut);
  302.         EnableItem(menu, iCopy);
  303.         EnableItem(menu, iClear);
  304.       } 
  305.     else
  306.       {
  307.         DisableItem(menu, iCut);
  308.         DisableItem(menu, iCopy);
  309.         DisableItem(menu, iClear);
  310.       }
  311.     if ( paste )
  312.         EnableItem(menu, iPaste);
  313.     else DisableItem(menu, iPaste);
  314. } // AdjustMenus
  315.  
  316.  
  317. // This is called when an item is chosen from the menu bar (after calling
  318. // MenuSelect or MenuKey). It does the right thing for each command.
  319.  
  320. void TESample::DoMenuCommand(short menuID, short menuItem)
  321. {
  322.     short        itemHit;
  323.     Str255        daName;
  324.     short        daRefNum;
  325.     WindowPtr    window;
  326.     TEDocument* fTECurDoc = (TEDocument*) fCurDoc;
  327.  
  328.     window = FrontWindow();
  329.     switch ( menuID )
  330.       {
  331.         case mApple:
  332.             switch ( menuItem )
  333.               {
  334.                 case iAbout:        // bring up alert for About 
  335.                     itemHit = Alert(rAboutAlert, nil);
  336.                     break;
  337.                 default:            // all non-About items in this menu are DAs et al 
  338.                     GetItem(GetMHandle(mApple), menuItem, daName);
  339.                     daRefNum = OpenDeskAcc(daName);
  340.                     break;
  341.               }
  342.             break;
  343.         case mFile:
  344.             switch ( menuItem )
  345.               {
  346.                 case iNew:
  347.                     DoNew();
  348.                     break;
  349.                 case iClose:
  350.                     if (fTECurDoc != nil)
  351.                       {
  352.                         fDocList->RemoveDoc(fTECurDoc);
  353.                         fTECurDoc->DoClose();
  354.                       }
  355.                     else CloseDeskAcc(((WindowPeek) fWhichWindow)->windowKind);
  356.                     // make sure our current document/window references are valid
  357.                     fWhichWindow = FrontWindow();
  358.                     if (fWhichWindow != nil)
  359.                       {
  360.                         fCurDoc = fDocList->FindDoc(fWhichWindow);
  361.                         SetPort(fWhichWindow);
  362.                       }
  363.                     else fCurDoc = nil;
  364.                     break;
  365.                 case iQuit:
  366.                     Terminate();
  367.                     break;
  368.               }
  369.             break;
  370.         case mEdit:                    // call SystemEdit for DA editing & MultiFinder 
  371.             if ( !SystemEdit(menuItem-1) )
  372.               {
  373.                 switch ( menuItem )
  374.                   {
  375.                     case iCut:
  376.                         fTECurDoc->DoCut();
  377.                         break;
  378.                     case iCopy:
  379.                         fTECurDoc->DoCopy();
  380.                         break;
  381.                     case iPaste:
  382.                         fTECurDoc->DoPaste();
  383.                         break;
  384.                     case iClear:
  385.                         fTECurDoc->DoClear();
  386.                         break;
  387.                    }
  388.               }
  389.             break;
  390.       }
  391.     HiliteMenu(0);                    // unhighlight what MenuSelect (or MenuKey) hilited 
  392. } // DoMenuCommand
  393.  
  394. // Create a new document and window. 
  395.  
  396. void TESample::DoNew()
  397. {
  398.     TEDocument* tDoc;
  399.  
  400.     tDoc = new TEDocument(rDocWindow);
  401.     // if we didn't get an allocation error, add it to list
  402.     if (tDoc != nil)
  403.       fDocList->AddDoc(tDoc);
  404. } // DoNew
  405.  
  406. // Clean up the application and exits. You might want to close all
  407. // of your documents (and ask the user to save them) here.
  408.  
  409. void TESample::Terminate()
  410. {
  411.     ExitLoop();
  412. } // Terminate
  413.  
  414.