home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / Demos / Tools / Resorcerer® 1.2.5 / Starter Code Demo / DLOG 128.c next >
Encoding:
C/C++ Source or Header  |  1994-12-08  |  28.5 KB  |  1,124 lines  |  [TEXT/MPCC]

  1. /*
  2.  *    Dialog Module, created by Resorcerer
  3.  */
  4.  
  5. #include <Desk.h>
  6. #include <DiskInit.h>
  7. #include <Dialogs.h>
  8. #include <Files.h>
  9. #include <Fonts.h>
  10. #include <Lists.h>
  11. #include <Menus.h>
  12. #include <Resources.h>
  13. #include <Memory.h>
  14. #include <OSUtils.h>
  15. #include <OSEvents.h>
  16. #include <Packages.h>
  17. #include <Scrap.h>
  18. #include <Script.h>
  19. #include <SegLoad.h>
  20. #include <StdArg.h>
  21. #include <stdio.h>
  22. #include <ToolUtils.h>
  23. #include <Values.h>
  24.  
  25.  
  26. typedef struct {
  27.     ListHandle hndl;
  28.     Rect bounds,scroll,content,dataBounds;
  29.     Point cSize,cell;
  30.     short nCells;
  31.  
  32.     } UserList;
  33.  
  34.  
  35. typedef struct {
  36.     Rect box,bounds,shadow,prompt;
  37.     short currentChoice,menuID;
  38.     MenuHandle menu;
  39.     unsigned char str[256];
  40.     long roomToGrow[2];
  41.     
  42.     } UserPopUp;
  43.  
  44. #define lMargin 13
  45. #define rMargin 5
  46. #define bMargin 5
  47.  
  48. #define thisDialogID 128
  49.  
  50. /* Symbolic Dialog Item Numbers */
  51.  
  52. static enum {
  53.     BUT1_OK = 1,
  54.     BUT2_Cancel,
  55.     BUT3_Click,
  56.     RAD4_Breakfast,
  57.     RAD5_Lunch,
  58.     RAD6_Dinner,
  59.     STXT7,
  60.     RAD8_Midnight,
  61.     USER9,
  62.     LIST10,
  63.     LIST11,
  64.     EDIT12,
  65.     STXT13_Name,
  66.     POP14,
  67.     LASTITEM
  68.     } DialogItemNumber;
  69.  
  70. #define OK_ITEM     BUT1_OK
  71. #define CANCEL_ITEM     BUT2_Cancel
  72.  
  73. /* Useful constants */
  74.  
  75. #ifndef ENTERkey
  76. #define ENTERkey    0x3
  77. #endif
  78. #ifndef DELETEkey
  79. #define DELETEkey    0x8
  80. #endif
  81. #ifndef NIL
  82. #define NIL ((void *)0)
  83. #endif
  84. #ifndef TRUE
  85. #define TRUE 1
  86. #endif
  87. #ifndef FALSE
  88. #define FALSE 0
  89. #endif
  90. #ifndef FRONT_WINDOW
  91. #define FRONT_WINDOW  ((WindowPtr) (-1L))
  92. #endif
  93.  
  94. /* Prototypes */
  95.  
  96. int       DoDialog(void);
  97. DialogPtr OpenThisDialog(void);
  98. void      CloseThisDialog(DialogPtr dlog);
  99. void      DoDialogUpdate(DialogPtr dlog);
  100. void      DoDialogActivate(DialogPtr dlog, int activ);
  101. void      DoDialogContent(DialogPtr dlog, EventRecord *evt);
  102. int       DoDialogItem(DialogPtr dlog, short itemHit);
  103.  
  104. pascal  Boolean MyFilter(DialogPtr dlog, EventRecord *evt, short *itemHit);
  105. Boolean CheckUserItems(Point where, short *itemHit);
  106. int     AnyBadValues(DialogPtr dlog);
  107.  
  108. void    CenterWindow(WindowPtr w, int top);
  109. long    strlen(char *);
  110. char   *strcpy(char *dst, char *src);
  111. char   *PascalToC(unsigned char *pstr);
  112. unsigned char   *CToPascal(char *cstr);
  113. void    PutDlgString(DialogPtr dlog, int item, unsigned char *str, int sel);
  114. void    PutDlgWord(DialogPtr dlog, int item, int val, int sel);
  115. void    PutDlgLong(DialogPtr dlog, int item, long val, int sel);
  116. void    PutDlgChkRadio(DialogPtr dlog, int item, int val);
  117. int     GetDlgString(DialogPtr dlog, int item, unsigned char *str);
  118. int     GetDlgWord(DialogPtr dlog, int item, short *val);
  119. int     GetDlgLong(DialogPtr dlog, int item, long *val);
  120. int     GetDlgChkRadio(DialogPtr dlog, int item);
  121. int     TextSelected(DialogPtr dlog);
  122. OSType  CanPaste(int n, ...);
  123. void    FrameDefault(DialogPtr dlog, int item, int frame);
  124. void    GetDlgPanel(DialogPtr dlog, int item, Rect *panel);
  125.  
  126. int     BuildList(DialogPtr dlog, int item, int csize, UserList *l);
  127. unsigned char   *GetCellData(UserList *l, short i, short *len);
  128. int     GetLengthList(UserList *l);
  129. int     DoOpenCell(UserList *l, unsigned char *buf, short *len);
  130.  
  131. void    DrawPopUp(UserPopUp *popup);
  132. void    DrawPopUpTriangle(Rect *bounds);
  133. void    TruncPopUp(UserPopUp *popup);
  134. int     InitPopUp(DialogPtr dlog, UserPopUp *p, int item, int pItem, int menuID, int choice);
  135. void    SetPopUpChoice(UserPopUp *p, int choice);
  136. void    DisposePopUp(UserPopUp *p);
  137. int     DoUserPopUp(UserPopUp *p);
  138.  
  139. static Point where;
  140. static int modifiers;
  141.  
  142. /* Current button (item number), for each radio group */
  143.  
  144. static short group1;
  145.  
  146. /* Lists and/or popups */
  147.  
  148. static UserList list10;
  149. static UserList list11;
  150. static UserPopUp popup14;
  151.  
  152. /*
  153.  *    Display this modal dialog.  Return TRUE if OK, FALSE if CANCEL or error.
  154.  *    If the dialog displays values from outside this module, you should either
  155.  *    import them from globals, or change the argument list of this routine to
  156.  *    bring them in and pass them to OpenThisDialog(), DoDialogItem(), etc.
  157.  */
  158.  
  159. int DoDialog()
  160.     {
  161.         short itemHit,okay=FALSE,keepGoing=TRUE;
  162.         DialogPtr dlog=NIL; GrafPtr oldPort;
  163.         ModalFilterUPP MyFilterUPP;
  164.  
  165.         GetPort(&oldPort);
  166.  
  167.         /* On PowerPC, need a RoutineDescriptor from heap; on 68K, no allocation */
  168.         
  169.         MyFilterUPP = NewModalFilterProc(MyFilter);
  170.         if (MyFilterUPP == NIL) goto cleanUp;
  171.  
  172.         /* Build dialog window and install its item values */
  173.         
  174.         dlog = OpenThisDialog();
  175.         if (dlog == NIL) goto cleanUp;
  176.  
  177.         /* Entertain filtered user events until dialog is dismissed */
  178.         
  179.         while (keepGoing) {
  180.             ModalDialog(MyFilterUPP,&itemHit);
  181.             keepGoing = DoDialogItem(dlog,itemHit);
  182.             }
  183.         
  184.         /*
  185.          *    Do final processing of item values, such as exporting them to caller.
  186.          *    DoDialogItem() has already called AnyBadValues().
  187.          */
  188.         
  189.         okay = (itemHit == OK_ITEM);
  190.         if (okay) {        /* Or whatever is equivalent */
  191.             }
  192.  
  193.         /* That's all, folks! */
  194.  
  195. cleanUp:        
  196.         if (dlog) CloseThisDialog(dlog);
  197.         if (MyFilterUPP) DisposeRoutineDescriptor(MyFilterUPP);
  198.         SetPort(oldPort);
  199.         
  200.         return(okay);
  201.     }
  202.  
  203. /*
  204.  *    We have to have a filter function, at the very least so that we can outline
  205.  *    any default button, entertain keyboard editing commands, cmd-period canceling, etc.
  206.  *    Note that you do not need to have a special user item covering the default button
  207.  *    in your dialog item list.
  208.  */
  209.  
  210. static pascal Boolean MyFilter(DialogPtr dlog, EventRecord *evt, short *itemHit)
  211.     {
  212.         Boolean ans=FALSE,doHilite=FALSE; WindowPtr w;
  213.         short type,ch; Handle hndl; Rect box;
  214.         static long then; static Point clickPt;
  215.         
  216.         w = (WindowPtr)(evt->message);
  217.         switch(evt->what) {
  218.             case updateEvt:
  219.                 if (w == dlog) {
  220.                     /* Update our dialog contents */
  221.                     DoDialogUpdate(dlog);
  222.                     ans = TRUE; *itemHit = 0;
  223.                     }
  224.                  else {
  225.                     /*
  226.                      *    Call your main event loop DoUpdate(w) routine here if you
  227.                      *    don't want unsightly holes in background windows caused
  228.                      *    by nested alerts, balloon help, or screen savers (see
  229.                      *    Tech Note #304).
  230.                      */
  231.                     }
  232.                 break;
  233.             case activateEvt:
  234.                 if (w == dlog) {
  235.                     DoDialogActivate(dlog,(evt->modifiers & activeFlag)!=0);
  236.                     *itemHit = 0;
  237.                     }
  238.                  else {
  239.                     /*
  240.                      *    Call your main event loop DoActivate(w) routine here if
  241.                      *    you want to deactivate the former frontmost window, in order
  242.                      *    to unhighlight any selection, scroll bars, etc.
  243.                      */
  244.                     }
  245.                 break;
  246.             case mouseDown:
  247.             case mouseUp:
  248.                 where = evt->where;        /* Make info available to DoDialog() */
  249.                 GlobalToLocal(&where);
  250.                 modifiers = evt->modifiers;
  251.                 ans = CheckUserItems(where,itemHit);
  252.                 break;
  253.             case keyDown:
  254.                 if ((ch=(unsigned char)evt->message)=='\r' || ch==ENTERkey) {
  255.                     *itemHit = OK_ITEM /* Default Item Number here */;
  256.                     doHilite = ans = TRUE;
  257.                     }
  258.                  else if (evt->modifiers & cmdKey) {
  259.                     ch = (unsigned char)evt->message;
  260.                     switch(ch) {
  261.                         case 'x':
  262.                         case 'X':
  263.                             if (TextSelected(dlog))
  264.                                 { SystemEdit(3); ZeroScrap(); DlgCut(dlog); TEToScrap(); }
  265.                              else {
  266.                                 /* Cut from anything else cuttable, like a list */
  267.                                 }
  268.                             break;
  269.                         case 'c':
  270.                         case 'C':
  271.                             if (TextSelected(dlog))
  272.                                 { SystemEdit(3); ZeroScrap(); DlgCopy(dlog); TEToScrap(); }
  273.                              else {
  274.                                 /* Copy from anything else copyable, like a list */
  275.                                 }
  276.                             break;
  277.                         case 'v':
  278.                         case 'V':
  279.                             if (CanPaste(1,'TEXT'))
  280.                                 { TEFromScrap(); DlgPaste(dlog); }
  281.                              else {
  282.                                  /* Deal with any other pasteable scraps here */
  283.                                 }
  284.                             break;
  285.                         case 'a':
  286.                         case 'A':
  287.                             if (((DialogPeek)dlog)->editField >= 0) {
  288.                                 /* Dialog has text edit item: select all */
  289.                                 SelIText(dlog,((DialogPeek)dlog)->editField+1,0,32767);
  290.                                 }
  291.                              else {
  292.                                 }
  293.                             *itemHit = 0;
  294.                             break;
  295.                         case '.':
  296.                             *itemHit = CANCEL_ITEM;
  297.                             doHilite = TRUE;
  298.                             break;
  299.                         }
  300.                     ans = TRUE;        /* Other cmd-chars ignored */
  301.                     }
  302.                 break;
  303.             }
  304.         if (doHilite) {
  305.             GetDItem(dlog,*itemHit,&type,&hndl,&box);
  306.             /* Reality check */
  307.             if (type == (btnCtrl+ctrlItem)) {
  308.                 long soon = TickCount() + 7;        /* Or whatever feels right */
  309.                 HiliteControl((ControlHandle)hndl,1);
  310.                 while (TickCount() < soon) ;        /* Leave hilited for a bit */
  311.                 }
  312.             }
  313.         return(ans);
  314.     }
  315.  
  316. /*
  317.  * Mouse down event:
  318.  * Check if it's in some user item, and convert to itemHit if appropriate.
  319.  */
  320.  
  321. static Boolean CheckUserItems(Point where, short *itemHit)
  322.     {
  323.         if (PtInRect(where,&popup14.shadow))
  324.             { *itemHit = DoUserPopUp(&popup14) ? POP14 : 0; return(TRUE); }
  325.  
  326.         return(FALSE);
  327.     }
  328.  
  329. /*
  330.  * Redraw the contents of this dialog due to update event.
  331.  * If you have not installed UserItem draw routines, you should redraw
  332.  * them explicitly here; otherwise, UpdtDialog() will call your routines.
  333.  */
  334.  
  335. static void DoDialogUpdate(DialogPtr dlog)
  336.     {
  337.         GrafPtr oldPort;
  338.  
  339.         GetPort(&oldPort); SetPort(dlog);
  340.         BeginUpdate(dlog);
  341.  
  342.         UpdtDialog(dlog,dlog->visRgn);
  343.         FrameDefault(dlog,BUT1_OK,TRUE);
  344.         DrawPopUp(&popup14);
  345.         FrameRect(&list10.bounds);
  346.         LUpdate(((GrafPtr)dlog)->visRgn,list10.hndl);
  347.         FrameRect(&list11.bounds);
  348.         LUpdate(((GrafPtr)dlog)->visRgn,list11.hndl);
  349.  
  350.         EndUpdate(dlog);
  351.         SetPort(oldPort);
  352.     }
  353.  
  354. /*
  355.  * Activate event: Activate or deactivate this dialog and any items in it
  356.  */
  357.  
  358. static void DoDialogActivate(DialogPtr dlog, int activ)
  359.     {
  360.         SetPort(dlog);
  361.         LActivate(activ,list10.hndl);
  362.         LActivate(activ,list11.hndl);
  363.     }
  364.  
  365. /*
  366.  * Build this dialog's window on desktop, and install initial item values.
  367.  * Return the dlog opened, or NIL if error (no resource, no memory).
  368.  */
  369.  
  370. static DialogPtr OpenThisDialog()
  371.     {
  372.         short type; Handle hndl; Rect box; GrafPtr oldPort;
  373.         DialogPtr dlog; unsigned char *p,str[256];
  374.  
  375.         GetPort(&oldPort);
  376.         dlog = GetNewDialog(thisDialogID,NIL,FRONT_WINDOW);
  377.         if (dlog == NIL) { SysBeep(1); return(NIL); }    /* Poor man's error message */
  378.  
  379.         CenterWindow(dlog,0);
  380.         SetPort(dlog);
  381.  
  382.         /* Fill in dialog's values here */
  383.  
  384.         PutDlgChkRadio(dlog,group1 = RAD4_Breakfast, TRUE);
  385.         PutDlgChkRadio(dlog,RAD5_Lunch, FALSE);
  386.         PutDlgChkRadio(dlog,RAD6_Dinner, FALSE);
  387.         PutDlgChkRadio(dlog,RAD8_Midnight, FALSE);
  388.  
  389.         if (!BuildList(dlog,LIST10,16,&list10)) goto broken;
  390.         if (!BuildList(dlog,LIST11,16,&list11)) goto broken;
  391.  
  392.         PutDlgString(dlog,EDIT12,"\pEdit Me" /* Or whatever */, TRUE);
  393.  
  394.         if (!InitPopUp(dlog,&popup14,POP14,0/* Prompt STXT item num should go here */, 128 /* = ResID of MENU */, 1 /* Initial choice */))
  395.             goto broken;
  396.  
  397.         ShowWindow(dlog);
  398.         return(dlog);
  399.  
  400.         /* Error return */
  401. broken:
  402.         CloseThisDialog(dlog);
  403.         SetPort(oldPort);
  404.         return(NIL);
  405.     }
  406.  
  407. /*
  408.  * Clean up any allocated stuff, and return dialog to primordial mists
  409.  */
  410.  
  411. static void CloseThisDialog(DialogPtr dlog)
  412.     {
  413.         if (dlog) {
  414.             if (list10.hndl) { LDispose(list10.hndl); list10.hndl = NIL; }
  415.             if (list11.hndl) { LDispose(list11.hndl); list11.hndl = NIL; }
  416.             DisposePopUp(&popup14);
  417.             DisposeDialog(dlog);    /* Call CloseDialog if you provide storage to GetNewDialog */
  418.         }
  419.     }
  420.  
  421. /*
  422.  * Deal with user clicking on an item in this dialog, either modal or non-modal.
  423.  * The local point is in where; modifiers in modifiers.
  424.  * Returns whether or not the dialog should be closed (keepGoing).
  425.  */
  426.  
  427. static int DoDialogItem(DialogPtr dlog, short itemHit)
  428.     {
  429.         short type,okay=FALSE,keepGoing=TRUE,val;
  430.         Handle hndl; Rect box; Point pt;
  431.         unsigned char *p,str[256];
  432.  
  433.         if (itemHit<1 || itemHit>=LASTITEM)
  434.             return(keepGoing);                /* Only legal items, please */
  435.  
  436.         GetDItem(dlog,itemHit,&type,&hndl,&box);
  437.         switch(type) {
  438.             case ctrlItem+btnCtrl:
  439.                 switch(itemHit) {
  440.                     case BUT1_OK:
  441.                         keepGoing = FALSE; okay = TRUE;
  442.                         break;
  443.                     case BUT2_Cancel:
  444.                         keepGoing = FALSE;
  445.                         break;
  446.                     case BUT3_Click:
  447.                         break;
  448.                     }
  449.                 break;
  450.             case ctrlItem+chkCtrl:
  451.                 break;
  452.             case ctrlItem+radCtrl:
  453.                 switch(itemHit) {
  454.                     case RAD4_Breakfast:        /* Group 1 */
  455.                     case RAD5_Lunch:
  456.                     case RAD6_Dinner:
  457.                     case RAD8_Midnight:
  458.                             if (itemHit != group1) {
  459.                                 SetCtlValue((ControlHandle)hndl,val=!GetCtlValue((ControlHandle)hndl));
  460.                                 GetDItem(dlog,group1,&type,&hndl,&box);
  461.                                 SetCtlValue((ControlHandle)hndl,FALSE);
  462.                                 group1 = itemHit;
  463.                                 }
  464.                             break;
  465.                     }
  466.                 break;
  467.             case ctrlItem+resCtrl:
  468.                 break;
  469.             case statText:
  470.                 switch(itemHit) {
  471.                     case STXT7:        /* NOT Enabled */
  472.                         break;
  473.                     case STXT13_Name:        /* NOT Enabled */
  474.                         break;
  475.                     }
  476.                 break;
  477.             case editText:
  478.                 switch(itemHit) {
  479.                     case EDIT12:
  480.                         break;
  481.                     }
  482.                 break;
  483.             case iconItem:
  484.                 break;
  485.             case picItem:
  486.                 break;
  487.             case userItem:
  488.                 switch(itemHit) {
  489.                     case USER9:        /* NOT Enabled */
  490.                         break;
  491.                     case LIST10:
  492.                         if (LClick(where,modifiers,list10.hndl)) {
  493.                             val = 256;
  494.                             DoOpenCell(&list10,str,&val);
  495.                             }
  496.                         break;
  497.                     case LIST11:
  498.                         if (LClick(where,modifiers,list11.hndl)) {
  499.                             val = 256;
  500.                             DoOpenCell(&list11,str,&val);
  501.                             }
  502.                         break;
  503.                     case POP14:
  504.                         /* popup14.currentChoice has changed */
  505.                         break;
  506.                     }
  507.                 break;
  508.             }
  509.  
  510.         if (okay) keepGoing = AnyBadValues(dlog);
  511.         return(keepGoing);
  512.     }
  513.  
  514. /*
  515.  * Pull values out of dialog items and deliver TRUE if any of them are
  516.  * illegal or inconsistent; otherwise deliver FALSE.  If any values are bad,
  517.  * you should inform your user about the problem here before delivering TRUE.
  518.  * If any items are missing values, this is the place to assign any defaults.
  519.  */
  520.  
  521. static int AnyBadValues(DialogPtr dlog)
  522.     {
  523.         unsigned char str[256]; short val,len; Cell cell;
  524.  
  525.         /* ?? = group1; */    /* Contains item number of set radio button in this group */
  526.  
  527.         if (GetDlgString(dlog,EDIT12,str)) {
  528.             /* Got a string (can also call GetDlgWord(), etc. here) */
  529.             }
  530.         else {
  531.             /* Field was empty */
  532.             }
  533.  
  534.         /* ?? = popup14.currentChoice */
  535.         return(FALSE);
  536.     }
  537.  
  538. /*••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••*/
  539.  
  540. /*
  541.  *  The following are various utility routines for general dialog management.
  542.  *  Typically, you'll want to keep them in a library that is available to all
  543.  *  your dialog modules; however, they are included here (and declared static)
  544.  *  as a private library so that you can quickly compile this file for testing.
  545.  */
  546.  
  547. #define _PrivateLibraries_
  548. #ifdef  _PrivateLibraries_
  549.  
  550. /*
  551.  *    Center a given window, w, horizontally in the main screen, top pixels from
  552.  *    the top, or centered vertically if top is 0.  The window should be invisible.
  553.  */
  554.  
  555. static void CenterWindow(WindowPtr w, int top)
  556.     {
  557.         Rect scr; Point p;
  558.         int rsize,size,margin,xoff,yoff;
  559.  
  560.         scr = qd.screenBits.bounds;
  561.         SetPort(w);
  562.         p.h = w->portRect.left; p.v = w->portRect.top;
  563.         LocalToGlobal(&p);
  564.  
  565.         size = scr.right - scr.left;
  566.         rsize = w->portRect.right - w->portRect.left;
  567.         margin = size - rsize;
  568.         if (margin > 0) {
  569.             margin >>= 1;
  570.             p.h = scr.left + margin;
  571.             }
  572.         size = scr.bottom - scr.top;
  573.         rsize = w->portRect.bottom - w->portRect.top;
  574.         margin = size - rsize;
  575.         if (margin > 0) {
  576.             margin >>= 1;
  577.             p.v = scr.top + margin;
  578.             }
  579.         MoveWindow(w,p.h,top?top:p.v,FALSE);
  580.     }
  581.  
  582. /* Local C string length routine */
  583.  
  584. static long strlen(register char *str)
  585.     {
  586.         register char *p;
  587.  
  588.         p = str;
  589.         while (*p++) ;
  590.         return((long)(--p - str));
  591.     }
  592.  
  593. /* Convert in place a Pascal string to C string, and deliver its address */
  594.  
  595. static char *PascalToC(unsigned char *str)
  596.     {
  597.         register unsigned char *p,*q,*end;
  598.  
  599.         end = str + *str;
  600.         q = (p=str) + 1;
  601.         while (p < end) *p++ = *q++;
  602.         *p = '\0';
  603.         return((char *)str);
  604.     }
  605.  
  606. /*
  607.  *    Convert in place a C string to Pascal string, and deliver its address.
  608.  *    The C string should not be greater than 255 chars in length, or the
  609.  *    resulting Pascal string will be truncated to 255 chars.
  610.  */
  611.  
  612. static unsigned char *CToPascal(char *str)
  613.     {
  614.         register char *p,*q;
  615.         register long len;
  616.  
  617.         len = strlen(str);
  618.         if (len > 255) len = 255;
  619.         p = str + len;
  620.         q = p-1;
  621.         while (p != str) *p-- = *q--;
  622.         *str = len;
  623.         return((unsigned char *)str);
  624.     }
  625.  
  626. /* Dialog Item Stuffers */
  627.  
  628. /*
  629.  *    Install a given Pascal string, str, into the given static or edit text item
  630.  *    in the dialog, dlog.  If the item is an edit text item, leave the installed
  631.  *    text selected or not according to the value of sel (TRUE or FALSE).
  632.  */
  633.  
  634. static void PutDlgString(DialogPtr dlog, int item, unsigned char *str, int sel)
  635.     {
  636.         short type; Handle hndl; Rect box;
  637.  
  638.         GetDItem(dlog,item,&type,&hndl,&box);
  639.         SetIText(hndl,str);
  640.         if (type == editText)
  641.             SelIText(dlog,item,sel?0:32767,32767);
  642.         InvalRect(&box);
  643.     }
  644.  
  645. /*
  646.  *    Install a given decimal long value into the static or edit text item of the
  647.  *    given dialog, dlog.  If the item is an edit text item, leave the installed
  648.  *    text for the number selected or not according to sel (TRUE or FALSE).
  649.  */
  650.  
  651. static void PutDlgLong(DialogPtr dlog, int item, long val, int sel)
  652.     {
  653.         unsigned char str[32];
  654.  
  655.         NumToString(val,str);
  656.         PutDlgString(dlog,item,str,sel);
  657.     }
  658.  
  659. /*
  660.  *    Same as above, only for an int (word) decimal number.
  661.  */
  662.  
  663. static void PutDlgWord(DialogPtr dlog, int item, int val, int sel)
  664.     {
  665.         PutDlgLong(dlog,item,(long)val,sel);
  666.     }
  667.  
  668. /*
  669.  *    Set the given check box or radio button item of the given dialog, dlog, to
  670.  *    on or off, according to val.
  671.  */
  672.  
  673. static void PutDlgChkRadio(DialogPtr dlog, int item, int val)
  674.     {
  675.         short type; Handle hndl; Rect box;
  676.  
  677.         GetDItem(dlog,item,&type,&hndl,&box);
  678.         SetCtlValue((ControlHandle)hndl,val!=0);
  679.     }
  680.  
  681. /*
  682.  *    Deliver the value of the checkbox or radio button item of the given dialog.
  683.  */
  684.  
  685. static int GetDlgChkRadio(DialogPtr dlog, int item)
  686.     {
  687.         short type; Handle hndl; Rect box;
  688.         
  689.         GetDItem(dlog,item,&type,&hndl,&box);
  690.         return(GetCtlValue((ControlHandle)hndl) != 0);
  691.     }
  692.  
  693. /* Dialog Item Unstuffers */
  694.  
  695. /*
  696.  *    Retrieve the value of an edit text item in a given dialog, placing the
  697.  *    resulting Pascal string in the buffer, str, which is assumed large enough
  698.  *    to hold the text (256 bytes max).  If item is the number of a static text
  699.  *    item, the empty string is delivered.  Delivers TRUE or FALSE according to
  700.  *    whether or not the text so delivered was empty.  
  701.  */
  702.  
  703. static int GetDlgString(DialogPtr dlog, int item, unsigned char *str)
  704.     {
  705.         short type; Handle hndl; Rect box;
  706.  
  707.         GetDItem(dlog,item,&type,&hndl,&box);
  708.         if (type == editText) GetIText(hndl,str);
  709.          else                 *str = 0;
  710.         return(*str != 0);
  711.     }
  712.  
  713. /*
  714.  *    Retrieve the value of an edit text item in a given dialog, converting the
  715.  *    Pascal string to a long and setting *val to it.  Delivers TRUE or FALSE
  716.  *    according to whether or not the text so delivered was empty.  If FALSE,
  717.  *    *val is set to 0; if TRUE, *val is set to whatever StringToNum() says the
  718.  *    value is, even if the text contains non-numerical characters.
  719.  */
  720.  
  721. static int GetDlgLong(DialogPtr dlog, int item, long *val)
  722.     {
  723.         int ans; unsigned char str[256];
  724.  
  725.         *val = 0;
  726.         ans = GetDlgString(dlog,item,str);
  727.         if (ans)
  728.             StringToNum(str,val);
  729.         return(ans);
  730.         }
  731.  
  732. /* Same as above, only delivers the value of a word */
  733.  
  734. static int GetDlgWord(DialogPtr dlog, int item, short *val)
  735.     {
  736.         int ans; long num;
  737.  
  738.         *val = 0;
  739.         ans = GetDlgLong(dlog,item,&num);
  740.         if (ans)
  741.             *val = num;
  742.         return(ans);
  743.     }
  744.  
  745. /*
  746.  *    Deliver the number of the current editText item in given dialog if any text
  747.  *    is selected in it, or 0 if none selected.
  748.  */
  749.  
  750. int TextSelected(DialogPtr dlog)
  751.     {
  752.         register TEHandle textH; int item = 0;
  753.         
  754.         textH = ((DialogPeek)dlog)->textH;
  755.         if (*textH)
  756.             if ( (*textH)->selStart != (*textH)->selEnd )
  757.                 item = ((DialogPeek)dlog)->editField+1;
  758.         return(item);
  759.     }
  760.  
  761. /*
  762.  *  If any of the variable argument scrap types are available for pasting from
  763.  *  the scrap, deliver the first one.  Otherwise, deliver 0.  For example,
  764.  *    
  765.  *      if (whichType = CanPaste(3,'TEXT','PICT','STUF')) ...
  766.  *
  767.  *  There can be any number of types in the list, as long as the preceding count, n,
  768.  *  is correct.
  769.  */
  770.  
  771. static OSType CanPaste(int n, ...)
  772.     {
  773.         register OSType nextType,ans = 0L;
  774.         long err,offset;
  775.         va_list nextArg;
  776.         
  777.         va_start(nextArg,n);
  778.         nextType = va_arg(nextArg, OSType);
  779.         
  780.         while (n-- > 0) {
  781.             err = GetScrap(NIL, nextType, &offset);
  782.             if (err >= -1) {
  783.                 ans = nextType;
  784.                 break;
  785.                 }
  786.             nextType = va_arg(nextArg, OSType);
  787.             }
  788.         
  789.         va_end(nextArg);
  790.         return(ans);
  791.     }
  792.  
  793. /*
  794.  *    Frame or unframe a default dialog item (presumed a button) in given dialog.
  795.  *    Note that you don't need to use an extra user item to do this unless you
  796.  *    are doing some sort of non-standard default highlighting (not recommended).
  797.  */
  798.  
  799. static void FrameDefault(DialogPtr dlog, int item, int frame)
  800.     {
  801.         short type; Handle hndl; Rect box;
  802.         GrafPtr oldPort; PenState oldPen;
  803.         
  804.         GetPort(&oldPort); SetPort(dlog);
  805.         GetPenState(&oldPen);
  806.         
  807.         GetDItem(dlog,item,&type,&hndl,&box);
  808.         InsetRect(&box,-4,-4);
  809.         
  810.         PenSize(3,3);
  811.         if (frame) PenPat(&qd.black);        /* Paint frame */
  812.          else      PenPat(&qd.white);        /* Erase frame */
  813.         FrameRoundRect(&box,16,16);
  814.         
  815.         SetPenState(&oldPen);
  816.         SetPort(oldPort);
  817.     }
  818.  
  819. /*
  820.  *    Get rectangle, *panel, for a given item (usually a user or picture item)
  821.  *    and then hide the item so that it doesn't interfere with mouse clicking.
  822.  *    This lets you stop worrying about the item order any user or pict items that
  823.  *    obscure other items in the item list, which can affect how the DialogMgr
  824.  *    returns itemHits.
  825.  */
  826.  
  827. static void GetDlgPanel(DialogPtr dlog, int item, Rect *panel)
  828.     {
  829.         short type; Handle hndl;
  830.         
  831.         GetDItem(dlog,item,&type,&hndl,panel);
  832.         HideDItem(dlog,item);
  833.     }
  834.  
  835.     
  836. /*
  837.  *    Draw a given popup user item in the system font.
  838.  */
  839.  
  840. static void DrawPopUp(UserPopUp *p)
  841.     {
  842.         short font,face,mode,size,sysFontSize;
  843.         
  844.         FrameRect(&p->bounds);
  845.         /* And its drop shadow */
  846.         MoveTo(p->bounds.right,p->shadow.top+2);
  847.         LineTo(p->bounds.right,p->bounds.bottom);
  848.         LineTo(p->shadow.left+2,p->bounds.bottom);
  849.         /* And draw the current setting str in */
  850.         MoveTo(p->bounds.left+lMargin,p->bounds.bottom-bMargin);
  851.  
  852.         /* Save/restore current window font characteristics while drawing. */
  853.         
  854.         font = qd.thePort->txFont;
  855.         size = qd.thePort->txSize;
  856.         face = qd.thePort->txFace;
  857.         mode = qd.thePort->txMode;
  858.         
  859.         /* Get the system font size and set system font */
  860.         
  861.         sysFontSize = GetDefFontSize();
  862.         if (sysFontSize < 1) sysFontSize = 12;
  863.         TextFont(systemFont); TextSize(sysFontSize); TextFace(0); TextMode(0);
  864.         
  865.         /* Draw the current (truncated) string */
  866.         DrawString(p->str);
  867.         DrawPopUpTriangle(&p->bounds);
  868.  
  869.         /* Kiss and make up after last argument(s) */
  870.         TextFont(font); TextSize(size); TextFace(face); TextMode(mode);
  871.     }
  872.  
  873. void DrawPopUpTriangle(Rect *bounds)
  874.     {
  875.         Rect box;
  876.         
  877.         box = *bounds;
  878.         box.left = box.right - 16;
  879.         InsetRect(&box,1,1);
  880.         EraseRect(&box);
  881.         
  882.         /* Draw triangle in right side of popup bounds, centered vertically */
  883.         
  884.         MoveTo(bounds->right-rMargin-1,bounds->top+((bounds->bottom-bounds->top)-6)/2);
  885.         Line(10,0); Move(-1,1);
  886.         Line(-8,0); Move(1,1);
  887.         Line(6,0); Move(-1,1);
  888.         Line(-4,0); Move(1,1);
  889.         Line(2,0); Move(-1,1);
  890.         Line(0,0);
  891.     }
  892.  
  893. /*
  894.  *    TruncPopUp() fits the string for the current setting into
  895.  *    the popup item's bounding box by truncating it and adding …
  896.  */
  897.  
  898. static void TruncPopUp(UserPopUp *p)
  899.     {
  900.         int width,space; register int len,n; unsigned char *ptr;
  901.  
  902.         if (p->currentChoice) GetItem(p->menu,p->currentChoice,p->str);
  903.          else                 *p->str = 0;
  904.  
  905.         /* Trim off any trailing blanks used to increase width of menu as a whole */
  906.         
  907.         for (len = *(unsigned char *)p->str; len>0; len--) {
  908.             ptr = p->str + len;
  909.             if (*ptr!=' ' && *ptr!=' ') break;        /* Option-space as well */
  910.             }
  911.         *p->str = len;
  912.         
  913.         space = p->bounds.right - p->bounds.left - (lMargin+rMargin);
  914.         len = *(unsigned char *)p->str;
  915.         while (len > 0)
  916.             if (p->str[len]!=' ') break; else len--;
  917.         *p->str = len;
  918.         width = StringWidth(p->str);        /* Assumes the system font is current */
  919.         if (width > space) {
  920.             len = *p->str;
  921.             width += CharWidth('…');
  922.             while (len>0 && width>space)
  923.                 width -= CharWidth(p->str[len--]);
  924.             p->str[++len] = '…';
  925.             *p->str = len;
  926.         }
  927.     }
  928.  
  929. /*
  930.  *    Initialize a UserPopUp data structure, p, for a given dialog, dlog, and the
  931.  *    user item, item, in it that represents the popup menu.
  932.  *    pItem is the item number of the staticText prompt item, or 0 if none.
  933.  *    menuID is the resource ID of the menu to be popped up; choice is the
  934.  *    initial choice from the menu to show in the popup field, or 0 if none.
  935.  *    Returns FALSE if error, TRUE otherwise.  A successful call to this
  936.  *    should be matched by a call to DisposePopUp, in order to get rid of
  937.  *    the detached MENU resource.
  938.  */
  939.  
  940. static int InitPopUp(DialogPtr dlog, UserPopUp *p, int item, int pItem, int menuID, int firstChoice)
  941.     {
  942.         short type; Handle hndl;
  943.  
  944.         if (pItem) GetDItem(dlog,pItem,&type,&hndl,&p->prompt);
  945.          else       SetRect(&p->prompt,0,0,0,0);
  946.         
  947.         GetDItem(dlog,item,&type,&hndl,&p->box);
  948.         p->bounds = p->box; InsetRect(&p->bounds,-1,-1);
  949.         p->shadow = p->bounds;
  950.         p->shadow.right++; p->shadow.bottom++;
  951.         p->currentChoice = firstChoice;
  952.         p->menu = GetMenu(p->menuID = menuID);
  953.         if (p->menu) {
  954.             /* Make sure each popup has its own local copy of menu */
  955.             DetachResource((Handle)p->menu);
  956.             TruncPopUp(p);
  957.             if (firstChoice)
  958.                 SetItemMark(p->menu,firstChoice,(char)checkMark);
  959.             }
  960.  
  961.         return(p->menu != NIL);
  962.     }
  963.  
  964. /*
  965.  *    This is called to get rid of the MENU handle, which was detached
  966.  *    by InitPopUp.
  967.  */
  968.  
  969. static void DisposePopUp(UserPopUp *p)
  970.     {
  971.         if (p->menu) DisposHandle((Handle)p->menu);
  972.         p->menu = NIL;
  973.     }
  974.  
  975. /*
  976.  *    Invoke a popup menu; return TRUE if new choice made, which will be
  977.  *    in p->currentChoice.
  978.  */
  979.  
  980. static int DoUserPopUp(UserPopUp *p)
  981.     {
  982.         long choice; int ans = FALSE; Point pt;
  983.  
  984.         InvertRect(&p->prompt);        /* May be empty if no prompt static text item */
  985.         InsertMenu(p->menu,-1);
  986.         CalcMenuSize(p->menu);
  987.         pt = *(Point *)(&p->box);
  988.         LocalToGlobal(&pt);
  989.         choice = PopUpMenuSelect(p->menu,pt.v,pt.h,p->currentChoice);
  990.         InvertRect(&p->prompt);
  991.         DeleteMenu(p->menuID);
  992.         if (choice) {
  993.             choice = LoWord(choice);
  994.             if (choice != p->currentChoice) {
  995.                 SetPopUpChoice(p,(short)choice);
  996.                 ans = TRUE;
  997.                 }
  998.             }
  999.         return(ans);
  1000.     }
  1001.  
  1002. /*
  1003.  *    Set popup menu to show given choice, or nothing if choice is 0.
  1004.  */
  1005.  
  1006. static void SetPopUpChoice(UserPopUp *p, int choice)
  1007.     {
  1008.         if (p->currentChoice)
  1009.             SetItemMark(p->menu,p->currentChoice,0);
  1010.         *p->str = 0;
  1011.         if (choice>0 && choice<=CountMItems(p->menu)) {
  1012.             SetItemMark(p->menu,p->currentChoice = choice,checkMark);
  1013.             TruncPopUp(p);
  1014.             }
  1015.          else
  1016.             p->currentChoice = 0;
  1017.         EraseRect(&p->box); DrawPopUp(p);
  1018.     }
  1019.  
  1020.  
  1021. /*
  1022.  *    GetLengthList() should compute a given list's length and prepare
  1023.  *    its data to be inserted into screen cells.
  1024.  */
  1025.  
  1026. static int GetLengthList(UserList *l)
  1027.     {
  1028.         return(l->nCells = 8);    /* Or whatever (8 is size of stubstr[] below) */
  1029.     }
  1030.  
  1031. /*
  1032.  *    GetCellData(l,i,len) should deliver start of pure data of i'th cell's
  1033.  *    contents in the given list, and sets the data's length in *len.  It can
  1034.  *    be assumed to be called sequentially from i=0 to l->nCells-1.  The prototype code
  1035.  *    here should be replaced with your content-specific instructions.
  1036.  */
  1037.  
  1038. static unsigned char *GetCellData(UserList *l, short i, short *len)
  1039.     {
  1040.         static unsigned char *stubstr[8] = {
  1041.                 "\pCell 0 data",
  1042.                 "\pCell 1 data",
  1043.                 "\pCell 2 data",
  1044.                 "\pCell 3 data",
  1045.                 "\pCell 4 data",
  1046.                 "\pCell 5 data",
  1047.                 "\pCell 6 data",
  1048.                 "\pCell 7 data"
  1049.                 };
  1050.                 
  1051.         /* Reality check */
  1052.         
  1053.         if (i < 0) i = 0;
  1054.          else if (i >= l->nCells) i = l->nCells-1;
  1055.          
  1056.         *len = *(unsigned char *)stubstr[i];
  1057.         return(stubstr[i]+1);
  1058.     }
  1059.  
  1060. /*
  1061.  *    Build a new list in given user item box of dialog, dlog,
  1062.  *    with cell height, csize.  If success, delivers TRUE; if couldn't
  1063.  *    allocate ListMgr list (no more memory or whatever), delivers FALSE.
  1064.  */
  1065.  
  1066. static int BuildList(DialogPtr dlog, int item, int csize, UserList *l)
  1067.     {
  1068.         short i,len,type; Rect box; Handle hndl; unsigned char *data;
  1069.  
  1070.         /* Content area (plus scroll bar) of list corresponds to user item box */
  1071.         
  1072.         GetDItem(dlog,item,&type,&hndl,&box);
  1073.         l->bounds = box; InsetRect(&l->bounds,-1,-1);
  1074.         SetDItem(dlog,item,userItem,NIL,&l->bounds);
  1075.  
  1076.         l->scroll = box; l->scroll.left = l->scroll.right - 15;        /* Scrollbar width */
  1077.         l->content = box; l->content.right = l->scroll.left;
  1078.  
  1079.         SetRect(&l->dataBounds,0,0,1,0);
  1080.         l->cSize.v = csize > 0 ? csize : 1;
  1081.         l->cSize.h = l->content.right - l->content.left;
  1082.  
  1083.         l->hndl = LNew(&l->content,&l->dataBounds,l->cSize,0,dlog,FALSE,FALSE,FALSE,TRUE);
  1084.         if (l->hndl) {
  1085.             (*l->hndl)->selFlags = lOnlyOne;        /* Or whatever */
  1086.             GetLengthList(l);
  1087.             LAddRow(l->nCells,0,l->hndl);
  1088.             for (i=0; i<l->nCells; i++) {
  1089.                 l->cell.h = 0; l->cell.v = i;
  1090.                 data = GetCellData(l,i,&len);
  1091.                 LSetCell((Ptr)data,len,l->cell,l->hndl);
  1092.                 }
  1093.             l->cell.v = 0;
  1094.             LSetSelect(TRUE,l->cell,l->hndl);
  1095.             EraseRect(&l->content);
  1096.             InvalRect(&l->bounds);
  1097.             LDoDraw(TRUE,l->hndl);
  1098.             }
  1099.         return(l->hndl!=NIL);
  1100.     }
  1101.  
  1102. /*
  1103.  *    Do whatever when user double clicks (opens) on a list cell.  Delivers
  1104.  *    TRUE or FALSE according to whether any cell was selected or not.
  1105.  */
  1106.  
  1107. static int DoOpenCell(UserList *l, unsigned char *buf, short *len)
  1108.     {
  1109.         int ans;
  1110.  
  1111.         l->cell.h = l->cell.v = 0;
  1112.         ans = LGetSelect(TRUE,&l->cell,l->hndl);
  1113.         if (ans) {
  1114.             LGetCell((Ptr)buf,len,l->cell,l->hndl);
  1115.             /* Got data for first selected cell: do whatever with it */
  1116.             /* ... */
  1117.             }
  1118.  
  1119.         return(ans);
  1120.     }
  1121.  
  1122. #endif
  1123.  
  1124.