home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / VISUAL_B / REFERENC / VB_GRID / GRID.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-02-23  |  52.7 KB  |  2,216 lines

  1. //---------------------------------------------------------------------------
  2. // Grid.c
  3. //---------------------------------------------------------------------------
  4. // Grid Button Control
  5. //---------------------------------------------------------------------------
  6.  
  7. #define NOCOMM
  8.  
  9. #include <windows.h>
  10.  
  11. #include "vbapi.h"
  12. #include "grid.h"
  13.  
  14. typedef union tagPARAMS // event procedure parameter profiles
  15. {
  16.     struct {
  17.     LPVOID    Index;    // reserve space for index parameter to array ctl
  18.     } Void;        // NO PARAMS: Click, GotFocus, LostFocus events
  19.             // KeyPress event is handled inside VB
  20. }
  21. PARAMS;
  22.  
  23. VOID FAR * _cdecl _fmemset(VOID FAR *, USHORT, USHORT);
  24.  
  25. ERR    NEAR ClearGridSel(HCTL hctl, HWND hwnd);
  26. VOID    NEAR GetCellRect(PGRID pgrid, SHORT Row, SHORT Col, LPRECT lprect);
  27. BHSTR    NEAR GetCellText(PGRID pgrid, SHORT Row, SHORT Col);
  28. HSZ    NEAR GetClipText(PGRID pgrid);
  29. BOOL    NEAR GridCellSelected(PGRID pgrid, SHORT Row, SHORT Col);
  30. VOID    NEAR GridKeyDown(HCTL hctl, HWND hwnd, USHORT VKKey);
  31. VOID    NEAR GridKeyUp(HCTL hctl, HWND hwnd, USHORT VKKey);
  32. VOID    NEAR GridMouseDblClick(HCTL hctl, HWND hwnd, SHORT x, SHORT y);
  33. VOID    NEAR GridMouseDown(HCTL hctl, HWND hwnd, SHORT x, SHORT y);
  34. VOID    NEAR GridMouseMove(HCTL hctl, HWND hwnd, SHORT x, SHORT y);
  35. VOID    NEAR GridMouseUp(HCTL hctl, HWND hwnd, SHORT x, SHORT y);
  36. VOID    NEAR GridScrollToView(PGRID pgrid, HWND hwnd, SHORT Row, SHORT Col);
  37. BOOL    NEAR InitGrid(HCTL hctl);
  38. VOID    NEAR InvalidateCell(PGRID pgrid, HWND hwnd, SHORT Row, SHORT Col);
  39. VOID    NEAR InvalidateCol(PGRID pgrid, HWND hwnd, SHORT Col);
  40. VOID    NEAR InvalidateRow(PGRID pgrid, HWND hwnd, SHORT Row);
  41. BHVOID    NEAR MemAlloc(USHORT cb);
  42. VOID    NEAR MemFree(BHVOID bhTemp);
  43. BHVOID    NEAR MemReAlloc(BHVOID bhTemp, USHORT cb);
  44. WORD    NEAR MemSize(BHVOID bhTemp);
  45. VOID    NEAR NewFont(PGRID pgrid, HWND hwnd);
  46. VOID    NEAR PaintGrid(PGRID pgrid, HWND hwnd, HDC hdc);
  47. SHORT    NEAR XToCol(PGRID pgrid, SHORT X, BOOL fFixed);
  48. SHORT    NEAR YToRow(PGRID pgrid, SHORT Y, BOOL fFixed);
  49. VOID    NEAR ScrollGridHorz(PGRID pgrid, HWND hwnd, USHORT Code, USHORT Val);
  50. VOID    NEAR ScrollGridVert(PGRID pgrid, HWND hwnd, USHORT Code, USHORT Val);
  51. ERR    NEAR SetCellText(HCTL hctl, HWND hwnd, SHORT Row, SHORT Col, LPSTR pszText);
  52. ERR    NEAR SetClipText(HCTL hctl, HWND hwnd, LPSTR pszText);
  53. ERR    NEAR SetGridCols(HCTL hctl, SHORT Cols);
  54. ERR    NEAR SetGridRowCol(HCTL hctl, PGRID pgrid, HWND hwnd, SHORT Row, SHORT Col);
  55. ERR    NEAR SetGridRows(HCTL hctl, SHORT Rows);
  56. VOID    NEAR SetGridScroll(PGRID pgrid, HWND hwnd);
  57. ERR    NEAR SetGridSel(HCTL hctl, HWND hwnd, BOOL fSwap,
  58.         SHORT StartRow, SHORT StartCol, SHORT EndRow, SHORT EndCol);
  59. VOID    NEAR SizeGrid(PGRID pgrid, HWND hwnd);
  60.  
  61. #define ROW(Row) (*(*pgrid->bhRowArray)->bhRowData[Row])
  62. #define ROWHEIGHT(Row) ROW(Row)->Height
  63. #define COL(Col) (*pgrid->bhColArray)->ColData[Col]
  64. #define COLWIDTH(Col) COL(Col).Width
  65. #define COLALIGN(Col) COL(Col).Alignment
  66.  
  67. #define limit(a,b,c) (min(max(a, b), c))
  68.  
  69. //---------------------------------------------------------------------------
  70. // Grid control function
  71. //---------------------------------------------------------------------------
  72. LONG _export GridCtlProc
  73. (
  74.     HCTL    hctl,
  75.     HWND    hwnd,
  76.     USHORT  msg,
  77.     USHORT  wp,
  78.     LONG    lp
  79. )
  80. {
  81.     LONG    lResult;
  82.     PGRID   pgrid;
  83.  
  84.     segGrid = (_segment)hctl;
  85.     if (hctl) {
  86.     pgrid = GRIDDEREF(hctl);
  87.     segData = pgrid->segData;
  88.     }
  89.  
  90.     // Message pre-processing
  91.     switch( msg )
  92.     {
  93.     case WM_NCCREATE:
  94.         NewFont(pgrid, hwnd);
  95.         if (!InitGrid(hctl))
  96.         return FALSE;
  97.         break;
  98.  
  99.     case WM_NCDESTROY:
  100.         GlobalFree(LOWORD(GlobalHandle(pgrid->segData)));
  101.         if (pgrid->segClip)
  102.         GlobalFree(LOWORD(GlobalHandle(pgrid->segClip)));
  103.         break;
  104.  
  105.     case WM_SETFONT:
  106.         pgrid->hFont = (HFONT)wp;
  107.         NewFont(pgrid, hwnd);
  108.         break;
  109.  
  110.     case WM_GETFONT:
  111.         return pgrid->hFont;
  112.  
  113.     case WM_ERASEBKGND:
  114.         return TRUE;
  115.  
  116.     case WM_SIZE:
  117.         SizeGrid(pgrid, hwnd);
  118.         break;
  119.  
  120.     case WM_PAINT:
  121.         if (wp)
  122.         PaintGrid(pgrid, hwnd, (HDC)wp);
  123.         else {
  124.         PAINTSTRUCT ps;
  125.  
  126.         BeginPaint(hwnd, &ps);
  127.         PaintGrid(pgrid, hwnd, ps.hdc);
  128.         EndPaint(hwnd, &ps);
  129.         }
  130.         break;
  131.  
  132.     case WM_KILLFOCUS:
  133.         pgrid->fFocus = FALSE;
  134.         InvalidateCell(pgrid, hwnd, pgrid->Row, pgrid->Col);
  135.         break;
  136.  
  137.     case WM_SETFOCUS:
  138.         pgrid->fFocus = TRUE;
  139.         InvalidateCell(pgrid, hwnd, pgrid->Row, pgrid->Col);
  140.         break;
  141.  
  142.     case WM_HSCROLL:
  143.         ScrollGridHorz(pgrid, hwnd, wp, LOWORD(lp));
  144.         break;
  145.  
  146.     case WM_VSCROLL:
  147.         ScrollGridVert(pgrid, hwnd, wp, LOWORD(lp));
  148.         break;
  149.  
  150.     case WM_LBUTTONDOWN:
  151.         GridMouseDown(hctl, hwnd, (SHORT)LOWORD(lp), (SHORT)HIWORD(lp));
  152.         return 0;
  153.  
  154.     case WM_MOUSEMOVE:
  155.         GridMouseMove(hctl, hwnd, (SHORT)LOWORD(lp), (SHORT)HIWORD(lp));
  156.         return 0;
  157.  
  158.     case WM_LBUTTONUP:
  159.         GridMouseUp(hctl, hwnd, (SHORT)LOWORD(lp), (SHORT)HIWORD(lp));
  160.         return 0;
  161.  
  162.     case WM_LBUTTONDBLCLK:
  163.         GridMouseDblClick(hctl, hwnd, (SHORT)LOWORD(lp), (SHORT)HIWORD(lp));
  164.         return 0;
  165.  
  166.     case VBM_GETPROPERTY:
  167.         switch ( wp ) {
  168.         case IPROP_GRID_TEXT:
  169.             {
  170.             BHSTR   bhStr;
  171.             BHSTR   bhStr2;
  172.  
  173.             bhStr = GetCellText(pgrid, pgrid->Row, pgrid->Col);
  174.             bhStr2 = MemAlloc((bhStr ? lstrlen(*bhStr) : 0) + 1);
  175.             if (!bhStr2)
  176.             return 1; // UNDONE
  177.             if (bhStr)
  178.             lstrcpy(*bhStr2, *bhStr);
  179.             *((HSZ FAR *)lp) = (HSZ)MAKELONG(bhStr2, segData);
  180.             return 0;
  181.             }
  182.  
  183.         case IPROP_GRID_CLIP:
  184.             {
  185.             HSZ     hszClip;
  186.  
  187.             hszClip = GetClipText(pgrid);
  188.             if (!hszClip)
  189.             return 1; // UNDONE
  190.             *((HSZ FAR *)lp) = hszClip;
  191.             return 0;
  192.             }
  193.  
  194.         case IPROP_GRID_ROWHEIGHT:
  195.             *(LONG FAR *)lp = VBYPixelsToTwips(ROWHEIGHT(pgrid->Row));
  196.             return 0;
  197.  
  198.         case IPROP_GRID_COLWIDTH:
  199.             *(LONG FAR *)lp = VBXPixelsToTwips(COLWIDTH(pgrid->Col));
  200.             return 0;
  201.  
  202.         case IPROP_GRID_COLALIGN:
  203.             *(SHORT FAR *)lp = COLALIGN(pgrid->Col);
  204.             return 0;
  205.  
  206.         case IPROP_GRID_SCROLLBARS:
  207.             *(USHORT FAR *)lp = (pgrid->fScrollHorz | (pgrid->fScrollVert << 1));
  208.             return 0;
  209.  
  210.         case IPROP_GRID_CELLSEL:
  211.             *(USHORT FAR *)lp = GridCellSelected(pgrid, pgrid->Row, pgrid->Col);
  212.             return 0;
  213.         }
  214.         break;
  215.  
  216.     case VBM_SETPROPERTY:
  217.         switch ( wp ) {
  218.         case IPROP_GRID_ROW:
  219.             if ((SHORT)lp < 0L || (SHORT)lp >= (SHORT)pgrid->Rows)
  220.             return 1;   // UNDONE
  221.  
  222.             SetGridRowCol(hctl, pgrid, hwnd, (SHORT)lp, pgrid->Col);
  223.             return 0;
  224.  
  225.         case IPROP_GRID_COL:
  226.             if ((SHORT)lp < 0L || (SHORT)lp >= (SHORT)pgrid->Cols)
  227.             return 1;   // UNDONE
  228.  
  229.             SetGridRowCol(hctl, pgrid, hwnd, pgrid->Row, (SHORT)lp);
  230.             return 0;
  231.  
  232.         case IPROP_GRID_SELSTARTROW:
  233.             if ((SHORT)lp < pgrid->FixedRows
  234.                 || (SHORT)lp >= (SHORT)pgrid->Rows)
  235.             return 1;   // UNDONE
  236.             SetGridSel(hctl, hwnd, FALSE,
  237.                      (SHORT)lp,       pgrid->SelLeftCol,
  238.                      pgrid->SelBottomRow, pgrid->SelRightCol);
  239.             return 0;
  240.  
  241.         case IPROP_GRID_SELENDROW:
  242.             if ((SHORT)lp < pgrid->FixedRows
  243.                 || (SHORT)lp >= (SHORT)pgrid->Rows)
  244.             return 1;   // UNDONE
  245.             SetGridSel(hctl, hwnd, FALSE,
  246.                      pgrid->SelTopRow, pgrid->SelLeftCol,
  247.                      (SHORT)lp,        pgrid->SelRightCol);
  248.             return 0;
  249.  
  250.         case IPROP_GRID_SELSTARTCOL:
  251.             if ((SHORT)lp < pgrid->FixedCols
  252.                 || (SHORT)lp >= (SHORT)pgrid->Cols)
  253.             return 1;   // UNDONE
  254.             SetGridSel(hctl, hwnd, FALSE,
  255.                      pgrid->SelTopRow,      (SHORT)lp,
  256.                      pgrid->SelBottomRow, pgrid->SelRightCol);
  257.             return 0;
  258.  
  259.         case IPROP_GRID_SELENDCOL:
  260.             if ((SHORT)lp < pgrid->FixedCols
  261.                 || (SHORT)lp >= (SHORT)pgrid->Cols)
  262.             return 1;   // UNDONE
  263.             SetGridSel(hctl, hwnd, FALSE,
  264.                      pgrid->SelTopRow,      pgrid->SelLeftCol,
  265.                      pgrid->SelBottomRow, (SHORT)lp);
  266.             return 0;
  267.  
  268.         case IPROP_GRID_TEXT:
  269.             return SetCellText(hctl, hwnd, pgrid->Row,
  270.                 pgrid->Col, (LPSTR)lp);
  271.  
  272.         case IPROP_GRID_CLIP:
  273.             return SetClipText(hctl, hwnd, (LPSTR)lp);
  274.  
  275.         case IPROP_GRID_ROWS:
  276.         case IPROP_GRID_COLS:
  277.             {
  278.             ERR err;
  279.  
  280.             if ((SHORT)lp <= 0)
  281.             return 1;   // UNDONE
  282.             if (wp == IPROP_GRID_ROWS) {
  283.             if (pgrid->Rows == (SHORT)lp)
  284.                 return 0;
  285.             err = SetGridRows(hctl, (SHORT)lp);
  286.             }
  287.             else {
  288.             if (pgrid->Cols == (SHORT)lp)
  289.                 return 0;
  290.             err = SetGridCols(hctl, (SHORT)lp);
  291.             }
  292.             SizeGrid(GRIDDEREF(hctl), hwnd);
  293.             InvalidateRect(hwnd, NULL, FALSE);
  294.             return err;
  295.             }
  296.  
  297.         case IPROP_GRID_FIXEDROWS:
  298.             if ((SHORT)lp < 0 || (SHORT)lp >= (SHORT)pgrid->Rows)
  299.             return 1;   // UNDONE
  300.             if (pgrid->FixedRows == (SHORT)lp)
  301.             return 0;
  302.             pgrid->TopRow -= pgrid->FixedRows - (SHORT)lp;
  303.             pgrid->FixedRows = (SHORT)lp;
  304.             SizeGrid(pgrid, hwnd);
  305.             InvalidateRect(hwnd, NULL, FALSE);
  306.             return 0;
  307.  
  308.         case IPROP_GRID_FIXEDCOLS:
  309.             if ((SHORT)lp < 0 || (SHORT)lp >= (SHORT)pgrid->Cols)
  310.             return 1;   // UNDONE
  311.             if (pgrid->FixedCols == (SHORT)lp)
  312.             return 0;
  313.             pgrid->LeftCol -= pgrid->FixedCols - (SHORT)lp;
  314.             pgrid->FixedCols = (SHORT)lp;
  315.             SizeGrid(pgrid, hwnd);
  316.             InvalidateRect(hwnd, NULL, FALSE);
  317.             return 0;
  318.  
  319.         case IPROP_GRID_ROWHEIGHT:
  320.             {
  321.             SHORT   Height;
  322.  
  323.             // UNDONE: limit overall height
  324.             Height = VBYTwipsToPixels((LONG)lp);
  325.             if (ROWHEIGHT(pgrid->Row) == Height)
  326.             return 0;
  327.             ROWHEIGHT(pgrid->Row) = Height;
  328.             SizeGrid(pgrid, hwnd);
  329.             InvalidateRect(hwnd, NULL, FALSE);
  330.             return 0;
  331.             }
  332.  
  333.         case IPROP_GRID_COLWIDTH:
  334.             {
  335.             SHORT   Width;
  336.  
  337.             // UNDONE: limit overall width
  338.             Width = VBXTwipsToPixels((LONG)lp);
  339.             if (COLWIDTH(pgrid->Col) == Width)
  340.             return 0;
  341.             COLWIDTH(pgrid->Col) = Width;
  342.             SizeGrid(pgrid, hwnd);
  343.             InvalidateRect(hwnd, NULL, FALSE);
  344.             return 0;
  345.             }
  346.  
  347.         case IPROP_GRID_COLALIGN:
  348.             if ((SHORT)lp > 2)
  349.             return 1;   // UNDONE
  350.             if (COLALIGN(pgrid->Col) == (SHORT)lp)
  351.             return 0;
  352.             COLALIGN(pgrid->Col) = (SHORT)lp;
  353.             InvalidateCol(pgrid, hwnd, pgrid->Col);
  354.             return 0;
  355.  
  356.         case IPROP_GRID_SCROLLBARS:
  357.             {
  358.             if ((USHORT)lp == (pgrid->fScrollHorz | (pgrid->fScrollVert << 1)))
  359.             return 0;
  360.             pgrid->fScrollHorz = ((USHORT)lp & 1) != 0;
  361.             pgrid->fScrollVert = ((USHORT)lp & 2) != 0;
  362.             SetGridScroll(pgrid, hwnd);
  363.             InvalidateRect(hwnd, NULL, TRUE);
  364.             return 0;
  365.             }
  366.         }
  367.         break;
  368.     }
  369.  
  370.     // Default processing:
  371.     lResult = VBDefControlProc( hctl, hwnd, msg, wp, lp );
  372.  
  373.     // Message post-processing:
  374.     switch( msg )
  375.     {
  376.     case WM_KEYDOWN:
  377.         // make sure the control is still valid after events
  378.         if (!IsWindow(hwnd))
  379.         break;
  380.         GridKeyDown(hctl, hwnd, wp);
  381.         return 0;
  382.  
  383.     case WM_KEYUP:
  384.         // make sure the control is still valid after events
  385.         if (!IsWindow(hwnd))
  386.         break;
  387.         GridKeyUp(hctl, hwnd, wp);
  388.         return 0;
  389.     }
  390.  
  391.     return lResult;
  392. }
  393.  
  394.  
  395. //---------------------------------------------------------------------------
  396. // SetGridRowCol
  397. //---------------------------------------------------------------------------
  398. ERR NEAR SetGridRowCol
  399. (
  400.     HCTL    hctl,
  401.     PGRID   pgrid,
  402.     HWND    hwnd,
  403.     SHORT   Row,
  404.     SHORT   Col
  405. )
  406. {
  407.     if (pgrid->Row == Row && pgrid->Col == Col)
  408.     return 0;
  409.     if (pgrid->fFocus && hwnd)
  410.     InvalidateCell(pgrid, hwnd, pgrid->Row, pgrid->Col);
  411.     pgrid->Row = Row;
  412.     pgrid->Col = Col;
  413.     if (pgrid->fFocus && hwnd)
  414.     InvalidateCell(pgrid, hwnd, Row, Col);
  415.     return VBFireEvent(hctl, EVENT_GRID_CLICK, NULL);
  416. }
  417.  
  418.  
  419. //---------------------------------------------------------------------------
  420. // SetGridCols
  421. //---------------------------------------------------------------------------
  422. ERR NEAR SetGridCols
  423. (
  424.     HCTL    hctl,
  425.     SHORT   Cols
  426. )
  427. {
  428.     BHVOID  bhTemp;
  429.     SHORT   Row;
  430.     SHORT   Col;
  431.     SHORT   cb;
  432.     ERR     err;
  433.     PGRID   pgrid = GRIDDEREF(hctl);
  434.  
  435.     if (Cols == pgrid->Cols)
  436.     return 0;
  437.  
  438.     cb = sizeof(COLARRAY) + sizeof(COLDATA) * (Cols-1);
  439.     if (Cols == 0) {
  440.     bhTemp = NULL;
  441.     MemFree((BHVOID)pgrid->bhColArray);
  442.     }
  443.     else {
  444.     err = SetGridRowCol(hctl, pgrid, NULL, pgrid->Row, min(pgrid->Col,Cols-1));
  445.     if (err)
  446.         return err;
  447.     pgrid = GRIDDEREF(hctl);
  448.     err = SetGridSel(hctl, NULL, FALSE,
  449.              pgrid->SelTopRow,    min(pgrid->SelLeftCol,Cols-1),
  450.              pgrid->SelBottomRow, min(pgrid->SelRightCol,Cols-1));
  451.     if (err)
  452.         return err;
  453.     pgrid = GRIDDEREF(hctl);
  454.     if (pgrid->FixedCols >= Cols)
  455.         pgrid->FixedCols = Cols - 1;
  456.     pgrid->LeftCol = pgrid->KeySelCol = pgrid->FixedCols;
  457.     if (pgrid->Cols)
  458.         bhTemp = MemReAlloc(pgrid->bhColArray, cb);
  459.     else
  460.         bhTemp = MemAlloc(cb);
  461.     if (!bhTemp)
  462.         return 1;    // UNDONE
  463.     pgrid = GRIDDEREF(hctl);
  464.     }
  465.     pgrid->bhColArray = (COLARRAY DBH)bhTemp;
  466.     if (Cols > pgrid->Cols)
  467.     // set default widths for all new columns
  468.     for (Col = pgrid->Cols; Col < Cols; Col++) {
  469.         COLWIDTH(Col) = pgrid->DefWidth;
  470.         COL(Col).fDefWidth = TRUE;
  471.         COLALIGN(Col) = DT_CENTER;
  472.         }
  473.     else
  474.     // shorten row data to remove old columns
  475.     for (Row = 0; Row < pgrid->Rows; Row++)
  476.         if (Cols < ROW(Row)->Cols) {
  477.         for (Col = Cols; Col < ROW(Row)->Cols; Col++) {
  478.             bhTemp = ROW(Row)->bhStr[Col];
  479.             if (bhTemp)
  480.             MemFree(bhTemp);
  481.             }
  482.         // reduce row size
  483.         bhTemp = MemReAlloc((*pgrid->bhRowArray)->bhRowData[Row],
  484.             sizeof(ROWDATA) + sizeof(BHSTR)*(Cols-1));
  485.         pgrid = GRIDDEREF(hctl);
  486.         (*pgrid->bhRowArray)->bhRowData[Row] = (ROWDATA DBH)bhTemp;
  487.         ROW(Row)->Cols = Cols;
  488.         }
  489.     pgrid->Cols = Cols;
  490.     return 0;
  491. }
  492.  
  493.  
  494. //---------------------------------------------------------------------------
  495. // SetGridRows
  496. //---------------------------------------------------------------------------
  497. ERR NEAR SetGridRows
  498. (
  499.     HCTL    hctl,
  500.     SHORT   Rows
  501. )
  502. {
  503.     BHVOID  bhTemp;
  504.     SHORT   Row;
  505.     SHORT   Col;
  506.     SHORT   cb;
  507.     ERR     err;
  508.     PGRID   pgrid = GRIDDEREF(hctl);
  509.  
  510.     if (Rows == pgrid->Rows)
  511.     return 0;
  512.  
  513.     if (Rows < pgrid->Rows)
  514.     // remove old rows
  515.     for (Row = Rows; Row < pgrid->Rows; Row++) {
  516.         for (Col = 0; Col < ROW(Row)->Cols; Col++) {
  517.         bhTemp = ROW(Row)->bhStr[Col];
  518.         if (bhTemp)
  519.             MemFree(bhTemp);
  520.         }
  521.         MemFree((BHVOID)(*pgrid->bhRowArray)->bhRowData[Row]);
  522.         }
  523.     if (Rows == 0) {
  524.     bhTemp = NULL;
  525.     MemFree((BHVOID)pgrid->bhRowArray);
  526.     }
  527.     else {
  528.     err = SetGridRowCol(hctl, pgrid, NULL, min(pgrid->Row,Rows-1), pgrid->Col);
  529.     if (err)
  530.         return err;
  531.     pgrid = GRIDDEREF(hctl);
  532.     err = SetGridSel(hctl, NULL, FALSE,
  533.              min(pgrid->SelTopRow,Rows-1),      pgrid->SelLeftCol,
  534.              min(pgrid->SelBottomRow,Rows-1), pgrid->SelRightCol);
  535.     if (err)
  536.         return err;
  537.     pgrid = GRIDDEREF(hctl);
  538.     if (pgrid->FixedRows >= Rows)
  539.         pgrid->FixedRows = Rows - 1;
  540.     pgrid->TopRow = pgrid->KeySelRow = pgrid->FixedRows;
  541.     cb = sizeof(ROWARRAY) + sizeof(ROWDATA DBH) * (Rows-1);
  542.     if (pgrid->Rows)
  543.         bhTemp = MemReAlloc(pgrid->bhRowArray, cb);
  544.     else
  545.         bhTemp = MemAlloc(cb);
  546.     if (!bhTemp)
  547.         return 1;    // UNDONE
  548.     pgrid = GRIDDEREF(hctl);
  549.     }
  550.     pgrid->bhRowArray = (ROWARRAY DBH)bhTemp;
  551.  
  552.     if (Rows > pgrid->Rows)
  553.     // setup all new rows
  554.     for (Row = pgrid->Rows; Row < Rows; Row++) {
  555.         bhTemp = MemAlloc(sizeof(ROWDATA));
  556.         pgrid = GRIDDEREF(hctl);
  557.         if (!bhTemp) {
  558.         while (--Row >= pgrid->Rows)
  559.             MemFree((BHVOID)(*pgrid->bhRowArray)->bhRowData[Row]);
  560.         if (!pgrid->Rows)
  561.             MemFree((BHVOID)pgrid->bhRowArray);
  562.         return 1;   // UNDONE
  563.         }
  564.         (*pgrid->bhRowArray)->bhRowData[Row] = (ROWDATA DBH)bhTemp;
  565.         ROWHEIGHT(Row) = pgrid->DefHeight;
  566.         ROW(Row)->fDefHeight = TRUE;
  567.         }
  568.     pgrid->Rows = Rows;
  569.     return 0;
  570. }
  571.  
  572. //---------------------------------------------------------------------------
  573. // InitGrid
  574. //---------------------------------------------------------------------------
  575. BOOL NEAR InitGrid
  576. (
  577.     HCTL    hctl
  578. )
  579. {
  580.     HANDLE  hseg;
  581.  
  582.     PGRID   pgrid = GRIDDEREF(hctl);
  583.  
  584.     pgrid->Row = 1;
  585.     pgrid->Col = 1;
  586.     pgrid->Rows = 0;
  587.     pgrid->Cols = 0;
  588.     pgrid->FixedRows = 1;
  589.     pgrid->FixedCols = 1;
  590.     pgrid->TopRow = 1;
  591.     pgrid->LeftCol = 1;
  592.     pgrid->KeySelRow = pgrid->FixedRows;
  593.     pgrid->KeySelCol = pgrid->FixedCols;
  594.     pgrid->fSelection = FALSE;
  595.     pgrid->hFont = NULL;
  596.     pgrid->fScrollVert = TRUE;
  597.     pgrid->fScrollHorz = TRUE;
  598.  
  599.     hseg = GlobalAlloc(GHND, 1000);
  600.     if (!hseg)
  601.     return FALSE;
  602.     pgrid->segData = segData = (_segment)GlobalLock(hseg);
  603.     LocalInit(segData, 16, (USHORT)GlobalSize(hseg));
  604.  
  605.     if (SetGridCols(hctl, 2))
  606.     return FALSE;
  607.  
  608.     if (SetGridRows(hctl, 2)) {
  609.     GlobalFree(hseg);
  610.     return FALSE;
  611.     }
  612.  
  613.     return TRUE;
  614. }
  615.  
  616.  
  617. //---------------------------------------------------------------------------
  618. // SizeGrid
  619. //---------------------------------------------------------------------------
  620. VOID NEAR SizeGrid
  621. (
  622.     PGRID   pgrid,
  623.     HWND    hwnd
  624. )
  625. {
  626.     RECT    rect;
  627.     SHORT   Row;
  628.     SHORT   Col;
  629.     SHORT   coord;
  630.  
  631.     GetClientRect(hwnd, &rect);
  632.  
  633.     coord = 0;
  634.     for (Row = 0; Row < pgrid->Rows; Row++) {
  635.     if (Row == pgrid->FixedRows) {
  636.         pgrid->OrgY = coord;
  637.         Row = pgrid->TopRow;
  638.         }
  639.     coord += ROWHEIGHT(Row) + 1;
  640.     pgrid->BottomRow = Row;
  641.     if (Row >= pgrid->TopRow && coord >= rect.bottom)
  642.         break;
  643.     }
  644.     pgrid->Height = min(coord, rect.bottom);
  645.     pgrid->WholeBottomRow = pgrid->BottomRow;
  646.     if (coord > rect.bottom && pgrid->WholeBottomRow > pgrid->TopRow)
  647.     pgrid->WholeBottomRow--;
  648.  
  649.     coord = rect.bottom;
  650.     Row = pgrid->Rows;
  651.     do {
  652.     coord -= ROWHEIGHT(Row-1) + 1;
  653.     } while (coord >= pgrid->OrgY && --Row > pgrid->FixedRows);
  654.     pgrid->TopRowMax = min(Row, pgrid->Rows-1);
  655.  
  656.     coord = 0;
  657.     for (Col = 0; Col < pgrid->Cols; Col++) {
  658.     if (Col == pgrid->FixedCols) {
  659.         pgrid->OrgX = coord;
  660.         Col = pgrid->LeftCol;
  661.         }
  662.     coord += COLWIDTH(Col) + 1;
  663.     pgrid->RightCol = Col;
  664.     if (Col >= pgrid->LeftCol && coord >= rect.right)
  665.         break;
  666.     }
  667.     pgrid->Width = min(coord, rect.right);
  668.     pgrid->WholeRightCol = pgrid->RightCol;
  669.     if (coord > rect.right && pgrid->WholeRightCol > pgrid->LeftCol)
  670.     pgrid->WholeRightCol--;
  671.  
  672.     coord = rect.right;
  673.     Col = pgrid->Cols;
  674.     do {
  675.     coord -= COLWIDTH(Col-1) + 1;
  676.     } while (coord >= pgrid->OrgX && --Col > pgrid->FixedCols);
  677.     pgrid->LeftColMax = min(Col, pgrid->Cols-1);
  678.  
  679.     SetGridScroll(pgrid, hwnd);
  680. }
  681.  
  682. //---------------------------------------------------------------------------
  683. // SetGridScroll
  684. //---------------------------------------------------------------------------
  685. VOID NEAR SetGridScroll
  686. (
  687.     PGRID   pgrid,
  688.     HWND    hwnd
  689. )
  690. {
  691.     RECT    rcHome, rcEnd;
  692.  
  693.     GetCellRect(pgrid, pgrid->FixedRows, pgrid->FixedCols, &rcHome);
  694.     GetCellRect(pgrid, pgrid->TopRowMax, pgrid->LeftColMax, &rcEnd);
  695.     if (pgrid->fScrollHorz) {
  696.     SetScrollRange(hwnd, SB_HORZ, rcHome.left, rcEnd.left, FALSE);
  697.     SetScrollPos(hwnd, SB_HORZ, pgrid->OrgX, TRUE);
  698.     }
  699.     else {
  700.     SetScrollRange(hwnd, SB_HORZ, 0, 0, FALSE);
  701.     SetScrollPos(hwnd, SB_HORZ, 0, TRUE);
  702.     }
  703.     if (pgrid->fScrollVert) {
  704.     SetScrollRange(hwnd, SB_VERT, rcHome.top, rcEnd.top, FALSE);
  705.     SetScrollPos(hwnd, SB_VERT, pgrid->OrgY, TRUE);
  706.     }
  707.     else {
  708.     SetScrollRange(hwnd, SB_VERT, 0, 0, FALSE);
  709.     SetScrollPos(hwnd, SB_VERT, 0, TRUE);
  710.     }
  711. }
  712.  
  713.  
  714. //---------------------------------------------------------------------------
  715. // GridCellSelected
  716. //---------------------------------------------------------------------------
  717. BOOL NEAR GridCellSelected
  718. (
  719.     PGRID   pgrid,
  720.     SHORT   Row,
  721.     SHORT   Col
  722. )
  723. {
  724.     if (pgrid->fSelection
  725.         && Row >= pgrid->SelTopRow && Row <= pgrid->SelBottomRow
  726.         && Col >= pgrid->SelLeftCol && Col <= pgrid->SelRightCol)
  727.     return TRUE;
  728.     return FALSE;
  729. }
  730.  
  731.  
  732. //---------------------------------------------------------------------------
  733. // PaintGrid
  734. //---------------------------------------------------------------------------
  735. VOID NEAR PaintGrid
  736. (
  737.     PGRID   pgrid,
  738.     HWND    hwnd,
  739.     HDC     hdc
  740. )
  741. {
  742.     RECT    rcCell;
  743.     SHORT   Row;
  744.     SHORT   Col;
  745.     RECT    rcClient;
  746.     LPSTR   pszText;
  747.     BHSTR   bhStr;
  748.     BOOL    fFocus;
  749.     HBRUSH  hbrGray;
  750.     HBRUSH  hbrBlack;
  751.     DWORD   colorUse;
  752.     DWORD   colorHilite;
  753.     DWORD   colorText;
  754.     HBRUSH  hbr;
  755.     HBRUSH  hbrUse;
  756.     HBRUSH  hbrHilite = NULL;
  757.  
  758.     GetClientRect(hwnd, &rcClient);
  759.     hbr = (HBRUSH)SendMessage(GetParent(hwnd), WM_CTLCOLOR, hdc, MAKELONG(hwnd, 0));
  760.     colorText = GetTextColor(hdc);
  761.     if (pgrid->fSelection) {
  762.     colorHilite = GetSysColor(COLOR_HIGHLIGHTTEXT);
  763.     hbrHilite = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
  764.     }
  765.     // Use the gizmo's font, if any
  766.     if (pgrid->hFont)
  767.     SelectObject(hdc, pgrid->hFont);
  768.     hbrGray = GetStockObject(LTGRAY_BRUSH); // UNDONE
  769.     hbrBlack = GetStockObject(BLACK_BRUSH); // UNDONE
  770.     rcCell.top = 0;
  771.     for (Row = 0; Row <= pgrid->BottomRow; Row++) {
  772.     if (Row == pgrid->FixedRows)
  773.         Row = pgrid->TopRow;
  774.     rcCell.bottom = rcCell.top + ROWHEIGHT(Row);
  775.     if (Row != pgrid->BottomRow) {
  776.         SelectObject(hdc, hbrBlack);
  777.         PatBlt(hdc, 0, rcCell.bottom, pgrid->OrgX, 1, PATCOPY);
  778.         if (Row >= pgrid->FixedRows)
  779.         SelectObject(hdc, hbrGray);
  780.         PatBlt(hdc, pgrid->OrgX, rcCell.bottom,
  781.             pgrid->Width - pgrid->OrgX, 1, PATCOPY);
  782.         }
  783.     rcCell.left = 0;
  784.     for (Col = 0; Col <= pgrid->RightCol; Col++) {
  785.         if (Col == pgrid->FixedCols)
  786.         Col = pgrid->LeftCol;
  787.         rcCell.right = rcCell.left + COLWIDTH(Col);
  788.         if (rcCell.top == 0 && Col != pgrid->RightCol) {
  789.         SelectObject(hdc, hbrBlack);
  790.         PatBlt(hdc, rcCell.right, 0, 1, pgrid->OrgY, PATCOPY);
  791.         if (Col >= pgrid->FixedCols)
  792.             SelectObject(hdc, hbrGray);
  793.         PatBlt(hdc, rcCell.right, pgrid->OrgY,
  794.             1, pgrid->Height - pgrid->OrgY, PATCOPY);
  795.         }
  796.         if (RectVisible(hdc, &rcCell)) {
  797.         colorUse = colorText;
  798.         fFocus = FALSE;
  799.         if (Row < pgrid->FixedRows || Col < pgrid->FixedCols)
  800.             hbrUse = hbrGray;
  801.         else if (pgrid->fFocus && Row == pgrid->Row && Col == pgrid->Col) {
  802.             fFocus = TRUE;
  803.             hbrUse = hbr;
  804.             }
  805.         else if (GridCellSelected(pgrid, Row, Col)) {
  806.             hbrUse = hbrHilite;
  807.             colorUse = colorHilite;
  808.             }
  809.         else
  810.             hbrUse = hbr;
  811.         bhStr = GetCellText(pgrid, Row, Col);
  812.         FillRect(hdc, &rcCell, hbrUse);
  813.         if (bhStr) {
  814.             pszText = (LPSTR)*bhStr;
  815.             rcCell.left++;
  816.             InflateRect(&rcCell, -1, -1);
  817.             SetTextColor(hdc, colorUse);
  818.             SetBkMode(hdc, TRANSPARENT);
  819.             DrawText(hdc, pszText, -1, &rcCell,
  820.                 DT_WORDBREAK | COLALIGN(Col));
  821.             InflateRect(&rcCell, 1, 1);
  822.             rcCell.left--;
  823.             }
  824.         if (fFocus)
  825.             DrawFocusRect(hdc, &rcCell);
  826.         }
  827.         rcCell.left = rcCell.right + 1;
  828.         }
  829.     rcCell.top = rcCell.bottom + 1;
  830.     }
  831.     SelectObject(hdc, hbrBlack);
  832.     PatBlt(hdc, 0, rcCell.bottom, rcCell.right, 1, PATCOPY);
  833.     PatBlt(hdc, rcCell.right, 0, 1, rcCell.bottom, PATCOPY);
  834.     // fill area behind grid
  835.     rcCell = rcClient;
  836.     rcCell.top = pgrid->Height;
  837.     FillRect(hdc, &rcCell, hbrGray);
  838.     rcCell.top = 0;
  839.     rcCell.left = pgrid->Width;
  840.     rcCell.bottom = pgrid->Height;
  841.     FillRect(hdc, &rcCell, hbrGray);
  842.     SelectObject(hdc, hbr);
  843.     if (hbrHilite)
  844.     DeleteObject(hbrHilite);
  845. }
  846.  
  847.  
  848. //---------------------------------------------------------------------------
  849. // MemAlloc
  850. //---------------------------------------------------------------------------
  851.  
  852. BHVOID NEAR MemAlloc
  853. (
  854.     USHORT  cb
  855. )
  856. {
  857.     HANDLE  hTemp;
  858.  
  859.     _asm
  860.     {
  861.     push    ds
  862.     mov    ds,segData
  863.     }
  864.  
  865.     hTemp = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, cb);
  866.  
  867.     _asm
  868.     {
  869.     pop    ds
  870.     }
  871.  
  872.     return (BHVOID)hTemp;
  873. }
  874.  
  875.  
  876. //---------------------------------------------------------------------------
  877. // MemReAlloc
  878. //---------------------------------------------------------------------------
  879.  
  880. BHVOID NEAR MemReAlloc
  881. (
  882.     BHVOID  bhTemp,
  883.     USHORT  cb
  884. )
  885. {
  886.     HANDLE  hTemp;
  887.     WORD    size1;
  888.     WORD    size2;
  889.  
  890.     size1 = MemSize(bhTemp);
  891.     if (size1 >= cb)
  892.     return bhTemp;
  893.  
  894.     _asm
  895.     {
  896.     push    ds
  897.     mov    ds,segData
  898.     }
  899.  
  900.     hTemp = LocalReAlloc((HANDLE)bhTemp, cb, LMEM_MOVEABLE | LMEM_ZEROINIT);
  901.  
  902.     _asm
  903.     {
  904.     pop    ds
  905.     }
  906.  
  907.     if (hTemp) {
  908.     size2 = MemSize((BHVOID)hTemp);
  909.     _fmemset((*(BHSTR)hTemp)+size1, 0, size2-size1);
  910.     }
  911.  
  912.     return (BHVOID)hTemp;
  913. }
  914.  
  915.  
  916. //---------------------------------------------------------------------------
  917. // MemFree
  918. //---------------------------------------------------------------------------
  919.  
  920. VOID NEAR MemFree
  921. (
  922.     BHVOID  bhTemp
  923. )
  924. {
  925.     _asm
  926.     {
  927.     push    ds
  928.     mov    ds,segData
  929.     }
  930.  
  931.     LocalFree((HANDLE)bhTemp);
  932.  
  933.     _asm
  934.     {
  935.     pop    ds
  936.     }
  937.  
  938.     return bhTemp;
  939. }
  940.  
  941.  
  942. //---------------------------------------------------------------------------
  943. // MemSize
  944. //---------------------------------------------------------------------------
  945.  
  946. WORD NEAR MemSize
  947. (
  948.     BHVOID  bhTemp
  949. )
  950. {
  951.     WORD    size;
  952.  
  953.     _asm
  954.     {
  955.     push    ds
  956.     mov    ds,segData
  957.     }
  958.  
  959.     size = LocalSize((HANDLE)bhTemp);
  960.  
  961.     _asm
  962.     {
  963.     pop    ds
  964.     }
  965.  
  966.     return size;
  967. }
  968.  
  969.  
  970. //---------------------------------------------------------------------------
  971. // GetCellText
  972. //---------------------------------------------------------------------------
  973. BHSTR NEAR GetCellText
  974. (
  975.     PGRID   pgrid,
  976.     SHORT   Row,
  977.     SHORT   Col
  978. )
  979. {
  980.     if (Col >= ROW(Row)->Cols)
  981.     return NULL;
  982.     return ROW(Row)->bhStr[Col];
  983. }
  984.  
  985.  
  986. //---------------------------------------------------------------------------
  987. // SetCellText
  988. //---------------------------------------------------------------------------
  989. ERR NEAR SetCellText
  990. (
  991.     HCTL    hctl,
  992.     HWND    hwnd,
  993.     SHORT   Row,
  994.     SHORT   Col,
  995.     LPSTR   pszText
  996. )
  997. {
  998.     BHVOID  bhTemp;
  999.     SHORT   cbLen;
  1000.     PGRID   pgrid = GRIDDEREF(hctl);
  1001.  
  1002.     cbLen = pszText ? lstrlen(pszText) : 0;
  1003.     if (Col >= ROW(Row)->Cols) {
  1004.     if (cbLen == 0)
  1005.         return 0;
  1006.     // grow data for this row to include this column
  1007.     bhTemp = MemReAlloc((*pgrid->bhRowArray)->bhRowData[Row],
  1008.         sizeof(ROWDATA) + sizeof(BHSTR)*Col);
  1009.     if (!bhTemp)
  1010.         return 1;    // UNDONE: idaNoMem
  1011.     pgrid = GRIDDEREF(hctl);
  1012.     (*pgrid->bhRowArray)->bhRowData[Row] = (ROWDATA DBH)bhTemp;
  1013.     // clear new cols
  1014.     _fmemset(&ROW(Row)->bhStr[ROW(Row)->Cols], 0,
  1015.         (Col+1-ROW(Row)->Cols)*sizeof(BHSTR));
  1016.     ROW(Row)->Cols = Col+1;
  1017.     if (ROW(Row)->bhStr[Col])   // UNDONE: remove
  1018.         return 5;
  1019.     }
  1020.     bhTemp = ROW(Row)->bhStr[Col];
  1021.     if (cbLen == 0) {
  1022.     // removing the item
  1023.     if (bhTemp == NULL)
  1024.         // already gone
  1025.         return 0;
  1026.     // free it
  1027.     MemFree(bhTemp);
  1028.     bhTemp = NULL;
  1029.     }
  1030.     else {
  1031.     if (bhTemp)
  1032.         // RE-allocate existing string for this entry
  1033.         bhTemp = MemReAlloc(bhTemp, cbLen+1);
  1034.     else
  1035.         // allocate new string for this entry
  1036.         bhTemp = MemAlloc(cbLen+1);
  1037.     if (!bhTemp)
  1038.         return 1;        // UNDONE: idaNoMem
  1039.     lstrcpy((LPSTR)*(BHSTR)bhTemp, pszText);
  1040.     pgrid = GRIDDEREF(hctl);
  1041.     }
  1042.     ROW(Row)->bhStr[Col] = (BHSTR)bhTemp;
  1043.     InvalidateCell(pgrid, hwnd, Row, Col);
  1044.     return 0;
  1045. }
  1046.  
  1047.  
  1048. //---------------------------------------------------------------------------
  1049. // CellIsUnobstructed
  1050. //---------------------------------------------------------------------------
  1051. BOOL NEAR CellIsUnobstructed
  1052. (
  1053.     PGRID   pgrid,
  1054.     SHORT   Row,
  1055.     SHORT   Col
  1056. )
  1057. {
  1058.     BOOL    a,b,c,d;
  1059.  
  1060.     a = (Row >= pgrid->TopRow && Row <= pgrid->WholeBottomRow);
  1061.     b = (Col >= pgrid->LeftCol && Col <= pgrid->WholeRightCol);
  1062.     c = (Row < pgrid->FixedRows);
  1063.     d = (Col < pgrid->FixedCols);
  1064.  
  1065.     return (a && (b || d) || b && (a || c) || c && d);
  1066. }
  1067.  
  1068.  
  1069. //---------------------------------------------------------------------------
  1070. // CellIsVisible
  1071. //---------------------------------------------------------------------------
  1072. BOOL NEAR CellIsVisible
  1073. (
  1074.     PGRID   pgrid,
  1075.     SHORT   Row,
  1076.     SHORT   Col
  1077. )
  1078. {
  1079.     BOOL    a,b,c,d;
  1080.  
  1081.     a = (Row >= pgrid->TopRow && Row <= pgrid->BottomRow);
  1082.     b = (Col >= pgrid->LeftCol && Col <= pgrid->RightCol);
  1083.     c = (Row < pgrid->FixedRows);
  1084.     d = (Col < pgrid->FixedCols);
  1085.  
  1086.     return (a && (b || d) || b && (a || c) || c && d);
  1087. }
  1088.  
  1089.  
  1090. //---------------------------------------------------------------------------
  1091. // XToCol
  1092. //---------------------------------------------------------------------------
  1093. SHORT  NEAR XToCol
  1094. (
  1095.     PGRID   pgrid,
  1096.     SHORT   X,
  1097.     BOOL    fFixed
  1098. )
  1099. {
  1100.     SHORT   c;
  1101.     SHORT   lim;
  1102.     SHORT   coord;
  1103.  
  1104.     coord = pgrid->OrgX;
  1105.     if (X >= pgrid->OrgX) {
  1106.     for (c = pgrid->LeftCol; c < pgrid->Cols; c++) {
  1107.         coord += COLWIDTH(c) + 1;
  1108.         if (X <= coord)
  1109.         return c;
  1110.         }
  1111.     return pgrid->Cols-1;
  1112.     }
  1113.     else {
  1114.     if (fFixed) {
  1115.         lim = 0;
  1116.         c = pgrid->FixedCols-1;
  1117.         }
  1118.     else {
  1119.         lim = pgrid->FixedCols;
  1120.         c = pgrid->LeftCol-1;
  1121.         }
  1122.     for (; c > lim; c--) {
  1123.         coord -= COLWIDTH(c) + 1;
  1124.         if (X > coord)
  1125.         return c;
  1126.         }
  1127.     return lim;
  1128.     }
  1129. }
  1130.  
  1131.  
  1132. //---------------------------------------------------------------------------
  1133. // YToRow
  1134. //---------------------------------------------------------------------------
  1135. SHORT  NEAR YToRow
  1136. (
  1137.     PGRID   pgrid,
  1138.     SHORT   Y,
  1139.     BOOL    fFixed
  1140. )
  1141. {
  1142.     SHORT   r;
  1143.     SHORT   lim;
  1144.     SHORT   coord;
  1145.  
  1146.     coord = pgrid->OrgY;
  1147.     if (Y >= pgrid->OrgY) {
  1148.     for (r = pgrid->TopRow; r < pgrid->Rows; r++) {
  1149.         coord += ROWHEIGHT(r) + 1;
  1150.         if (Y <= coord)
  1151.         return r;
  1152.         }
  1153.     return pgrid->Rows-1;
  1154.     }
  1155.     else {
  1156.     if (fFixed) {
  1157.         lim = 0;
  1158.         r = pgrid->FixedRows-1;
  1159.         }
  1160.     else {
  1161.         lim = pgrid->FixedRows;
  1162.         r = pgrid->TopRow-1;
  1163.         }
  1164.     for (; r > lim; r--) {
  1165.         coord -= ROWHEIGHT(r) + 1;
  1166.         if (Y > coord)
  1167.         return r;
  1168.         }
  1169.     return lim;
  1170.     }
  1171. }
  1172.  
  1173.  
  1174. //---------------------------------------------------------------------------
  1175. // GetCellRect
  1176. //---------------------------------------------------------------------------
  1177. VOID NEAR GetCellRect
  1178. (
  1179.     PGRID   pgrid,
  1180.     SHORT   Row,
  1181.     SHORT   Col,
  1182.     LPRECT  lprect
  1183. )
  1184. {
  1185.     SHORT   i;
  1186.     SHORT   coord;
  1187.  
  1188.     coord = pgrid->OrgY;
  1189.     if (Row >= pgrid->TopRow)
  1190.     for (i = pgrid->TopRow; i < Row; i++)
  1191.         coord += ROWHEIGHT(i) + 1;
  1192.     else if (Row >= pgrid->FixedRows)
  1193.     for (i = 0; i < pgrid->TopRow; i++)
  1194.         coord -= ROWHEIGHT(i) + 1;
  1195.     else {
  1196.     coord = 0;
  1197.     for (i = 0; i < Row; i++)
  1198.         coord += ROWHEIGHT(i) + 1;
  1199.     }
  1200.     lprect->top = coord;
  1201.     lprect->bottom = coord + ROWHEIGHT(Row) + 1;
  1202.  
  1203.     coord = pgrid->OrgX;
  1204.     if (Col >= pgrid->LeftCol) {
  1205.     for (i = pgrid->LeftCol; i < Col; i++)
  1206.         coord += COLWIDTH(i) + 1;
  1207.     }
  1208.     else if (Col >= pgrid->FixedCols)
  1209.     for (i = 0; i < pgrid->LeftCol; i++)
  1210.         coord -= COLWIDTH(i) + 1;
  1211.     else {
  1212.     coord = 0;
  1213.     for (i = 0; i < Col; i++)
  1214.         coord += COLWIDTH(i) + 1;
  1215.     }
  1216.     lprect->left = coord;
  1217.     lprect->right = coord + COLWIDTH(Col) + 1;
  1218. }
  1219.  
  1220.  
  1221. //---------------------------------------------------------------------------
  1222. // InvalidateCell
  1223. //---------------------------------------------------------------------------
  1224. VOID NEAR InvalidateCell
  1225. (
  1226.     PGRID   pgrid,
  1227.     HWND    hwnd,
  1228.     SHORT   Row,
  1229.     SHORT   Col
  1230. )
  1231. {
  1232.     RECT    rect;
  1233.  
  1234.     if (CellIsVisible(pgrid, Row, Col)) {
  1235.     GetCellRect(pgrid, Row, Col, &rect);
  1236.     InvalidateRect(hwnd, &rect, FALSE);
  1237.     }
  1238. }
  1239.  
  1240.  
  1241. //---------------------------------------------------------------------------
  1242. // InvalidateCol
  1243. //---------------------------------------------------------------------------
  1244. VOID NEAR InvalidateCol
  1245. (
  1246.     PGRID   pgrid,
  1247.     HWND    hwnd,
  1248.     SHORT   Col
  1249. )
  1250. {
  1251.     RECT    rect;
  1252.  
  1253.     if (CellIsVisible(pgrid, pgrid->TopRow, Col)) {
  1254.     GetCellRect(pgrid, pgrid->TopRow, Col, &rect);
  1255.     rect.top = 0;
  1256.     rect.bottom = pgrid->Height;
  1257.     InvalidateRect(hwnd, &rect, FALSE);
  1258.     }
  1259. }
  1260.  
  1261.  
  1262. //---------------------------------------------------------------------------
  1263. // InvalidateRow
  1264. //---------------------------------------------------------------------------
  1265. VOID NEAR InvalidateRow
  1266. (
  1267.     PGRID   pgrid,
  1268.     HWND    hwnd,
  1269.     SHORT   Row
  1270. )
  1271. {
  1272.     RECT    rect;
  1273.  
  1274.     if (CellIsVisible(pgrid, Row, pgrid->LeftCol)) {
  1275.     GetCellRect(pgrid, Row, pgrid->LeftCol, &rect);
  1276.     rect.left = 0;
  1277.     rect.right = pgrid->Width;
  1278.     InvalidateRect(hwnd, &rect, FALSE);
  1279.     }
  1280. }
  1281.  
  1282.  
  1283. //---------------------------------------------------------------------------
  1284. // ScrollGridVert
  1285. //---------------------------------------------------------------------------
  1286. VOID NEAR ScrollGridVert
  1287. (
  1288.     PGRID   pgrid,
  1289.     HWND    hwnd,
  1290.     USHORT  Code,
  1291.     USHORT  Val
  1292. )
  1293. {
  1294.     SHORT   Row;
  1295.     RECT    rect;
  1296.  
  1297.     switch (Code) {
  1298.     case SB_TOP:
  1299.         Row = pgrid->FixedRows;
  1300.         break;
  1301.  
  1302.     case SB_BOTTOM:
  1303.         Row = pgrid->TopRowMax;
  1304.         break;
  1305.  
  1306.     case SB_LINEUP:
  1307.         Row = pgrid->TopRow - 1;
  1308.         break;
  1309.  
  1310.     case SB_LINEDOWN:
  1311.         Row = pgrid->TopRow + 1;
  1312.         break;
  1313.  
  1314.     case SB_PAGEUP:
  1315.         Row = YToRow(pgrid, pgrid->OrgY - (pgrid->Height - pgrid->OrgY),
  1316.             FALSE);
  1317.         if (Row == pgrid->TopRow)
  1318.         Row--;
  1319.         break;
  1320.  
  1321.     case SB_PAGEDOWN:
  1322.         Row = YToRow(pgrid, pgrid->OrgY + (pgrid->Height - pgrid->OrgY),
  1323.             FALSE);
  1324.         if (Row == pgrid->TopRow)
  1325.         Row++;
  1326.         break;
  1327.  
  1328.     case SB_THUMBPOSITION:
  1329.         Row = YToRow(pgrid, Val, FALSE);
  1330.         break;
  1331.  
  1332.     case SB_ENDSCROLL:
  1333.     case SB_THUMBTRACK:
  1334.     default:
  1335.         return;
  1336.       }
  1337.  
  1338.     Row = limit(Row, pgrid->FixedRows, pgrid->TopRowMax);
  1339.     if (Row == pgrid->TopRow)
  1340.     return;
  1341.     pgrid->TopRow = Row;
  1342.     SizeGrid(pgrid, hwnd);
  1343.     GetClientRect(hwnd, &rect);
  1344.     rect.top = pgrid->OrgY;
  1345.     InvalidateRect(hwnd, &rect, FALSE);
  1346. }
  1347.  
  1348.  
  1349. //---------------------------------------------------------------------------
  1350. // ScrollGridHorz
  1351. //---------------------------------------------------------------------------
  1352. VOID NEAR ScrollGridHorz
  1353. (
  1354.     PGRID   pgrid,
  1355.     HWND    hwnd,
  1356.     USHORT  Code,
  1357.     USHORT  Val
  1358. )
  1359. {
  1360.     SHORT   Col;
  1361.     RECT    rect;
  1362.  
  1363.     switch (Code) {
  1364.     case SB_TOP:
  1365.         Col = pgrid->FixedCols;
  1366.         break;
  1367.  
  1368.     case SB_BOTTOM:
  1369.         Col = pgrid->LeftColMax;
  1370.         break;
  1371.  
  1372.     case SB_LINEUP:
  1373.         Col = pgrid->LeftCol - 1;
  1374.         break;
  1375.  
  1376.     case SB_LINEDOWN:
  1377.         Col = pgrid->LeftCol + 1;
  1378.         break;
  1379.  
  1380.     case SB_PAGEUP:
  1381.         Col = XToCol(pgrid, pgrid->OrgX - (pgrid->Width - pgrid->OrgX),
  1382.             FALSE);
  1383.         if (Col == pgrid->LeftCol)
  1384.         Col--;
  1385.         break;
  1386.  
  1387.     case SB_PAGEDOWN:
  1388.         Col = XToCol(pgrid, pgrid->OrgX + (pgrid->Width - pgrid->OrgX),
  1389.             FALSE);
  1390.         if (Col == pgrid->LeftCol)
  1391.         Col++;
  1392.         break;
  1393.  
  1394.     case SB_THUMBPOSITION:
  1395.         Col = XToCol(pgrid, Val, FALSE);
  1396.         break;
  1397.  
  1398.     case SB_ENDSCROLL:
  1399.     case SB_THUMBTRACK:
  1400.     default:
  1401.         return;
  1402.       }
  1403.  
  1404.     Col = limit(Col, pgrid->FixedCols, pgrid->LeftColMax);
  1405.     if (Col == pgrid->LeftCol)
  1406.     return;
  1407.     pgrid->LeftCol = Col;
  1408.     SizeGrid(pgrid, hwnd);
  1409.     GetClientRect(hwnd, &rect);
  1410.     rect.left = pgrid->OrgX;
  1411.     InvalidateRect(hwnd, &rect, FALSE);
  1412. }
  1413.  
  1414.  
  1415. //---------------------------------------------------------------------------
  1416. // GridScrollToView
  1417. //---------------------------------------------------------------------------
  1418. VOID NEAR GridScrollToView
  1419. (
  1420.     PGRID   pgrid,
  1421.     HWND    hwnd,
  1422.     SHORT   Row,
  1423.     SHORT   Col
  1424. )
  1425. {
  1426.     RECT    rect;
  1427.     RECT    rcCell;
  1428.  
  1429.     if (CellIsUnobstructed(pgrid, Row, Col))
  1430.     return;
  1431.     GetClientRect(hwnd, &rect);
  1432.     rect.top = pgrid->OrgY;
  1433.     rect.left = pgrid->OrgX;
  1434.     GetCellRect(pgrid, Row, Col, &rcCell);
  1435.     if (Row < pgrid->TopRow) {
  1436.     pgrid->TopRow = Row;
  1437.     rect.left = 0;
  1438.     }
  1439.     else if (Row > pgrid->WholeBottomRow) {
  1440.     do {
  1441.         pgrid->Height += ROWHEIGHT(pgrid->TopRow++);
  1442.         } while (pgrid->Height < rcCell.bottom
  1443.             && Row > pgrid->TopRow);
  1444.     rect.left = 0;
  1445.     }
  1446.     if (Col < pgrid->LeftCol) {
  1447.     pgrid->LeftCol = Col;
  1448.     rect.top = 0;
  1449.     }
  1450.     else if (Col > pgrid->WholeRightCol) {
  1451.     do {
  1452.         pgrid->Width += COLWIDTH(pgrid->LeftCol++);
  1453.         } while (pgrid->Width < rcCell.right
  1454.             && Col > pgrid->LeftCol);
  1455.     rect.top = 0;
  1456.     }
  1457.     SizeGrid(pgrid, hwnd);
  1458.     InvalidateRect(hwnd, &rect, FALSE);
  1459. }
  1460.  
  1461.  
  1462. //---------------------------------------------------------------------------
  1463. // InvalidateSelection
  1464. //---------------------------------------------------------------------------
  1465. VOID NEAR InvalidateSelection
  1466. (
  1467.     PGRID   pgrid,
  1468.     HWND    hwnd
  1469. )
  1470. {
  1471.     RECT    rcSel;
  1472.     RECT    rcCell;
  1473.  
  1474.     if (!pgrid->fSelection)
  1475.     return;
  1476.  
  1477.     GetCellRect(pgrid, pgrid->SelTopRow, pgrid->SelLeftCol, &rcSel);
  1478.     if (pgrid->SelBottomRow > pgrid->SelTopRow
  1479.         || pgrid->SelRightCol > pgrid->SelLeftCol) {
  1480.     GetCellRect(pgrid, pgrid->SelBottomRow, pgrid->SelRightCol, &rcCell);
  1481.     UnionRect(&rcSel, &rcSel, &rcCell);
  1482.     }
  1483.     InvalidateRect(hwnd, &rcSel, FALSE);
  1484. }
  1485.  
  1486.  
  1487. //---------------------------------------------------------------------------
  1488. // ClearGridSel
  1489. //---------------------------------------------------------------------------
  1490. ERR NEAR ClearGridSel
  1491. (
  1492.     HCTL    hctl,
  1493.     HWND    hwnd
  1494. )
  1495. {
  1496.     PGRID   pgrid = GRIDDEREF(hctl);
  1497.  
  1498.     if (!pgrid->fSelection)
  1499.     return 0;
  1500.     if (hwnd)
  1501.     InvalidateSelection(pgrid, hwnd);
  1502.     pgrid->SelTopRow    = 0;
  1503.     pgrid->SelBottomRow = 0;
  1504.     pgrid->SelLeftCol    = 0;
  1505.     pgrid->SelRightCol    = 0;
  1506.     pgrid->fSelection = FALSE;
  1507.     return VBFireEvent(hctl, EVENT_GRID_SELCHANGE, NULL);
  1508. }
  1509.  
  1510.  
  1511. //---------------------------------------------------------------------------
  1512. // SetGridSel
  1513. //---------------------------------------------------------------------------
  1514. ERR NEAR SetGridSel
  1515. (
  1516.     HCTL    hctl,
  1517.     HWND    hwnd,
  1518.     BOOL    fSwap,
  1519.     SHORT   StartRow,
  1520.     SHORT   StartCol,
  1521.     SHORT   EndRow,
  1522.     SHORT   EndCol
  1523. )
  1524. {
  1525.     PGRID   pgrid = GRIDDEREF(hctl);
  1526.     SHORT   Swap;
  1527.     BOOL    fSel = TRUE;
  1528.  
  1529.     if (StartRow > EndRow) {
  1530.     if (fSwap) {
  1531.         Swap = StartRow;
  1532.         StartRow = EndRow;
  1533.         EndRow = Swap;
  1534.         }
  1535.     else
  1536.         fSel = FALSE;
  1537.     }
  1538.     if (StartCol > EndCol) {
  1539.     if (fSwap) {
  1540.         Swap = StartCol;
  1541.         StartCol = EndCol;
  1542.         EndCol = Swap;
  1543.         }
  1544.     else
  1545.         fSel = FALSE;
  1546.     }
  1547.  
  1548.     if ((BOOL)pgrid->fSelection == fSel
  1549.         && pgrid->SelTopRow    == StartRow
  1550.         && pgrid->SelBottomRow == EndRow
  1551.         && pgrid->SelLeftCol   == StartCol
  1552.         && pgrid->SelRightCol  == EndCol)
  1553.     return 0;
  1554.  
  1555.     if (hwnd)
  1556.     InvalidateSelection(pgrid, hwnd);
  1557.     pgrid->SelTopRow    = StartRow;
  1558.     pgrid->SelBottomRow = EndRow;
  1559.     pgrid->SelLeftCol    = StartCol;
  1560.     pgrid->SelRightCol    = EndCol;
  1561.     pgrid->fSelection    = fSel;
  1562.     if (hwnd)
  1563.     InvalidateSelection(pgrid, hwnd);
  1564.     return VBFireEvent(hctl, EVENT_GRID_SELCHANGE, NULL);
  1565. }
  1566.  
  1567. #define HIT_NONE    0
  1568. #define HIT_VERT    1
  1569. #define HIT_HORZ    2
  1570.  
  1571. //---------------------------------------------------------------------------
  1572. // GridMouseHit
  1573. //---------------------------------------------------------------------------
  1574. LONG NEAR GridMouseHit
  1575. (
  1576.     PGRID   pgrid,
  1577.     SHORT   x,
  1578.     SHORT   y
  1579. )
  1580. {
  1581.     SHORT   coord;
  1582.     SHORT   i;
  1583.  
  1584.     if (x >= pgrid->OrgX && y >= pgrid->OrgY)
  1585.     return HIT_NONE;
  1586.  
  1587.     if (y < pgrid->OrgY) {
  1588.     coord = 0;
  1589.     for (i = 0; i < pgrid->Cols; i++) {
  1590.         if (i == pgrid->FixedCols)
  1591.         i = pgrid->LeftCol;
  1592.         coord += COLWIDTH(i) + 1;
  1593.         if (coord > pgrid->Width)
  1594.         break;
  1595.         if (x >= coord-3 && x <= coord+1)
  1596.         return MAKELONG(HIT_VERT, i);
  1597.         if (coord > x)
  1598.         break;
  1599.         }
  1600.     }
  1601.  
  1602.     if (x < pgrid->OrgX) {
  1603.     coord = 0;
  1604.     for (i = 0; i < pgrid->Rows; i++) {
  1605.         if (i == pgrid->FixedRows)
  1606.         i = pgrid->TopRow;
  1607.         coord += ROWHEIGHT(i) + 1;
  1608.         if (coord > pgrid->Height)
  1609.         break;
  1610.         if (y >= coord-3 && y <= coord+1) {
  1611.         return MAKELONG(HIT_HORZ, i);
  1612.         }
  1613.         if (coord > y)
  1614.         break;
  1615.         }
  1616.     }
  1617.     return HIT_NONE;
  1618. }
  1619.  
  1620.  
  1621. #define SEL_RNG     0
  1622. #define SEL_ROWS    1
  1623. #define SEL_COLS    2
  1624.  
  1625. USHORT    OldCaretRate;
  1626.  
  1627. LONG    Hit;
  1628. SHORT    HitCoord;
  1629.  
  1630. //---------------------------------------------------------------------------
  1631. // GridMouseDown
  1632. //---------------------------------------------------------------------------
  1633. VOID NEAR GridMouseDown
  1634. (
  1635.     HCTL    hctl,
  1636.     HWND    hwnd,
  1637.     SHORT   x,
  1638.     SHORT   y
  1639. )
  1640. {
  1641.     SHORT   Row;
  1642.     SHORT   Col;
  1643.     SHORT   StartRow;
  1644.     SHORT   EndRow;
  1645.     SHORT   StartCol;
  1646.     SHORT   EndCol;
  1647.     BOOL    fShift;
  1648.     PGRID   pgrid = GRIDDEREF(hctl);
  1649.  
  1650.     SetFocus(hwnd);
  1651.     if (y > pgrid->Height || x > pgrid->Width)
  1652.     return;
  1653.     SetCapture(hwnd);
  1654.     pgrid->fMouseCapture = TRUE;
  1655.  
  1656.     Hit = GridMouseHit(pgrid, x, y);
  1657.     if (Hit) {
  1658.     RECT    rcCell;
  1659.     RECT    rcClip;
  1660.  
  1661.     GetClientRect(hwnd, &rcClip);
  1662.     if (LOWORD(Hit) == HIT_VERT) {
  1663.         GetCellRect(pgrid, 0, HIWORD(Hit), &rcCell);
  1664.         CreateCaret(hwnd, NULL, 2, pgrid->Height);
  1665.         SetCaretPos(x, 0);
  1666.         rcClip.left = HitCoord = rcCell.left;
  1667.         }
  1668.     else {
  1669.         GetCellRect(pgrid, HIWORD(Hit), 0, &rcCell);
  1670.         CreateCaret(hwnd, NULL, pgrid->Width, 2);
  1671.         SetCaretPos(0, y);
  1672.         rcClip.top = HitCoord = rcCell.top;
  1673.         }
  1674.     OldCaretRate = GetCaretBlinkTime();
  1675.     SetCaretBlinkTime(0xFFFF);
  1676.     ShowCaret(hwnd);
  1677.     ClientToScreen(hwnd, (LPPOINT)&rcClip.left);
  1678.     ClientToScreen(hwnd, (LPPOINT)&rcClip.right);
  1679.     ClipCursor(&rcClip);
  1680.     return;
  1681.     }
  1682.  
  1683.     fShift = GetAsyncKeyState(VK_SHIFT);
  1684.  
  1685.     Row = YToRow(pgrid, y, TRUE);
  1686.     Col = XToCol(pgrid, x, TRUE);
  1687.     StartRow = fShift ? pgrid->Row : Row;
  1688.     EndRow = Row;
  1689.     StartCol = fShift ? pgrid->Col : Col;
  1690.     EndCol = Col;
  1691.     pgrid->SelType = SEL_RNG;
  1692.     if (Row < pgrid->FixedRows) {
  1693.     pgrid->SelType |= SEL_COLS;
  1694.     Row = pgrid->TopRow;
  1695.     StartRow = pgrid->FixedRows;
  1696.     EndRow = pgrid->Rows - 1;
  1697.     }
  1698.     if (Col < pgrid->FixedCols) {
  1699.     pgrid->SelType |= SEL_ROWS;
  1700.     Col = pgrid->LeftCol;
  1701.     StartCol = pgrid->FixedCols;
  1702.     EndCol = pgrid->Cols - 1;
  1703.     }
  1704.     pgrid->KeySelRow = Row;
  1705.     pgrid->KeySelCol = Col;
  1706.     if (!fShift)
  1707.     if (SetGridRowCol(hctl, pgrid, hwnd, Row, Col))
  1708.         return;
  1709.     SetGridSel(hctl, hwnd, TRUE, StartRow, StartCol, EndRow, EndCol);
  1710. }
  1711.  
  1712.  
  1713. //---------------------------------------------------------------------------
  1714. // GridMouseDblClick
  1715. //---------------------------------------------------------------------------
  1716. VOID NEAR GridMouseDblClick
  1717. (
  1718.     HCTL    hctl,
  1719.     HWND    hwnd,
  1720.     SHORT   x,
  1721.     SHORT   y
  1722. )
  1723. {
  1724.     PGRID   pgrid = GRIDDEREF(hctl);
  1725.  
  1726.     hwnd = hwnd;
  1727.     if (y > pgrid->Height || x > pgrid->Width)
  1728.     return;
  1729.     VBFireEvent(hctl, EVENT_GRID_DBLCLICK, NULL);
  1730. }
  1731.  
  1732.  
  1733. //---------------------------------------------------------------------------
  1734. // GridMouseMove
  1735. //---------------------------------------------------------------------------
  1736. VOID NEAR GridMouseMove
  1737. (
  1738.     HCTL    hctl,
  1739.     HWND    hwnd,
  1740.     SHORT   x,
  1741.     SHORT   y
  1742. )
  1743. {
  1744.     SHORT   StartRow;
  1745.     SHORT   EndRow;
  1746.     SHORT   StartCol;
  1747.     SHORT   EndCol;
  1748.     PGRID   pgrid = GRIDDEREF(hctl);
  1749.  
  1750.     if (!pgrid->fMouseCapture) {
  1751.     LONG    Hit;
  1752.  
  1753.     Hit = GridMouseHit(pgrid, x, y);
  1754.     if (!Hit) {
  1755.         SetCursor(hcurArrow);
  1756.         return;
  1757.         }
  1758.     if (LOWORD(Hit) == HIT_VERT)
  1759.         SetCursor(hcurVSep);
  1760.     else
  1761.         SetCursor(hcurHSep);
  1762.     return;
  1763.     }
  1764.  
  1765.     if (LOWORD(Hit) == HIT_VERT) {
  1766.     SetCursor(hcurVSep);
  1767.     SetCaretPos(x, 0);
  1768.     return;
  1769.     }
  1770.     if (LOWORD(Hit) == HIT_HORZ) {
  1771.     SetCursor(hcurHSep);
  1772.     SetCaretPos(0, y);
  1773.     return;
  1774.     }
  1775.  
  1776.     StartRow = pgrid->Row;
  1777.     EndRow   = YToRow(pgrid, y, FALSE);
  1778.     StartCol = pgrid->Col;
  1779.     EndCol   = XToCol(pgrid, x, FALSE);
  1780.     GridScrollToView(pgrid, hwnd, EndRow, EndCol);
  1781.     pgrid->KeySelRow = EndRow;
  1782.     pgrid->KeySelCol = EndCol;
  1783.  
  1784.     if (pgrid->SelType & SEL_ROWS) {
  1785.     StartCol = pgrid->FixedCols;
  1786.     EndCol = pgrid->Cols-1;
  1787.     }
  1788.     if (pgrid->SelType & SEL_COLS) {
  1789.     StartRow = pgrid->FixedRows;
  1790.     EndRow = pgrid->Rows-1;
  1791.     }
  1792.     SetGridSel(hctl, hwnd, TRUE, StartRow, StartCol, EndRow, EndCol);
  1793. }
  1794.  
  1795.  
  1796. //---------------------------------------------------------------------------
  1797. // GridMouseUp
  1798. //---------------------------------------------------------------------------
  1799. VOID NEAR GridMouseUp
  1800. (
  1801.     HCTL    hctl,
  1802.     HWND    hwnd,
  1803.     SHORT   x,
  1804.     SHORT   y
  1805. )
  1806. {
  1807.     PGRID   pgrid = GRIDDEREF(hctl);
  1808.  
  1809.     if (!pgrid->fMouseCapture)
  1810.     return;
  1811.  
  1812.     pgrid->fMouseCapture = FALSE;
  1813.     ReleaseCapture();
  1814.  
  1815.     if (Hit) {
  1816.     BOOL    fChange;
  1817.  
  1818.     ClipCursor(NULL);
  1819.     SetCaretBlinkTime(OldCaretRate);
  1820.     HideCaret(hwnd);
  1821.     if (LOWORD(Hit) == HIT_VERT) {
  1822.         fChange =  COLWIDTH(HIWORD(Hit)) != x - HitCoord;
  1823.         COLWIDTH(HIWORD(Hit)) = x - HitCoord;
  1824.         }
  1825.     else {
  1826.         fChange = ROWHEIGHT(HIWORD(Hit)) != y - HitCoord;
  1827.         ROWHEIGHT(HIWORD(Hit)) = y - HitCoord;
  1828.         }
  1829.     Hit = 0L;
  1830.     if (fChange) {
  1831.         SizeGrid(pgrid, hwnd);
  1832.         InvalidateRect(hwnd, NULL, FALSE);
  1833.         }
  1834.     }
  1835. }
  1836.  
  1837.  
  1838. //---------------------------------------------------------------------------
  1839. // GridKeyDown
  1840. //---------------------------------------------------------------------------
  1841. VOID NEAR GridKeyDown
  1842. (
  1843.     HCTL    hctl,
  1844.     HWND    hwnd,
  1845.     USHORT  VKKey
  1846. )
  1847. {
  1848.     RECT    rect;
  1849.     SHORT   Temp;
  1850.     SHORT   Row;
  1851.     SHORT   Col;
  1852.     BOOL    fShift;
  1853.     BOOL    fCtrl;
  1854.     PGRID   pgrid = GRIDDEREF(hctl);
  1855.  
  1856.     if (pgrid->fMouseCapture)
  1857.     return;
  1858.  
  1859.     pgrid = GRIDDEREF(hctl);
  1860.  
  1861.  
  1862.     fShift = GetAsyncKeyState(VK_SHIFT);
  1863.     fCtrl  = GetAsyncKeyState(VK_CONTROL);
  1864.  
  1865.     if (fShift && pgrid->fSelection && VKKey != VK_RETURN) {
  1866.     Row = pgrid->KeySelRow;
  1867.     Col = pgrid->KeySelCol;
  1868.     }
  1869.     else {
  1870.     pgrid->SelType = SEL_RNG;
  1871.     Row = pgrid->Row;
  1872.     Col = pgrid->Col;
  1873.     }
  1874.  
  1875.     switch (VKKey) {
  1876.     case VK_RETURN:
  1877.         if (!pgrid->fSelection)
  1878.         return;
  1879.         do {
  1880.         if (fShift) {
  1881.             if (Col-- < pgrid->FixedCols) {
  1882.             Col = pgrid->Cols-1;
  1883.             Row--;
  1884.             if (Row < pgrid->FixedRows)
  1885.                 Row = pgrid->Rows-1;
  1886.             }
  1887.             }
  1888.         else {
  1889.             if (Col++ >= pgrid->Cols) {
  1890.             Col = pgrid->FixedCols;
  1891.             Row++;
  1892.             if (Row >= pgrid->Rows)
  1893.                 Row = pgrid->FixedRows;
  1894.             }
  1895.             }
  1896.         } while (!GridCellSelected(pgrid, Row, Col));
  1897.         GridScrollToView(pgrid, hwnd, Row, Col);
  1898.         SetGridRowCol(hctl, pgrid, hwnd, Row, Col);
  1899.         return;
  1900.  
  1901.     case VK_PRIOR:
  1902.         GetCellRect(pgrid, Row, Col, &rect);
  1903.         if (fCtrl) {
  1904.         Temp = XToCol(pgrid, rect.left - (pgrid->Width - pgrid->OrgX),
  1905.                 FALSE);
  1906.         if (Temp == Col)
  1907.             Col--;
  1908.         else
  1909.             Col = Temp;
  1910.         }
  1911.         else {
  1912.         Temp = YToRow(pgrid, rect.top - (pgrid->Height - pgrid->OrgY),
  1913.                 FALSE);
  1914.         if (Temp == Row)
  1915.             Row--;
  1916.         else
  1917.             Row = Temp;
  1918.         }
  1919.         break;
  1920.  
  1921.     case VK_NEXT:
  1922.         GetCellRect(pgrid, Row, Col, &rect);
  1923.         if (fCtrl) {
  1924.         Temp = XToCol(pgrid, rect.left + (pgrid->Width - pgrid->OrgX),
  1925.                 FALSE);
  1926.         if (Temp == Col)
  1927.             Col++;
  1928.         else
  1929.             Col = Temp;
  1930.         }
  1931.         else {
  1932.         Temp = YToRow(pgrid, rect.top + (pgrid->Height - pgrid->OrgY),
  1933.                 FALSE);
  1934.         if (Temp == Row)
  1935.             Row++;
  1936.         else
  1937.             Row = Temp;
  1938.         }
  1939.         break;
  1940.  
  1941.     case VK_END:
  1942.         if (fCtrl)
  1943.         Row = pgrid->Rows-1;
  1944.         Col = pgrid->Cols-1;
  1945.         break;
  1946.  
  1947.     case VK_HOME:
  1948.         if (fCtrl)
  1949.         Row = pgrid->FixedRows;
  1950.         Col = pgrid->FixedCols;
  1951.         break;
  1952.  
  1953.     case VK_LEFT:
  1954.         Col--;
  1955.         if (fCtrl)
  1956.         while (Col > pgrid->FixedCols && !GetCellText(pgrid, Row, Col))
  1957.             Col--;
  1958.         break;
  1959.  
  1960.     case VK_UP:
  1961.         Row--;
  1962.         if (fCtrl)
  1963.         while (Row > pgrid->FixedRows && !GetCellText(pgrid, Row, Col))
  1964.             Row--;
  1965.         break;
  1966.  
  1967.     case VK_RIGHT:
  1968.         Col++;
  1969.         if (fCtrl)
  1970.         while (Col < pgrid->Cols-1 && !GetCellText(pgrid, Row, Col))
  1971.             Col++;
  1972.         break;
  1973.  
  1974.     case VK_DOWN:
  1975.         Row++;
  1976.         if (fCtrl)
  1977.         while (Row < pgrid->Rows-1 && !GetCellText(pgrid, Row, Col))
  1978.             Row++;
  1979.         break;
  1980.  
  1981.     case VK_SPACE:
  1982.         if (fShift)
  1983.         pgrid->SelType |= SEL_COLS;
  1984.         if (fCtrl)
  1985.         pgrid->SelType |= SEL_ROWS;
  1986.         // force selection
  1987.         fShift |= fCtrl;
  1988.         break;
  1989.  
  1990.     default:
  1991.         return;
  1992.     }
  1993.  
  1994.     Row = limit(Row, pgrid->FixedRows, pgrid->Rows-1);
  1995.     Col = limit(Col, pgrid->FixedCols, pgrid->Cols-1);
  1996.     pgrid->KeySelRow = Row;
  1997.     pgrid->KeySelCol = Col;
  1998.     GridScrollToView(pgrid, hwnd, Row, Col);
  1999.     if (!fShift) {
  2000.     if (!SetGridRowCol(hctl, pgrid, hwnd, Row, Col))
  2001.         SetGridSel(hctl, hwnd, TRUE, Row, Col, Row, Col);
  2002.     }
  2003.     else {
  2004.     SHORT    StartRow = pgrid->Row;
  2005.     SHORT    StartCol = pgrid->Col;
  2006.  
  2007.     if (pgrid->SelType & SEL_ROWS) {
  2008.         StartCol = pgrid->FixedCols;
  2009.         Col = pgrid->Cols-1;
  2010.         }
  2011.     if (pgrid->SelType & SEL_COLS) {
  2012.         StartRow = pgrid->FixedRows;
  2013.         Row = pgrid->Rows-1;
  2014.         }
  2015.     SetGridSel(hctl, hwnd, TRUE, StartRow, StartCol, Row, Col);
  2016.     }
  2017. }
  2018.  
  2019.  
  2020. //---------------------------------------------------------------------------
  2021. // GridKeyUp
  2022. //---------------------------------------------------------------------------
  2023. VOID NEAR GridKeyUp
  2024. (
  2025.     HCTL    hctl,
  2026.     HWND    hwnd,
  2027.     USHORT  VKKey
  2028. )
  2029. {
  2030.     hctl = hctl;
  2031.     hwnd = hwnd;
  2032.     VKKey = VKKey;
  2033. }
  2034.  
  2035.  
  2036. //---------------------------------------------------------------------------
  2037. // NewFont
  2038. //---------------------------------------------------------------------------
  2039. VOID NEAR NewFont
  2040. (
  2041.     PGRID   pgrid,
  2042.     HWND    hwnd
  2043. )
  2044. {
  2045.     SHORT   i;
  2046.     TEXTMETRIC tm;
  2047.     HDC     hdc = CreateIC("Display", NULL, NULL, NULL);
  2048.  
  2049.     if (pgrid->hFont)
  2050.     SelectObject(hdc, pgrid->hFont);
  2051.     GetTextMetrics(hdc, &tm);
  2052.     DeleteDC(hdc);
  2053.     pgrid->DefHeight = tm.tmHeight + 2;
  2054.     pgrid->DefWidth = pgrid->DefHeight * 5 / 2;
  2055.  
  2056.     // resize the defaulted rows and columns if initialized
  2057.     if (pgrid->Rows && pgrid->Cols) {
  2058.     for (i = 0; i < pgrid->Rows; i++)
  2059.         if (ROW(i)->fDefHeight)
  2060.         ROWHEIGHT(i) = pgrid->DefHeight;
  2061.     for (i = 0; i < pgrid->Cols; i++)
  2062.         if (COL(i).fDefWidth)
  2063.         COLWIDTH(i) = pgrid->DefWidth;
  2064.  
  2065.     SizeGrid(pgrid, hwnd);
  2066.     InvalidateRect(hwnd, NULL, FALSE);
  2067.     }
  2068. }
  2069.  
  2070.  
  2071. //---------------------------------------------------------------------------
  2072. // GetClipText
  2073. //---------------------------------------------------------------------------
  2074. HSZ NEAR GetClipText
  2075. (
  2076.     PGRID   pgrid
  2077. )
  2078. {
  2079.     HANDLE  hseg;
  2080.     _segment segClip;
  2081.     CHAR    _based(segClip) * _based(segClip) * hClip;
  2082.     SHORT   Row;
  2083.     SHORT   Col;
  2084.     SHORT   cb;
  2085.     SHORT   ClipLen;
  2086.     SHORT   CellLen;
  2087.     BHSTR   bhStr;
  2088.  
  2089.     segClip = pgrid->segClip;
  2090.     if (segClip == NULL) {
  2091.     hseg = GlobalAlloc(GHND, 1000);
  2092.     if (!hseg)
  2093.         return NULL;
  2094.     pgrid->segClip = segClip = (_segment)GlobalLock(hseg);
  2095.     LocalInit(segClip, 16, (USHORT)GlobalSize(hseg));
  2096.     }
  2097.  
  2098.     _asm
  2099.     {
  2100.     push    ds
  2101.     mov    ds,segClip
  2102.     }
  2103.     hClip = (CHAR _based(segClip) * _based(segClip) *)
  2104.         LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, cb = 500);
  2105.     _asm
  2106.     {
  2107.     pop    ds
  2108.     }
  2109.     if (!hClip)
  2110.     return NULL;
  2111.  
  2112.     ClipLen = 0;
  2113.  
  2114.     for (Row = pgrid->SelTopRow; Row <= pgrid->SelBottomRow; ++Row) {
  2115.     for (Col = pgrid->SelLeftCol; Col <= pgrid->SelRightCol; ++Col) {
  2116.         bhStr = GetCellText(pgrid, Row, Col);
  2117.         CellLen = bhStr ? lstrlen((LPSTR)*bhStr) : 0;
  2118.         if (ClipLen+CellLen+2 > cb) {
  2119.         _asm
  2120.         {
  2121.             push    ds
  2122.             mov     ds,segClip
  2123.         }
  2124.         hClip = (CHAR _based(segClip) * _based(segClip) *)
  2125.             LocalReAlloc((HANDLE)hClip, cb += CellLen+2000,
  2126.             LMEM_MOVEABLE | LMEM_ZEROINIT);
  2127.         _asm
  2128.         {
  2129.             pop     ds
  2130.         }
  2131.         if (!hClip) {
  2132.             GlobalFree(LOWORD(GlobalHandle(pgrid->segClip)));
  2133.             pgrid->segClip = NULL;
  2134.             return NULL;
  2135.             }
  2136.         }
  2137.         if (CellLen){
  2138.         lstrcpy((LPSTR)(*hClip)+ClipLen, (LPSTR)*bhStr);
  2139.         ClipLen += CellLen;
  2140.         }
  2141.         if (Col < pgrid->SelRightCol) {
  2142.         *((LPSTR)(*hClip)+ClipLen) = '\t';
  2143.         ++ClipLen;
  2144.         }
  2145.         }
  2146.     if (Row < pgrid->SelBottomRow) {
  2147.         *((LPSTR)(*hClip)+ClipLen) = '\r';
  2148.         ++ClipLen;
  2149.         }
  2150.     }
  2151.  
  2152.     return (HSZ)MAKELONG(hClip, segClip);
  2153. }
  2154.  
  2155.  
  2156. //---------------------------------------------------------------------------
  2157. // SetClipText
  2158. //---------------------------------------------------------------------------
  2159. ERR NEAR SetClipText
  2160. (
  2161.     HCTL    hctl,
  2162.     HWND    hwnd,
  2163.     LPSTR   pszText
  2164. )
  2165. {
  2166.     SHORT   Row;
  2167.     SHORT   Col;
  2168.     LPSTR   pszScan;
  2169.     CHAR    chSave;
  2170.     ERR     err = 0;
  2171.     PGRID   pgrid = GRIDDEREF(hctl);
  2172.  
  2173.     // start at the upper left of the selection region
  2174.     Row = pgrid->SelTopRow;
  2175.     Col = pgrid->SelLeftCol;
  2176.     pszScan = pszText;
  2177.     for (;;) {
  2178.     // look for terminator to cell string
  2179.     while (*pszScan && *pszScan != '\t' && *pszScan != '\r')
  2180.         ++pszScan;
  2181.     // save it, and change to NULL
  2182.     chSave = *pszScan;
  2183.     *pszScan = 0;
  2184.     // set text for this cell and advance to next
  2185.     if (Col <= pgrid->SelRightCol)
  2186.         err = SetCellText(hctl, hwnd, Row, Col++, pszText);
  2187.     // restore original terminator
  2188.     *pszScan = chSave;
  2189.     if (err)
  2190.         // exit if unable to set a cell
  2191.         return err;
  2192.     if (chSave == '\r' || chSave == 0) {
  2193.         if (chSave == '\r' && *(pszScan+1) == '\n')
  2194.         ++pszScan;
  2195.         // end of the line (row) for supplied text
  2196.         while (Col <= pgrid->SelRightCol) {
  2197.         // clear any remaining selected cols in the row
  2198.         SetCellText(hctl, hwnd, Row, Col, NULL);
  2199.         ++Col;
  2200.         }
  2201.         // continue with start of next row
  2202.         Col = pgrid->SelLeftCol;
  2203.         if (Row == pgrid->SelBottomRow)
  2204.         // finished with last row, so exit
  2205.         return 0;
  2206.         ++Row;
  2207.         }
  2208.     // advance to next cell string
  2209.     if (chSave)
  2210.         ++pszScan;
  2211.     pszText = pszScan;
  2212.     }
  2213. }
  2214.  
  2215. //---------------------------------------------------------------------------
  2216.