home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Macintosh Sample Code / SC.010.EditTextCdev / EditCdev.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-12  |  9.5 KB  |  282 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Macintosh Developer Technical Support
  4. #
  5. #    EditText Sample Control Panel Device
  6. #
  7. #    EditCdev
  8. #
  9. #    EditCdev.c    -    C Source
  10. #
  11. #    Copyright © 1988 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.00                8/88
  15. #                1.10                6/92
  16. #
  17. #    Components:    EditCdev.p            August 1, 1988
  18. #                EditCdev.c            August 1, 1988
  19. #                EditCdev.r            August 1, 1988
  20. #                PEditCdev.make        August 1, 1988
  21. #                CEditCdev.make        August 1, 1988
  22. #                TCEditCdev.π        June 12, 1992
  23. #                TCEditCdev.π.rsrc    June 12, 1992
  24. #
  25. #    EditCdev is a sample Control Panel device (cdev) that 
  26. #    demonstrates the usage of the edit-related messages.  
  27. #    EditCdev demonstrates how to implement an editText item
  28. #    in a Control Panel Device.  It utilizes the new undo, cut, copy,
  29. #    paste, and delete messages that are sent to cdevs in
  30. #    response to user menu selections.
  31. #
  32. #    It is comprised of two editText items that can be edited 
  33. #    and moved between via the mouse or tab key.
  34. #
  35. ------------------------------------------------------------------------------*/
  36.  
  37. #include <Types.h>
  38. #include <Memory.h>
  39. #include <Quickdraw.h>
  40. #include <TextEdit.h>
  41. #include <Dialogs.h>
  42. #include <Events.h>
  43. #include <Devices.h>
  44. #include <Scrap.h>
  45. #include <Packages.h>
  46. #include <TextEdit.h>
  47.  
  48. /* Constants */
  49. #define    textItm        1            /* first editText item in cdev */
  50. #define countItm    4            /* first staticText item for displaying character count */
  51.  
  52. #define    undoDev        9            /* cdev edit messages */
  53. #define    cutDev        10
  54. #define    copyDev        11
  55. #define    pasteDev    12
  56. #define    clearDev    13
  57.  
  58. #define maxLength    50            /* maximum # of characters in each textEdit field < 32K */
  59.  
  60. #define delete        0x08        /* character codes for various keys */
  61. #define tab            0x09
  62. #define leftArrow    0x1c
  63. #define rightArrow    0x1d
  64. #define upArrow        0x1e
  65. #define downArrow    0x1f
  66.  
  67. /*
  68.  *  TYPES:  Storage for CDEV - All permanent CDEV storage must be referred to by a single
  69.  *            handle, so put all permanent variables in a CDEVRec.
  70.  */
  71. typedef struct CDEVRec
  72. {
  73.     Boolean needUpdate;
  74. } CDEVRec, *CDEVPtr, **CDEVHnd;
  75.  
  76.  
  77. /*
  78.  *  PROTOTYPES
  79.  */
  80. void DoEditCommand (short message, DialogPtr CPDialog);
  81. pascal CDEVHnd main(short message, short item, short numItems, short CPanelID,
  82.          EventRecord *theEvent, CDEVHnd cdevStorage, DialogPtr CPDialog);
  83. void UpdateCharCount(DialogPtr CPDialog);
  84.  
  85.  
  86. /* This is the main dispatcher. It must be the first code in the cdev.
  87.  * EditCdev's dispatcher responds only to the following messages from
  88.  * the Control Panel:
  89.  *    
  90.  *        macDev        - To indicate what machines it is available on.
  91.  *        nulDev        - To check if the character count items need to be updated.
  92.  *        initDev        - To set up some temporary storage and get the caret started.
  93.  *        keyEvtDev    - To check for an edit command and do the appropriate action.
  94.  *        cutDev        - To cut the current selection.
  95.  *        copyDev        - To copy the current selection.
  96.  *        pasteDev    - To paste the contents of the clipboard.
  97.  *        clearDev    - To delete the current selection.
  98.  *
  99.  * The Dialog Manager's services are used to handle entry of text, selection
  100.  * of text, editing of text, and moving between the editText items via the
  101.  * tab key. Since the Dialog Manager handles the selection of text, we do not
  102.  * have to be concerned with hitDev messages for the editText items. The only
  103.  * things we have to take care of are calling the Dialog Manager editing
  104.  * routines in response to an edit command, and getting the caret to show up
  105.  * at the beginning. In response to an edit command that was the result of
  106.  * a command-key equivalent, we must also eliminate the event so that it does
  107.  * not get processed as a keyDown by the Dialog Manager. Otherwise, an 'x'
  108.  * would show up in the editText item when the user did a command-x to cut
  109.  * the text.
  110.  *        Also, we must be aware that the dialog manager does not keep track
  111.  * of the length of the characters typed or pasted into the textEdit items.
  112.  * Since the maximum number of characters that may be contained in a single
  113.  * textEdit item is 32K, we must make sure that the user does not enter more
  114.  * than some maximum number of characters.
  115.  */
  116.  
  117.  
  118. pascal CDEVHnd main(short message, short item, short numItems, short CPanelID,
  119.                        EventRecord *theEvent, CDEVHnd cdevStorage, DialogPtr CPDialog)
  120. {
  121. #pragma unused (item, CPanelID)        /* unused formal parameters */
  122.  
  123.     char         tempChar;
  124.     TEHandle    myTEHandle;    /* handle to the current textEdit record */
  125.     short        selLength;    /* length of current selection */
  126.  
  127.     if (message == macDev) return((CDEVHnd) 1);                /* we work on every machine */
  128.     else if (cdevStorage != nil) {
  129.         switch (message) {
  130.             case initDev:                                    /* initialize cdev */
  131.                 cdevStorage = (CDEVHnd)NewHandle(sizeof(CDEVRec));    /* create private storage */
  132.                 SelIText(CPDialog, numItems + textItm, 0, 999); /* make caret show up */
  133.                 break;
  134.                 
  135.             case nulDev:
  136.                 if ((**cdevStorage).needUpdate)        /* character count need updating? */
  137.                 {
  138.                     UpdateCharCount(CPDialog);
  139.                     (**cdevStorage).needUpdate = false;
  140.                 }
  141.                 break;
  142.  
  143.             case hitDev:                                    /* handle hit on item */
  144.             case closeDev:                                    /* clean up and dispose */
  145.             case updateDev:                                    /* handle any update drawing */
  146.             case activDev:                                    /* activate any needed items */
  147.             case deactivDev:                                /* deactivate any needed items */
  148.                 break;
  149.                 
  150.             case keyEvtDev:                                    /* respond to keydown */
  151.                 tempChar = theEvent->message & charCodeMask;/* get the character, and check */
  152.                 if (theEvent->modifiers & cmdKey)            /*  status of command key */
  153.                 {
  154.                     message = nulDev;                        /* start with no message */
  155.                     theEvent->what = nullEvent;                /* and empty event type */
  156.                     
  157.                     switch (tempChar)                        /* set appropriate message */
  158.                     {    
  159.                         case 'X':
  160.                         case 'x':
  161.                             message = cutDev;
  162.                             break;
  163.                         case 'C':
  164.                         case 'c':
  165.                             message = copyDev;
  166.                             break;
  167.                         case 'V':
  168.                         case 'v':
  169.                             message = pasteDev;
  170.                             break;
  171.                     }
  172.                     DoEditCommand(message, CPDialog);        /* Let edit command handler take it */
  173.                 }
  174.                 else    /* not a command key */
  175.                 {
  176.                     /*   We need to update the character count, but only after textEdit has    */
  177.                     /* a chance to deal with it.  Set a flag so that during the next nullEvent */
  178.                     /* we will process the character count update.  If we call UpdateCharCount */
  179.                     /* now, we will display the incorrect count.                               */
  180.                     /*   Also, we must make sure that we are not exceeding our character limit */
  181.                     /* with this character.  (unless it is a tab or backspace, that's OK)       */
  182.                     
  183.                     (**cdevStorage).needUpdate = true;        /* update count during next nulDev */
  184.                     
  185.                     myTEHandle = ((DialogPeek)CPDialog)->textH;
  186.                     selLength = ((**myTEHandle).selEnd-(**myTEHandle).selStart);
  187.  
  188.                     if ( ((**myTEHandle).teLength>=maxLength+selLength) &&
  189.                             (tempChar!=delete) && (tempChar!=tab) &&
  190.                             (tempChar!=leftArrow) && (tempChar!=rightArrow) &&
  191.                             (tempChar!=upArrow) && (tempChar!=downArrow) )
  192.                     {
  193.                         theEvent->what = nullEvent;    /* trash character (textEdit never sees it) */
  194.                         SysBeep(1);
  195.                     }
  196.                 }
  197.                 break;
  198.                 
  199.             case macDev:
  200.             case undoDev:
  201.                 break;
  202.                 
  203.             case cutDev:
  204.             case copyDev:
  205.             case pasteDev:
  206.             case clearDev:
  207.                 DoEditCommand(message, CPDialog);        /* respond to edit command */
  208.                 break;
  209.         }
  210.  
  211.         return (cdevStorage);
  212.     }  /* cdevStorage != nil */
  213.     
  214.     return (nil);    /* if cdevStorage = NIL then ControlPanel will put up memory error. */
  215. }
  216.  
  217. /* Call the appropriate Dialog Manager routine to handle an edit command for */
  218. /* an editText item. It will do *ALMOST* all the work regarding the TEScrap. */
  219. void DoEditCommand(short message, DialogPtr CPDialog)
  220. {
  221.     TEHandle    myTEHandle;        /* Handle to current textEdit record */
  222.     long        scrapLength;    /* length of textEdit scrap */
  223.     long        freeSpace;        /* amount of space before character limit */
  224.     short        selLength;        /* length of current selection */
  225.  
  226.     myTEHandle = ((DialogPeek)CPDialog)->textH;
  227.  
  228.     switch (message)
  229.     {
  230.     case cutDev:
  231.         DlgCut(CPDialog);
  232.         break;
  233.     case copyDev:
  234.         DlgCopy(CPDialog);
  235.         break;
  236.     case pasteDev:
  237.         /* Since the maximum number of characters that a textEdit structure can   */
  238.         /* handle is 32K, and the dialog manager doesn't check for overflow when  */
  239.         /* pasting, we must check for ourselves and only paste as many characters */
  240.         /* as we have room for. */
  241.         scrapLength = TEGetScrapLen();
  242.         selLength = ((**myTEHandle).selEnd-(**myTEHandle).selStart);
  243.         freeSpace = (long)(maxLength-(**myTEHandle).teLength)+selLength;
  244.         if (freeSpace>=scrapLength)    /* enough room for paste? */
  245.             DlgPaste(CPDialog);
  246.         else                        /* not enough room for paste */
  247.         {
  248.             SysBeep(1);
  249.             if (freeSpace>0)        /* any room for paste? */
  250.             {
  251.                 TESetScrapLen(freeSpace);    /* only paste as many characters as free */
  252.                 DlgPaste(CPDialog);
  253.                 TESetScrapLen(scrapLength);    /* restore textEdit scrap length */
  254.             }
  255.         }
  256.         break;
  257.     case clearDev:
  258.         DlgDelete(CPDialog);
  259.         break;
  260.     }
  261.     UpdateCharCount(CPDialog);
  262. }
  263.  
  264. /* Examine the appropriate structures to determine the number of characters in the */
  265. /* acvive TERecord and display in the corresponding static text item. */
  266. void UpdateCharCount(DialogPtr CPDialog)
  267. {
  268.     TEHandle    myTEHandle;
  269.     short        itemType;
  270.     Handle        itemHandle;
  271.     Rect        itemRect;
  272.     Str255        text;
  273.     short        editItem;
  274.  
  275.     myTEHandle = ((DialogPeek)CPDialog)->textH;            /* get handle to TERecord */
  276.     HLock((Handle)myTEHandle);
  277.     NumToString((long)((**myTEHandle).teLength), text);    /* get length of text */
  278.     HUnlock((Handle)myTEHandle);
  279.     editItem=countItm+((DialogPeek)CPDialog)->editField; /* calculate which item to change */
  280.     GetDItem(CPDialog, editItem, &itemType, &itemHandle, &itemRect);
  281.     SetIText(itemHandle, text);                            /* change text item contents */
  282. }