home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Code Resources / Jims CDEFs 1.50 / CDEF Source / source / cdefTab.c < prev    next >
Encoding:
Text File  |  1995-11-08  |  30.0 KB  |  948 lines  |  [TEXT/KAHL]

  1. //------------------------- © 1994-1995 by James G. Stout ---------------------------
  2. // File        : cdefTab.c
  3. // Date        : July 24, 1994
  4. // Author    : Jim Stout
  5. // Purpose    : A CDEF to provide a "Tabbed" panel for use in Tab Dialogs
  6. //            : use procID :   109 * 16 + varCode 
  7. //            :      when calling NewControl() or in your resource template.
  8. //            :
  9. //            : This control can have a maximum of 5 rows of tabs.  By default,  
  10. //            : there are 4 tabs per row, giving a total of 20 tabs.  The 
  11. //            : control Max field should contain the number of tabs to draw.
  12. //            :
  13. //            : This default can be changed in 2 ways:
  14. //            :
  15. //            : 1. Display a single row of tabs:
  16. //            :
  17. //            :    Variation code 2 will force a single row of tabs, with the
  18. //            :    number of tabs specified in the control Max field.
  19. //            :
  20. //            : 2. Increase or decrease the number of tabs per row:
  21. //            :
  22. //            :    The LoWord of the control refCon can be used to specify from
  23. //            :    2 to 8 tabs per row.  If zero, there will be 4 tabs per row.
  24. //            :
  25. //            : This CDEF supports varCodes of:
  26. //            :
  27. //            :        0 - tab label is Geneva 9, bold for active tab
  28. //            :        1 - tab label is System, underline for active tab
  29. //            :        2 - 1 row of tabs, contrlMax tabs.
  30. //            :        4 - * not used *
  31. //            :        8 - useWindFont for tab label, bold for active tab
  32. //            :
  33. //             : Pretty straight forward code here, only trick here is to use the 
  34. //             : contrlMin value as the height of the control.  This is done to keep
  35. //             : the Control Manager from thinking the control overlays the controls
  36. //             : you want inside the Panel.  Set the real control rect to some value
  37. //             : sufficient to see the title in your resource editor and set contrlMin
  38. //             : to the height of the panel you want drawn.
  39. //            :
  40. // ** IMPORTANT INFORMATION **
  41. //            :
  42. //            : Since the Control Manager doesn't know about the extra drawing done
  43. //            : by this control, some extra handling is required for update events.
  44. //            : Just calling UpdtControl() will not suffice since there could be a
  45. //            : portion of this control that needs to be drawn that is outside the
  46. //            : "official" control rect.
  47. //            :
  48. //            : So, make sure you call Draw1Control() in response to an update for
  49. //            : any control that uses this CDEF.  Calling DrawControls is ok too, but
  50. //            : will draw all of the controls in the window.
  51. //            :
  52. //            : If you find a use for this, I'd love to know about it.  Bug reports
  53. //            : are always interesting.
  54. //            :
  55. //            : Internet    : JimS@WRQ.COM(work hours, PST)
  56. //            : AppleLink   : WRQ            (daily)
  57. //            : CompuServe  : 73240,2052    (weekly or so)
  58. //            : AOL         : JasG        (weekly or so)
  59. //            : eWorld      : Jim Stout    (weekly or so)
  60. //----------------------------------------------------------------------------------
  61.  
  62. //#define _DEBUGCDEF 1
  63.  
  64. #include "fatCDEF.h"
  65.  
  66. #include <Controls.h>
  67. #include <LowMem.h>
  68. #include <Memory.h>
  69. #include <ToolUtils.h>
  70. #include <Types.h>
  71.  
  72. #include "grayCDEF.h"
  73. #include "colorCDEF.h"
  74. #include "miscCDEF.h"
  75.  
  76. #define MAXROWS 5            // maximum number of rows of tabs
  77. #define MINCOLS 2            // minimum number of tabs per row
  78. #define MAXCOLS 8            // maximum number of tabs per row
  79. #define DEFCOLS 4            // default number of tabs per row
  80. //----------------------------------------------------------------------------------
  81. // Variation codes
  82. //----------------------------------------------------------------------------------
  83.  
  84. #define sysFontTabs        0x0001
  85. #define oneTabRow        0x0002
  86. #define useWFont        0x0008            // draw control with window font
  87.  
  88. //----------------------------------------------------------------------------------
  89. //    CDEF private data
  90. //----------------------------------------------------------------------------------
  91. typedef struct {
  92. short    txFont;                // font to use for tab labels
  93. short    txSize;                // size to use for tab labels
  94. short    cdefHt;                // true height of tab panel
  95. short    rowWidth;            // width of all tabs on a row
  96. short    rowHt;                // height of one tab
  97. short    prevTab;            // previously active tab
  98. short    nRows;                // number of rows of tabs
  99. short    nCols;                // number of tabs in a row
  100. short    rowOrder[MAXROWS];
  101. short    rowChg;                // click on back row
  102. }cdefData,**cdefHandle;
  103.  
  104. //----------------------------------------------------------------------------------
  105. //    Function prototypes
  106. //----------------------------------------------------------------------------------
  107. static void        doInit            (ControlHandle cHdl, short varCode, short txF, short txS);
  108. static long        doTest            (ControlHandle theCtl, long param);
  109. static void        doDraw            (ControlHandle cHdl, short varCode);
  110. pascal void     drawControl     (short depth, short dFlags, GDHandle theDevice, 
  111.                                     long userData);
  112. static void        drawTitle        (ControlHandle cHdl, short varCode, Rect *r, 
  113.                                     Boolean bgInColor, Boolean rowChg, 
  114.                                     short part, short prevTab, short lnHt, short lnAdj);
  115. static void        getNextTitle    (Str255 t, Str255 s, short num);
  116. static short    getTabWidth        (ControlHandle cHdl, short row);
  117. static short    getMaxCols        (ControlHandle cHdl, short row);
  118.  
  119. #ifdef _DEBUGCDEF
  120. pascal long CDmain (short varCode, ControlHandle theCtl, short message, long param);
  121. pascal long CDmain (short varCode, ControlHandle theCtl, short message, long param)
  122. #else
  123.  
  124. //==================================================================================
  125. //    CDEF main
  126. //==================================================================================
  127.  
  128. pascal long main (short varCode, ControlHandle theCtl, short message, long param)
  129. #endif
  130. {
  131.     short        txF,txS;
  132.     long        ret = 0L;
  133.     Point        p;
  134.     GrafPtr        thisPort;
  135.     SignedByte    cState, dState;
  136.  
  137. #include "fatEntry.c"
  138.         
  139.     cState = HGetState((Handle)theCtl);
  140.     HLock((Handle)theCtl);
  141.     if((**theCtl).contrlData) {
  142.         dState = HGetState((**theCtl).contrlData);
  143.         HLock((**theCtl).contrlData);
  144.     }
  145.                 
  146.     GetPort(&thisPort);
  147.     
  148. //----------------------------------------------------------------------------------
  149. // set window font & size info    
  150. //----------------------------------------------------------------------------------
  151.     
  152.     txF = thisPort->txFont;
  153.     txS = thisPort->txSize;
  154.     if(varCode & sysFontTabs) {                    // use system font    
  155.         TextFont(LMGetSysFontFam());            // set system as current
  156.         TextSize(LMGetSysFontSize());
  157.     }
  158.     else
  159.     if(!(varCode & useWFont)) {                    // default to geneva 9
  160.         TextFont(geneva);
  161.         TextSize(9);
  162.     }
  163.     
  164. //----------------------------------------------------------------------------------
  165. // Process messages to control    
  166. //----------------------------------------------------------------------------------
  167.  
  168.     switch(message) {
  169.         case initCntl:
  170.             doInit(theCtl, varCode, txF, txS);
  171.             if((**theCtl).contrlData) {
  172.                 dState = HGetState((**theCtl).contrlData);
  173.                 HLock((**theCtl).contrlData);
  174.             }
  175.         break;
  176.         case dispCntl:
  177.             if((**theCtl).contrlData) {
  178.                 HUnlock((**theCtl).contrlData);
  179.                 DisposeHandle((**theCtl).contrlData);
  180.                 (**theCtl).contrlData = 0;
  181.             }
  182.         break;
  183.         case drawCntl:
  184.             if ((**theCtl).contrlVis != 0 &&
  185.                 ((WindowPeek)(**theCtl).contrlOwner)->visible) {
  186.                 doDraw(theCtl, varCode);
  187.             }
  188.         break;
  189.         case testCntl:
  190.             p.v=HiWord(param);
  191.             p.h=LoWord(param);
  192.             if(PtInRect(p,&(*theCtl)->contrlRect)) {
  193.                 ret = doTest(theCtl, param);
  194.             }
  195.         break;
  196.         case calcCRgns:
  197.             RectRgn((RgnHandle)(param & 0x7fffffffL), &(*theCtl)->contrlRect);
  198.         break;
  199.         case calcCntlRgn:
  200.         case calcThumbRgn:
  201.             RectRgn((RgnHandle)(param), &(*theCtl)->contrlRect);
  202.         break;
  203.     }
  204.  
  205. //----------------------------------------------------------------------------------
  206. // restore window font & size info    
  207. //----------------------------------------------------------------------------------
  208.  
  209.     if(!(varCode & useWindFont)) {
  210.         TextFont(txF);
  211.         TextSize(txS);
  212.     }
  213.     if((**theCtl).contrlData)
  214.         HSetState((**theCtl).contrlData, dState);
  215.     HSetState((Handle)theCtl, cState);
  216.  
  217. #include "fatExit.c"
  218.  
  219.     return (ret);
  220. }
  221. //==================================================================================
  222. // Initialize our private control data
  223. //==================================================================================
  224. static void doInit(ControlHandle theCtl, short varCode, short txF, short txS)
  225. {
  226.     cdefHandle    hCdef;
  227.     Rect        r;
  228.     short        wid,ht,notch,rows,maxTabs,maxCols;
  229.     
  230.     hCdef = (cdefHandle)NewHandle(sizeof(cdefData));
  231.     if(hCdef) {
  232. //----------------------------------------------------------------------------------
  233. // initialize row order array - this contains the 'order' of the rows which can
  234. // change in multi-row tab panels.
  235. //----------------------------------------------------------------------------------
  236.             
  237.         for(rows=1;rows<MAXROWS;rows++)
  238.             (**hCdef).rowOrder[rows-1] = rows;
  239.  
  240. //----------------------------------------------------------------------------------
  241. // bounds check on number of columns, rows, control maximum & control value
  242. //----------------------------------------------------------------------------------
  243.         if(varCode & oneTabRow) {
  244.             maxCols = (**theCtl).contrlMax;
  245.         }
  246.         else {
  247.             maxCols = LoWord((**theCtl).contrlRfCon);        // number of columns
  248.             if(maxCols < MINCOLS || maxCols > MAXCOLS)        // default to 4
  249.                 maxCols = DEFCOLS;
  250.         }
  251.             
  252.         maxTabs = MAXROWS*maxCols;
  253.     
  254.         if((**theCtl).contrlMax <= 0)                        // should be the total
  255.             (**theCtl).contrlMax = maxCols;                    // number of tabs
  256.         else
  257.         if((**theCtl).contrlMax > maxTabs)
  258.             (**theCtl).contrlMax = maxTabs;
  259.             
  260.         if((**theCtl).contrlValue > maxTabs)                // the 'active' tab
  261.             (**theCtl).contrlValue = 1;
  262.             
  263.         rows = (**theCtl).contrlMax / maxCols;
  264.         if((**theCtl).contrlMax % maxCols)                    // odd number of tabs,
  265.             rows++;                                            // so add a row
  266.  
  267. //----------------------------------------------------------------------------------
  268. // various size limits
  269. //----------------------------------------------------------------------------------
  270.         
  271.         r = (**theCtl).contrlRect;
  272.         wid = r.right-r.left;
  273.         ht = r.bottom-r.top;
  274. //----------------------------------------------------------------------------------        
  275. // the "notch" in the right corner must be no more than half the control width
  276. //----------------------------------------------------------------------------------
  277.  
  278.         notch = HiWord((**theCtl).contrlRfCon);
  279.         if(notch < 0 || notch > wid/2)
  280.             notch = 0;
  281. //----------------------------------------------------------------------------------        
  282. // width of a row of tabs must be adjusted for 'notch' and a 10 pixel inset
  283. // from the right edge of control for each row.
  284. //----------------------------------------------------------------------------------
  285.     
  286.         (**hCdef).rowWidth = wid - notch - ((rows-1)*10);
  287.  
  288. //----------------------------------------------------------------------------------
  289. // The height of the control is really in the contrlMin field, not contrlRect
  290. //----------------------------------------------------------------------------------
  291.         
  292.         (**hCdef).cdefHt = (**theCtl).contrlMin;
  293.         if((**hCdef).cdefHt < ht)
  294.             (**hCdef).cdefHt = 2*ht;
  295. //----------------------------------------------------------------------------------
  296. // calcuate the height of one row of tabs
  297. //----------------------------------------------------------------------------------
  298.         (**hCdef).rowHt = ht / rows;
  299.         
  300. //----------------------------------------------------------------------------------
  301. // set rows & columns
  302. //----------------------------------------------------------------------------------
  303.  
  304.         if(varCode & oneTabRow) {                    // only want 1 row of tabs
  305.             (**hCdef).nRows = 1;
  306.             (**hCdef).nCols = (**theCtl).contrlMax;
  307.         }
  308.         else {
  309.             (**hCdef).nRows = rows;
  310.             (**hCdef).nCols = maxCols;
  311.         }
  312.         
  313.         (**theCtl).contrlMin = 1;
  314.         (**hCdef).prevTab = 0;
  315.         (**hCdef).txFont = txF;
  316.         (**hCdef).txSize = txS;
  317.         
  318.         (**theCtl).contrlData = (Handle)hCdef;
  319.     }
  320. }
  321. //==================================================================================
  322. // Check for a mouse down in one of our tabs - tab information is stored in the
  323. // rowOrder.
  324. //==================================================================================
  325. static long doTest(ControlHandle theCtl, long param)
  326. {
  327.     short        item,row1,row,col,maxRow,maxCol,tabCnt,tabHt,tabWid;
  328.     Rect        r,r2;
  329.     Point        p;
  330.     cdefHandle    hCdef;
  331.     
  332.     hCdef = (cdefHandle)(**theCtl).contrlData;
  333.     if(!hCdef)
  334.         return 0L;
  335.     
  336.     p.v=HiWord(param);
  337.     p.h=LoWord(param);
  338.         
  339.     tabHt = (**hCdef).rowHt;
  340.     maxRow = (**hCdef).nRows;
  341.     tabCnt = (**theCtl).contrlMax;
  342.     r2 = r = (**theCtl).contrlRect;
  343.     
  344.     for(row=1;row<=maxRow;row++) {
  345.         row1 = (**hCdef).rowOrder[row-1];
  346.         tabWid = getTabWidth(theCtl, row1);
  347.         maxCol = getMaxCols(theCtl, row1);
  348.         r2.right = r2.left+tabWid;
  349.         r2.top = r2.bottom-tabHt;
  350.         r = r2;
  351.         for(col=1;col<=maxCol;col++) {
  352.             item = (row1-1)*(**hCdef).nCols + col;
  353.             if(item > tabCnt)
  354.                 break;
  355.             if(PtInRect(p, &r)) {
  356.                 while(StillDown()) {}
  357.                 GetMouse(&p);
  358.                 if(PtInRect(p,&r))
  359.                     return((long)item);
  360.                 else
  361.                     return (0L);
  362.             }
  363.             OffsetRect(&r,tabWid-1, 0);
  364.         }
  365.         OffsetRect(&r2, 10, -tabHt);
  366.     }
  367.     return (0L);
  368. }
  369. //==================================================================================
  370. // If running with System 7, use DeviceLoop to gracefully handle multiple screens.
  371. // Simulate DeviceLoop if using System 6...
  372. //==================================================================================
  373.  
  374. static void doDraw(ControlHandle theCtl, short varCode)
  375. {
  376.     devLoopHandle            hDl;
  377.     cdefHandle                hCdef;
  378.     short                    newRow,inx,row,maxRow;
  379.     Rect                    fullRect;
  380.     RgnHandle                saveClip, hRgn;
  381.     DeviceLoopDrawingUPP    drawControlUPP;
  382.     
  383.     hCdef = (cdefHandle)(**theCtl).contrlData;
  384.     if(!hCdef)
  385.         return;
  386.     
  387. //----------------------------------------------------------------------------------
  388. // if called to Hilite the control, just set the control value and leave
  389. // because we don't hilite.
  390. //----------------------------------------------------------------------------------
  391.     
  392.     if((**theCtl).contrlHilite == 0xFF)
  393.         return;
  394.         
  395.     if((**theCtl).contrlHilite) {
  396.         (**theCtl).contrlValue = (**theCtl).contrlHilite;
  397.         return;
  398.     }
  399.     
  400. //----------------------------------------------------------------------------------
  401. //    Setup our userData to pass via DeviceLoop
  402. //----------------------------------------------------------------------------------
  403.  
  404.     hDl = (devLoopHandle)NewHandle(sizeof(devLoopData));
  405.     if(hDl) {
  406.         drawControlUPP = NewDeviceLoopDrawingProc(drawControl);
  407.         
  408.         (**hDl).theCtl = theCtl;
  409.         (**hDl).varCode = varCode;
  410.         
  411.         fullRect = (**theCtl).contrlRect;
  412.         fullRect.bottom = fullRect.top + (**hCdef).cdefHt;
  413.         
  414.         (**hDl).controlRect = fullRect;
  415.         
  416. //----------------------------------------------------------------------------------
  417. //    Set clip region for the full panel, not just what the control mgr thinks it is
  418. //----------------------------------------------------------------------------------
  419.  
  420.         saveClip = NewRgn();
  421.         GetClip(saveClip);
  422.         
  423.         hRgn = NewRgn();
  424.         RectRgn(hRgn, &fullRect);
  425.         SectRgn(saveClip, hRgn, hRgn);
  426.         
  427. //----------------------------------------------------------------------------------
  428. // adjust the rowOrder if needed (user clicked on row other than lowest)
  429. //----------------------------------------------------------------------------------
  430.     
  431.     newRow = ((**theCtl).contrlValue-1) / (**hCdef).nCols + 1;
  432.             
  433.     if(newRow != (**hCdef).rowOrder[0]) {
  434.         (**hCdef).rowChg = true;
  435.         (**hCdef).rowOrder[0] = newRow;
  436.     
  437.         maxRow = (**hCdef).nRows;
  438.         row = 0;
  439.         for(inx=1;inx<=maxRow-1;inx++) {
  440.             row++;
  441.             if(row == newRow)
  442.                 row++;
  443.             (**hCdef).rowOrder[inx] = row;
  444.         }
  445.     }
  446.     else
  447.         (**hCdef).rowChg = false;
  448.         
  449. //----------------------------------------------------------------------------------
  450. //    Let DeviceLoop call our drawing routine for us
  451. //----------------------------------------------------------------------------------
  452.         
  453.         if(getOSVers() >= 0x0700) {
  454.             DeviceLoop (hRgn, drawControlUPP, (long)hDl, 0);
  455.         }
  456.         else {
  457.             sys6DeviceLoop (hRgn, drawControlUPP, (long)hDl, 0);
  458.         }
  459.         
  460.         SetClip(saveClip);
  461.         DisposeRgn(hRgn);
  462.         DisposeHandle((Handle)hDl);
  463.         DisposeRoutineDescriptor(drawControlUPP);
  464.         (**hCdef).prevTab = (**theCtl).contrlValue;
  465.     }
  466. }
  467. //==================================================================================
  468. // draw the tab panel.  Lots and lots of tedious line drawing - all done onscreen,
  469. // no real need for offscreen drawing.
  470. //==================================================================================
  471. pascal void drawControl (short depth, short dFlags, GDHandle theDevice, long userData)
  472. {
  473.  
  474. #pragma unused(dFlags, theDevice)
  475.  
  476.     ControlHandle    theCtl;
  477.     GrafPtr            thisPort;
  478.     short            varCode,tabWidth,tabHt,notch,rightEdge,bottomEdge;
  479.     short            inx,row,maxRow,col,maxCol,pLeft;
  480.     short            tabCnt,activeTab,currTab,prevTab;
  481.     short            lnHt,lnAdj;
  482.     Rect            panel,tab,fullRect,edgeRect,allTabs;
  483.     FontInfo        f;
  484.     PenState        savePen;
  485.     RGBColor        rgbGray,rgbGrayLt,saveFore,saveBack;
  486.     Boolean            inColor = false, bgInColor = false;
  487.     Boolean            partialRow = false;
  488.     
  489.     cdefHandle        hCdef;                                // our private data structures
  490.     devLoopHandle    hDl;
  491.     
  492. //----------------------------------------------------------------------------------
  493. // Can we draw?
  494. //----------------------------------------------------------------------------------
  495.     
  496.     hDl = (devLoopHandle)userData;                        // need control & varCode
  497.     if(hDl) {
  498.         theCtl = (**hDl).theCtl;
  499.         varCode = (**hDl).varCode;
  500.     }
  501.     else
  502.         return;
  503.  
  504.     hCdef = (cdefHandle)(**theCtl).contrlData;
  505.     if(!hCdef)
  506.         return;
  507.  
  508. //----------------------------------------------------------------------------------
  509. // initialize for drawing
  510. //----------------------------------------------------------------------------------
  511.     GetPort(&thisPort);
  512.     if(depth > 1 && !(((CGrafPtr)thisPort)->portVersion & 0x8000))
  513.         depth = 1;
  514.     
  515.     prevTab = (**hCdef).prevTab;
  516.     activeTab = (**theCtl).contrlValue;
  517.     tabCnt = (**theCtl).contrlMax;
  518.     tabHt = (**hCdef).rowHt;
  519.     maxRow = (**hCdef).nRows;
  520.     
  521.     if(maxRow != (tabCnt / (**hCdef).nCols))            // last row partial?
  522.         partialRow = true;
  523.  
  524. //----------------------------------------------------------------------------------
  525. // get the font info for drawing title
  526. //----------------------------------------------------------------------------------
  527.     
  528.     GetFontInfo(&f);
  529.     lnAdj = f.descent + f.leading;                
  530.     lnHt = f.ascent + lnAdj;
  531.     GetPenState(&savePen);
  532.     PenSize(1,1);
  533.     PenPat( (ConstPatternParam) "\xff\xff\xff\xff\xff\xff\xff\xff");
  534.  
  535. //----------------------------------------------------------------------------------
  536. // get the true Rect for the control
  537. //----------------------------------------------------------------------------------
  538.  
  539.     panel = tab = (**theCtl).contrlRect;
  540.     panel.bottom = panel.top + (**hCdef).cdefHt;
  541.     fullRect = panel;
  542.     panel.right = panel.right-((maxRow-1) * 10);
  543.     
  544. //----------------------------------------------------------------------------------
  545. // get color information and set flags for color/3D drawing
  546. //----------------------------------------------------------------------------------
  547.     
  548.     if(depth > 2) {
  549.         saveColors(&saveFore, &saveBack);
  550.         setPartColor(theCtl, cFrameColor, true);
  551.         
  552.         if(getGray(&rgbGray)) {                            // get the 2 grays we will
  553.             RGBForeColor(&rgbGray);                        // use for shading the right
  554.             getGray(&rgbGrayLt);                        // and bottom edges.
  555.         }
  556.         else {
  557.             rgbGray.red = rgbGray.green = rgbGray.blue = 0x8888;
  558.             rgbGrayLt.red = rgbGrayLt.green = rgbGrayLt.blue = 0xCCCC;
  559.         }
  560.         
  561.         inColor = true;
  562.         bgInColor = true;
  563.         if(saveBack.red == 65535 &&                        // is bg white?
  564.             saveBack.green == 65535 &&
  565.             saveBack.blue == 65535)
  566.             bgInColor = false;
  567.     }            
  568.     
  569. //----------------------------------------------------------------------------------
  570. // Draw each tab by looping thru rows (by rowOrder[]) & columns
  571. //----------------------------------------------------------------------------------
  572.  
  573.     for(inx=1;inx<=maxRow;inx++) {
  574.         row = (**hCdef).rowOrder[inx-1];
  575.         
  576.         tabWidth = getTabWidth(theCtl, row);            // may be different
  577.         tab.right = tab.left+tabWidth;                    // for some rows
  578.         tab.top = tab.bottom - tabHt;
  579.     
  580.         bottomEdge = tab.bottom;
  581.         rightEdge = panel.left + (**hCdef).rowWidth;
  582.         notch = panel.right - rightEdge;
  583.         
  584.         if((**hCdef).rowChg && partialRow) {            // clear old tabs
  585.             allTabs = tab;
  586.             allTabs.right = rightEdge-4;
  587.             allTabs.left+=4;
  588.             EraseRect(&allTabs);
  589.         }
  590.         
  591.         maxCol = getMaxCols(theCtl, row);
  592.         
  593.         for(col=1;col<=maxCol;col++) {
  594.             currTab = (**hCdef).nCols * (row-1) + col;
  595.             if(currTab > tabCnt)
  596.                 break;
  597.             if(col == maxCol)                            // last tab must 
  598.                 tab.right = rightEdge;                    // fill control rect
  599.  
  600. //----------------------------------------------------------------------------------
  601. // draw the tab title
  602. //----------------------------------------------------------------------------------
  603.             
  604.             if(inColor)
  605.                 setPartColor(theCtl, cTextColor, true);
  606.             drawTitle (theCtl, varCode, &tab, bgInColor, (**hCdef).rowChg,
  607.                         currTab, prevTab, lnHt, lnAdj);
  608.                     
  609.             if(inColor) {
  610.             
  611. //----------------------------------------------------------------------------------
  612. // draw white shadow along left & top inside edges of tab
  613. //----------------------------------------------------------------------------------
  614.  
  615.                 ForeColor(whiteColor);
  616.                 MoveTo(tab.left+1,tab.bottom-1);
  617.                 if(currTab == activeTab)
  618.                     Move(0,1);
  619.                 LineTo(tab.left+1,tab.top+3);                // left edge
  620.                 LineTo(tab.left+3,tab.top+1);                // left corner
  621.                 LineTo(tab.right-5,tab.top+1);                // top edge
  622.                 if(currTab == activeTab) {
  623.                     Move(1,1);
  624.                     LineTo(tab.left+2, tab.top+2);
  625.                     LineTo(tab.left+2, tab.bottom+1);
  626.                 }
  627.  
  628. //----------------------------------------------------------------------------------
  629. // draw white shadow on top edge of front panel (this is the bottom edge of tabs)
  630. // lots of work to avoid flicker...
  631. //----------------------------------------------------------------------------------
  632.  
  633.                 if(inx == 1) {
  634.     
  635.                     if(currTab != activeTab) {
  636.                         ForeColor(whiteColor);
  637.                         MoveTo(tab.left,tab.bottom+1);
  638.                         if(col == 1)
  639.                             Move(3,0);
  640.                         else
  641.                             Move(-1,0);
  642.                         if(col == maxCol) {
  643.                             LineTo(tab.right-3,tab.bottom+1);
  644.                             Move(-1,0);
  645.                         }
  646.                         else
  647.                             LineTo(tab.right,tab.bottom+1);
  648.                         Move(0,1);
  649.                         if(col == 1)
  650.                             LineTo(tab.left+3,tab.bottom+2);
  651.                         else
  652.                             LineTo(tab.left-2,tab.bottom+2);
  653.                     }
  654.                 }
  655.  
  656. //----------------------------------------------------------------------------------
  657. // draw gray shadow along right edge of tab
  658. //----------------------------------------------------------------------------------
  659.     
  660.                 RGBForeColor(&rgbGray);
  661.                 MoveTo(tab.right-4, tab.top+1);
  662.                 LineTo(tab.right-2, tab.top+3);                // right corner
  663.                 if(currTab == activeTab) {
  664.                     LineTo(tab.right-2, tab.bottom);        // right edge
  665.                     if(bgInColor)
  666.                         Move(-1,1);
  667.                     else
  668.                         Move(-1,0);
  669.                     RGBForeColor(&rgbGrayLt);
  670.                     LineTo(tab.right-3, tab.top+3);
  671.                 }
  672.                 else
  673.                     LineTo(tab.right-2, tab.bottom-1);
  674.                     if(col == maxCol && inx != 1)
  675.                         Line(0,2);
  676.             }
  677.             
  678. //----------------------------------------------------------------------------------
  679. // draw the tab frame
  680. //----------------------------------------------------------------------------------
  681.                 
  682.             if(inColor)
  683.                 setPartColor(theCtl, cFrameColor, true);
  684.             MoveTo(tab.left,tab.bottom);            
  685.             LineTo(tab.left,tab.top+3);                    // left edge
  686.             LineTo(tab.left+3,tab.top);                    // left corner
  687.             LineTo(tab.right-4,tab.top);                // top edge
  688.             LineTo(tab.right-1,tab.top+3);                // right corner
  689.             LineTo(tab.right-1,tab.bottom);                // right edge
  690.             
  691.             MoveTo(tab.right-2,tab.bottom);
  692.             if(currTab != activeTab && inx == 1)        // bottom edge needed
  693.                 LineTo(tab.left,tab.bottom);
  694.             else 
  695.             if(inx == 1) {                                // bottom edge not needed
  696.                 edgeRect = tab;
  697.                 edgeRect.left++;
  698.                 edgeRect.right--;
  699.                 edgeRect.top = edgeRect.bottom++;
  700.                 if(inColor) {                            // erase to bg color
  701.                     RGBForeColor(&saveBack);
  702.                     edgeRect.left+=2;
  703.                     edgeRect.right-=2;
  704.                 }
  705.                 EraseRect(&edgeRect);
  706.             }
  707.             OffsetRect(&tab,tabWidth-1, 0);                // next tab to draw
  708.         }
  709.  
  710. //----------------------------------------------------------------------------------
  711. // draw panel outline
  712. //----------------------------------------------------------------------------------
  713.             
  714.         if(inColor)
  715.             setPartColor(theCtl, cFrameColor, true);
  716.     
  717.         if(inx == 1) {                                    // first panel
  718.             pLeft = panel.left;
  719.             MoveTo(pLeft,bottomEdge);
  720.             LineTo(pLeft,panel.bottom-1);                // left edge
  721.         }
  722.         else
  723.             pLeft = panel.right-10;
  724.         MoveTo(pLeft,panel.bottom-1);    
  725.         LineTo(panel.right-1,panel.bottom-1);            // bottom edge
  726.         LineTo(panel.right-1,bottomEdge);                // right edge
  727.     
  728.         if(notch) {                                        // top edge (if needed)
  729.             Line(-notch,0);
  730.         }
  731.  
  732. //----------------------------------------------------------------------------------
  733. // draw panel shadowing inside panel frame
  734. //----------------------------------------------------------------------------------
  735.     
  736.         if(inColor) {
  737.  
  738. //----------------------------------------------------------------------------------
  739. // draw white shadow along left edge of panel
  740. //----------------------------------------------------------------------------------
  741.  
  742.             ForeColor(whiteColor);
  743.             PenSize(2,2);
  744.             if(inx == 1) {
  745.                 MoveTo(pLeft+1,bottomEdge+1);
  746.                 LineTo(pLeft+1,panel.bottom-3);
  747.             }
  748.             PenSize(1,1);
  749.  
  750. //----------------------------------------------------------------------------------
  751. // draw white shadow along top edge of panel
  752. //----------------------------------------------------------------------------------
  753.     
  754.             if(notch) {
  755.                 MoveTo(panel.right-3,bottomEdge+1);
  756.                 Line(-notch+1,0);
  757.                 if(inx == 1) {
  758.                     Move(-1,1);
  759.                     Line(notch-1,0);
  760.                 }
  761.             }
  762.  
  763. //----------------------------------------------------------------------------------
  764. // draw gray shadow along bottom edge of panel
  765. //----------------------------------------------------------------------------------
  766.     
  767.             RGBForeColor(&rgbGrayLt);
  768.             PenSize(1,1);
  769.             MoveTo(panel.right-3, panel.bottom-3);
  770.             if(inx == 1) {                                // first panel
  771.                 LineTo(pLeft+3,panel.bottom-3);
  772.                 Move(-1,1);
  773.             }
  774.             else
  775.                 MoveTo(pLeft,panel.bottom-2);
  776.             RGBForeColor(&rgbGray);
  777.             LineTo(panel.right-2, panel.bottom-2);
  778.  
  779. //----------------------------------------------------------------------------------
  780. // draw gray shadow along right edge of panel
  781. //----------------------------------------------------------------------------------
  782.             
  783.             if(inx == 1 || notch) {                        // first panel
  784.                 LineTo(panel.right-2, bottomEdge+1);
  785.                 if(bgInColor)
  786.                     Move(-1,1);
  787.                 else
  788.                     Move(-1,0);
  789.             }
  790.             else {
  791.                 LineTo(panel.right-2, tab.top+3);
  792.                 Move(-1,0);
  793.             }
  794.             RGBForeColor(&rgbGrayLt);
  795.             if(inx == 1)
  796.                 LineTo(panel.right-3, panel.bottom-3);
  797.         }
  798.  
  799. //----------------------------------------------------------------------------------
  800. // prepare to draw the next panel
  801. //----------------------------------------------------------------------------------
  802.  
  803.         OffsetRect(&panel, 10, 0);                        // draw next panel, which
  804.         panel.bottom-=10;                                // is shifted up & right
  805.         OffsetRect(&tab, 0, -tabHt);                    // shift tab up to next row
  806.         tab.left = panel.left;
  807.     }
  808.     
  809.     if(inColor)
  810.         restoreColors(&saveFore, &saveBack);
  811.     SetPenState(&savePen);
  812. }
  813.  
  814. //==================================================================================
  815. // draw the titles for the tabs.  Must take care of redrawing a previous tab in
  816. // a normal face and active tab in a bold face.
  817. //==================================================================================
  818. static void drawTitle (ControlHandle theCtl, short varCode, Rect *r,  
  819.                         Boolean bgInColor, Boolean rowChg, 
  820.                         short part, short prevTab, 
  821.                         short lnHt, short lnAdj)
  822. {
  823.     short         lnCnt=1,ctlSpace,v,h;
  824.     Str255        s;
  825.     RgnHandle    saveClip;
  826.     Rect        tRect;
  827.     RGBColor    saveFore;
  828.     
  829. //----------------------------------------------------------------------------------
  830. // clear title area if needed - click on new tab or we are swapping rows around
  831. //----------------------------------------------------------------------------------
  832.  
  833.     if(part == prevTab || part == (**theCtl).contrlValue || rowChg) {
  834.         tRect = *r;
  835.         InsetRect(&tRect, 2, 2);
  836.         if( part == (**theCtl).contrlValue)
  837.             tRect.bottom+=5;
  838.         else
  839.             tRect.bottom+=2;
  840.         EraseRect(&tRect);
  841.     }
  842.     
  843.     getNextTitle((*theCtl)->contrlTitle, s, part);
  844.     if(s[0]) {                                // is there a title?
  845.     
  846.         saveClip = NewRgn();
  847.         GetClip(saveClip);
  848.     
  849.         if(bgInColor)
  850.             GetForeColor(&saveFore);
  851.     
  852.         if(part == (**theCtl).contrlValue) {
  853.             if(varCode & sysFontTabs)
  854.                 TextFace(underline);
  855.             else
  856.                 TextFace(bold);
  857.         }
  858.         v = (*r).bottom;                    // baseline
  859.         ctlSpace = (*r).bottom - (*r).top;    // available height
  860.         v-=(ctlSpace - lnHt)/2;                // minus free space
  861.         v-=lnAdj;                            // minus descent+leading
  862.  
  863.         h = (*r).left + ((*r).right - (*r).left - StringWidth(s))/2;
  864.  
  865.         ClipRect(r);
  866.         
  867.         if(bgInColor) {                        // draw 3D title
  868.             ForeColor(whiteColor);
  869.             MoveTo(h+1, v+1);
  870.             DrawString(s);
  871.             RGBForeColor(&saveFore);
  872.         }
  873.         MoveTo(h, v);
  874.         DrawString(s);
  875.         
  876.         TextFace(normal);                    // cleanup & leave
  877.         SetClip(saveClip);
  878.     }
  879. }
  880. //==================================================================================
  881. // Grab the next tab title from the control title (assumes lines separated by CR)
  882. //==================================================================================
  883. static void getNextTitle(Str255 t, Str255 s, short part)
  884. {
  885.     short    inx=1,len=0,tCnt=1;
  886.     
  887.     s[0] = 0;                                // always default to null string
  888.     
  889.     do {
  890.         if(t[inx] == 0x0d)                    // next title
  891.             tCnt++;
  892.         else
  893.         if(tCnt == part) {                    // return this title
  894.             len++;
  895.             s[0] = len;
  896.             s[len] = t[inx];
  897.         }
  898.         inx++;
  899.     }while (inx <= t[0]);
  900. }
  901. //==================================================================================
  902. // calculate the maximum number of columns in this row of tabs
  903. //==================================================================================
  904.  
  905. static short getMaxCols (ControlHandle theCtl, short row)
  906. {
  907.     short        tabCnt;
  908.     cdefHandle    hCdef;
  909.         
  910.     hCdef = (cdefHandle)(**theCtl).contrlData;
  911.     if(!hCdef)
  912.         return 0;
  913.     
  914.     if(row == (**hCdef).nRows)                // last row
  915.         tabCnt = (**theCtl).contrlMax - ((**hCdef).nCols * (row -1));
  916.     else
  917.         tabCnt = (**hCdef).nCols;
  918.     return(tabCnt);
  919. }
  920. //==================================================================================
  921. // calculate the width of a single tab - can be different for last row if it is not
  922. // a complete row.
  923. //==================================================================================
  924.  
  925. static short getTabWidth(ControlHandle theCtl, short row)
  926. {
  927.     short        wid,tabWidth,temp,tabCnt;
  928.     cdefHandle    hCdef;
  929.         
  930.     hCdef = (cdefHandle)(**theCtl).contrlData;
  931.     if(!hCdef)
  932.         return 0;
  933.     
  934.     if(row == (**hCdef).nRows)                // last row
  935.         tabCnt = (**theCtl).contrlMax - ((**hCdef).nCols * (row -1));
  936.     else
  937.         tabCnt = (**hCdef).nCols;
  938.     
  939.     
  940.     wid = (**hCdef).rowWidth;            
  941.     
  942.     tabWidth = wid/tabCnt;                    // width of 1 tab
  943.     
  944.     temp = wid - (tabWidth-1)*tabCnt;        // round it up a bit
  945.     tabWidth = tabWidth + temp/tabCnt;
  946.     
  947.     return(tabWidth);
  948. }