home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / sdktools / windiff / table.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  31KB  |  927 lines

  1.  
  2. /******************************************************************************\
  3. *       This is a part of the Microsoft Source Code Samples. 
  4. *       Copyright (C) 1993-1997 Microsoft Corporation.
  5. *       All rights reserved. 
  6. *       This source code is only intended as a supplement to 
  7. *       Microsoft Development Tools and/or WinHelp documentation.
  8. *       See these sources for detailed information regarding the 
  9. *       Microsoft samples programs.
  10. \******************************************************************************/
  11.  
  12. /****************************** Module Header *******************************
  13. * Module Name: TABLE.C
  14. *
  15. * Standard table class and main interface functions.
  16. *
  17. * Functions:
  18. *
  19. * gtab_init()
  20. * gtab_deltools()
  21. * gtab_sendtq()
  22. * gtab_freelinedata()
  23. * gtab_wndproc()
  24. * gtab_createtools()
  25. * gtab_deltable()
  26. * gtab_buildtable()
  27. * gtab_setsize()
  28. * gtab_newsize()
  29. * gtab_calcwidths()
  30. * gtab_alloclinedata()
  31. * gtab_invallines()
  32. * gtab_append()
  33. *
  34. * Comments:
  35. *
  36. * The table class communicates with its 'owner' window to
  37. * get the layout info and the data to display. The owner window handle
  38. * can be sent as the lParam in CreateWindow - if not, the parent window will
  39. * be used.
  40. *
  41. * After creating the window, send it a TM_NEWID message, with a 'data id'
  42. * as the lParam. This is any non-zero 32-bit value. The table will then call
  43. * back to its owner window to find out how many rows/columns, then to fetch
  44. * the name/properties of each column, and finally to get the data to display.
  45. *
  46. * Send TM_NEWID of 0 to close (or destroy the window) - wait for TQ_CLOSE
  47. * (in either case) before discarding data. Send
  48. * TM_REFRESH if data or row-count changes; send TM_NEWLAYOUT if column
  49. * properties or nr cols change etc - this is the same as sending TM_NEWID
  50. * except that no TQ_CLOSE happens on TM_NEWLAYOUT.
  51. *
  52. * TQ_SELECT is sent whenever the current selection changes. TQ_ENTER is sent
  53. * when enter or double-click occurs.
  54. *
  55. ****************************************************************************/
  56.  
  57. #include <windows.h>
  58. #include <commdlg.h>
  59.  
  60. #include "gutils.h"
  61. #include "table.h"
  62. #include "tpriv.h"
  63.  
  64. /* global tools etc */
  65. extern HANDLE hLibInst;
  66. HANDLE hVertCurs;
  67. HANDLE hNormCurs;
  68. HPEN hpenDotted;
  69. UINT gtab_msgcode;
  70.  
  71. /* function prototypes */
  72. long FAR PASCAL gtab_wndproc(HWND, UINT, UINT, long);
  73. void gtab_createtools(void);
  74. void gtab_deltable(HWND hwnd, lpTable ptab);
  75. lpTable gtab_buildtable(HWND hwnd, DWORD id);
  76. void gtab_setsize(HWND hwnd, lpTable ptab);
  77. void gtab_newsize(HWND hwnd, lpTable ptab);
  78. void gtab_calcwidths(HWND hwnd, lpTable ptab);
  79. BOOL gtab_alloclinedata(HWND hwnd, HANDLE heap, lpTable ptab);
  80. void gtab_invallines(HWND hwnd, lpTable ptab, int start, int count);
  81. void gtab_append(HWND hwnd, lpTable ptab, int rows, DWORD id);
  82.  
  83. /***************************************************************************
  84.  * Function: gtab_init
  85.  *
  86.  * Purpose:
  87.  *
  88.  * Initialise window class - called from DLL main init
  89.  */
  90. void
  91. gtab_init(void)
  92. {
  93.         WNDCLASS wc;
  94.  
  95.         gtab_createtools();
  96.         gtab_msgcode = RegisterWindowMessage(TableMessage);
  97.  
  98.         wc.style = CS_GLOBALCLASS | CS_DBLCLKS;
  99.         wc.lpfnWndProc = gtab_wndproc;
  100.         wc.cbClsExtra = 0;
  101.         wc.cbWndExtra = WLTOTAL;
  102.         wc.hInstance = hLibInst;
  103.         wc.hIcon = NULL;
  104.         wc.hCursor = NULL;
  105.         wc.hbrBackground = GetStockObject(WHITE_BRUSH);
  106.         wc.lpszClassName = TableClassName;
  107.         wc.lpszMenuName = NULL;
  108.  
  109.         RegisterClass(&wc);
  110. }
  111.  
  112. /***************************************************************************
  113.  * Function: gtab_createtools
  114.  *
  115.  * Purpose:
  116.  *
  117.  * Load cursors and pens.
  118.  */
  119.  void
  120. gtab_createtools(void)
  121. {
  122.         hVertCurs = LoadCursor(hLibInst, "VertLine");
  123.         hNormCurs = LoadCursor(NULL, IDC_ARROW);
  124.  
  125.         hpenDotted = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
  126. }
  127.  
  128. /***************************************************************************
  129.  * Function: gtab_deltools
  130.  *
  131.  * Purpose:
  132.  *
  133.  * Delete pen
  134.  */
  135.  void
  136. gtab_deltools(void)
  137. {
  138.         DeleteObject(hpenDotted);
  139. }
  140.  
  141.  
  142. /***************************************************************************
  143.  * Function: gtab_wndproc
  144.  *
  145.  * Purpose:
  146.  *
  147.  * Window procedure for table
  148.  */
  149.  long FAR PASCAL
  150. gtab_wndproc(HWND hwnd, UINT msg, UINT wParam, long lParam)
  151. {
  152.         CREATESTRUCT FAR * csp;
  153.         HWND hOwner;
  154.         lpTable ptab;
  155.         HANDLE hHeap;
  156.         PAINTSTRUCT ps;
  157.         int y, y2, i;
  158.         HDC hDC;
  159.         lpTableSelection pselect;
  160.         long oldtop;
  161.         long change;
  162.  
  163.         switch(msg) {
  164.  
  165.         case WM_CREATE:
  166.                 /* create window. set the wnd extra bytes to
  167.                  * contain the owner window, a heap and a null table.
  168.                  * Owner window is either in lParam or the parent.
  169.                  * Then wait for TM_NEWID.
  170.                  */
  171.                 csp = (CREATESTRUCT FAR *) lParam;
  172.                 if (csp->lpCreateParams == NULL) {
  173.                         hOwner = GetParent(hwnd);
  174.                 } else {
  175.                         hOwner = (HWND) (long) csp->lpCreateParams;
  176.                 }
  177.                 ptab = NULL;
  178.                 hHeap = gmem_init();
  179.                 SetWindowLong(hwnd, WL_TABLE, (LONG) ptab);
  180.                 SetWindowLong(hwnd, WW_OWNER, (LONG) hOwner);
  181.                 SetWindowLong(hwnd, WW_HEAP, (LONG) hHeap);
  182.  
  183.                 SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  184.                 SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  185.                 break;
  186.  
  187.         case TM_NEWID:
  188.                 /* complete change of table.
  189.                  * close old table, discard memory and
  190.                  * build new table
  191.                  */
  192.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  193.                 if (ptab != NULL) {
  194.                         gtab_sendtq(hwnd, TQ_CLOSE, ptab->hdr.id);
  195.                         gtab_deltable(hwnd, ptab);
  196.                         SetCursor(hNormCurs);
  197.                         SetWindowLong(hwnd, WL_TABLE, 0);
  198.                 }
  199.                 if ( (ptab = gtab_buildtable(hwnd, lParam)) != NULL) {
  200.                         SetWindowLong(hwnd, WL_TABLE, (long) (LPSTR) ptab);
  201.                         gtab_setsize(hwnd, ptab);
  202.                 } else {
  203.                         SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  204.                         SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  205.                 }
  206.                 InvalidateRect(hwnd, NULL, TRUE);
  207.                 break;
  208.  
  209.         case TM_NEWLAYOUT:
  210.                 /* change of layout but for same id. no TQ_CLOSE,
  211.                  * but otherwise same as TM_NEWID
  212.                  */
  213.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  214.                 if (ptab != NULL) {
  215.                         gtab_deltable(hwnd, ptab);
  216.                         SetCursor(hNormCurs);
  217.                         SetWindowLong(hwnd, WL_TABLE, 0);
  218.                 }
  219.                 if ( (ptab = gtab_buildtable(hwnd, lParam)) != NULL) {
  220.                         SetWindowLong(hwnd, WL_TABLE, (long) (LPSTR) ptab);
  221.                         gtab_setsize(hwnd, ptab);
  222.                 } else {
  223.                         SetScrollRange(hwnd, SB_VERT, 0, 0, TRUE);
  224.                         SetScrollRange(hwnd, SB_HORZ, 0, 0, TRUE);
  225.                 }
  226.                 InvalidateRect(hwnd, NULL, TRUE);
  227.                 break;
  228.  
  229.         case TM_REFRESH:
  230.                 /* data in table has changed. nrows may have
  231.                  * changed. ncols and col types have not changed
  232.                  */
  233.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  234.                 if (ptab != NULL) {
  235.                         gtab_newsize(hwnd, ptab);
  236.                 }
  237.                 InvalidateRect(hwnd, NULL, TRUE);
  238.                 break;
  239.  
  240.         case TM_SELECT:
  241.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  242.                 if (ptab != NULL) {
  243.                         pselect = (lpTableSelection) lParam;
  244.  
  245.                         /*
  246.                          * we only support TM_SINGLE - so force the
  247.                          * selection to a single row or cell.
  248.                          */
  249.                         gtab_select(hwnd, ptab, pselect->startrow,
  250.                                 pselect->startcell,
  251.                                 1,
  252.                                 (ptab->hdr.selectmode & TM_ROW) ?
  253.                                         ptab->hdr.ncols : 1,
  254.                                 TRUE);
  255.                         gtab_showsel_middle(hwnd, ptab);
  256.                 }
  257.                 break;
  258.  
  259.         case TM_PRINT:
  260.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  261.                 hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
  262.                 if (ptab != NULL) {
  263.                         gtab_print(hwnd, ptab, hHeap, (lpPrintContext) lParam);
  264.                         return(TRUE);
  265.                 }
  266.  
  267.         case TM_TOPROW:
  268.  
  269.                 /* return top row. if wParam is TRUE, set lParam
  270.                  * as the new toprow
  271.                  */
  272.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  273.                 if (ptab == NULL) {
  274.                         return(0);
  275.                 }
  276.                 oldtop = ptab->toprow;
  277.                 if ((wParam) && (lParam < ptab->hdr.nrows)) {
  278.                         change = lParam - ptab->toprow;
  279.                         change -= ptab->hdr.fixedrows;
  280.                         gtab_dovscroll(hwnd, ptab, change);
  281.                 }
  282.                 return(oldtop);
  283.  
  284.         case TM_ENDROW:
  285.                 /* return the last visible row in the window */
  286.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  287.                 if (ptab == NULL) {
  288.                         return(0);
  289.                 }
  290.                 return(ptab->nlines + ptab->toprow - 1);
  291.  
  292.  
  293.         case TM_APPEND:
  294.                 /* new rows have been added to the end of the
  295.                  * table, but the rest of the table has no
  296.                  * been change. Update without forcing redraw of
  297.                  * everything.
  298.                  * lParam contains the new total nr of rows
  299.                  */
  300.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  301.                 if (ptab != NULL) {
  302.                         gtab_append(hwnd, ptab, wParam, lParam);
  303.                         return(TRUE);
  304.                 }
  305.                 break;
  306.  
  307.         case WM_SIZE:
  308.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  309.                 if (ptab != NULL) {
  310.                         gtab_setsize(hwnd, ptab);
  311.                 }
  312.                 break;
  313.  
  314.         case WM_DESTROY:
  315.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  316.                 if (ptab != NULL) {
  317.                         gtab_sendtq(hwnd, TQ_CLOSE, ptab->hdr.id);
  318.                         gtab_deltable(hwnd, ptab);
  319.                 }
  320.                 hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
  321.                 gmem_freeall(hHeap);
  322.                 break;
  323.  
  324.         case WM_PAINT:
  325.                 hDC = BeginPaint(hwnd, &ps);
  326.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  327.                 if (ptab != NULL) {
  328.                         /* separator lines between fixed rows/columns
  329.                          * (ie headers) and the rest - if enabled
  330.                          */
  331.                         /* paint here first for good impression,
  332.                          * and again after to clean up!!
  333.                          */
  334.                         if (ptab->hdr.vseparator) {
  335.                                 gtab_vsep(hwnd, ptab, hDC);
  336.                         }
  337.                         if (ptab->hdr.hseparator) {
  338.                                 gtab_hsep(hwnd, ptab, hDC);
  339.                         }
  340.  
  341.                         /* paint only the rows that need painting */
  342.                         for (i = 0; i < ptab->nlines; i++) {
  343.                                 y = ptab->pdata[i].linepos.start;
  344.                                 y2 = y + ptab->pdata[i].linepos.size;
  345.                                 if ( (y <= ps.rcPaint.bottom) &&
  346.                                      (y2 >= ps.rcPaint.top)) {
  347.                                         gtab_paint(hwnd, hDC, ptab, i);
  348.                                 }
  349.                         }
  350.                         if (ptab->hdr.vseparator) {
  351.                                 gtab_vsep(hwnd, ptab, hDC);
  352.                         }
  353.                         if (ptab->hdr.hseparator) {
  354.                                 gtab_hsep(hwnd, ptab, hDC);
  355.                         }
  356.                         if (ptab->selvisible) {
  357.                                 gtab_invertsel(hwnd, ptab, hDC);
  358.                         }
  359.                 }
  360.  
  361.                 EndPaint(hwnd, &ps);
  362.                 break;
  363.  
  364.         case WM_HSCROLL:
  365.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  366.                 if (ptab != NULL) {
  367.                         gtab_msg_hscroll(hwnd, ptab,
  368.                           GET_SCROLL_OPCODE(wParam, lParam),
  369.                           GET_SCROLL_POS(wParam, lParam));
  370.                 }
  371.                 break;
  372.  
  373.         case WM_VSCROLL:
  374.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  375.                 if (ptab != NULL) {
  376.                         gtab_msg_vscroll(hwnd, ptab,
  377.                           GET_SCROLL_OPCODE(wParam, lParam),
  378.                           GET_SCROLL_POS(wParam, lParam));
  379.                 }
  380.                 break;
  381.  
  382.         case WM_MOUSEMOVE:
  383.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  384.                 if (ptab != NULL) {
  385.                         gtab_move(hwnd, ptab, LOWORD(lParam), HIWORD(lParam));
  386.                 } else {
  387.                         SetCursor(hNormCurs);
  388.                 }
  389.                 break;
  390.  
  391.         case WM_LBUTTONDOWN:
  392.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  393.                 if (ptab != NULL) {
  394.                         gtab_press(hwnd, ptab, LOWORD(lParam), HIWORD(lParam));
  395.                 }
  396.                 break;
  397.  
  398.         case WM_LBUTTONUP:
  399.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  400.                 if (ptab != NULL) {
  401.                         gtab_release(hwnd, ptab,
  402.                                 LOWORD(lParam), HIWORD(lParam));
  403.                 }
  404.                 break;
  405.  
  406.         case WM_LBUTTONDBLCLK:
  407.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  408.                 if (ptab != NULL) {
  409.                         gtab_dblclick(hwnd, ptab,
  410.                                 LOWORD(lParam), HIWORD(lParam));
  411.                 }
  412.                 break;
  413.  
  414.         case WM_KEYDOWN:
  415.                 /* handle key presses for cursor movement about
  416.                  * the table, and return/space for selection.
  417.                  * Any key we don't handle is passed to the owner window
  418.                  * for him to handle.
  419.                  * The table window should have the focus
  420.                  */
  421.                 ptab = (lpTable) GetWindowLong(hwnd, WL_TABLE);
  422.                 if (ptab != NULL) {
  423.                         if (gtab_key(hwnd, ptab, wParam) != 0) {
  424.                                 /* pass key to owner since
  425.                                  * we don't know what to do with it
  426.                                  */
  427.                                 hOwner = (HANDLE) GetWindowLong(hwnd, WW_OWNER);
  428.                                 return(SendMessage(hOwner, WM_KEYDOWN,
  429.                                         wParam, lParam));
  430.                         } else {
  431.                                 return(0);      
  432.                         }
  433.                 }
  434.                 break;
  435.  
  436.         default:
  437.                 return(DefWindowProc(hwnd, msg, wParam, lParam));
  438.         }
  439.         return(TRUE);
  440. }
  441.  
  442. /***************************************************************************
  443.  * Function: gtab_sendtq
  444.  *
  445.  * Purpose:
  446.  *
  447.  * Send a table-query message to the owner window. Returns message
  448.  * value.
  449.  */
  450. long
  451. gtab_sendtq(HWND hwnd, UINT cmd, long lParam)
  452. {
  453.         HWND hOwner;
  454.  
  455.         hOwner = (HANDLE) GetWindowLong(hwnd, WW_OWNER);
  456.         return (SendMessage(hOwner, gtab_msgcode, cmd, lParam));
  457. }
  458.  
  459. /***************************************************************************
  460.  * Function: gtab_freelinedata
  461.  *
  462.  * Purpose:
  463.  *
  464.  * Free the memory allocated for the array of lines (each containing
  465.  * an array of Cells, each containing an array of chars for the actual
  466.  * data). Called on any occasion that would change the number of visible lines
  467.  */
  468. void
  469. gtab_freelinedata(HANDLE hHeap, lpTable ptab)
  470. {
  471.         int i, j, ncols;
  472.         lpCellData cd;
  473.  
  474.  
  475.         ncols = ptab->hdr.ncols;
  476.  
  477.         /* for each line */
  478.         for(i = 0; i < ptab->nlines; i++) {
  479.                 /* for each cell */
  480.                 for (j = 0; j < ncols; j++) {
  481.                         /* free up the actual text space */
  482.                         cd = &ptab->pdata[i].pdata[j];
  483.                         gmem_free(hHeap, (LPSTR) cd->ptext, cd->nchars);
  484.                 }
  485.                 /* dealloc array of CellData */
  486.                 gmem_free(hHeap, (LPSTR) ptab->pdata[i].pdata,
  487.                         sizeof(CellData) * ncols);
  488.         }
  489.         /* de-alloc array of linedatas */
  490.         gmem_free(hHeap, (LPSTR) ptab->pdata,
  491.                 sizeof(LineData) * ptab->nlines);
  492.         ptab->pdata = NULL;
  493. }
  494.  
  495. /***************************************************************************
  496.  * Function: gtab_alloclinedata
  497.  *
  498.  * Purpose:
  499.  *
  500.  * Allocate and init array of linedatas (include cell array
  501.  * and text for each cell)
  502.  */
  503. BOOL
  504. gtab_alloclinedata(HWND hwnd, HANDLE heap, lpTable ptab)
  505. {
  506.         lpLineData pline;
  507.         lpCellData cd;
  508.         int i, j;
  509.  
  510.         ptab->pdata = (lpLineData) gmem_get(heap,
  511.                 sizeof(LineData) * ptab->nlines);
  512.         if (ptab->pdata == NULL) {
  513.                 return(FALSE);
  514.         }
  515.         for (i = 0; i < ptab->nlines; i++) {
  516.                 pline = &ptab->pdata[i];
  517.                 pline->linepos.size = ptab->rowheight;
  518.                 pline->pdata = (lpCellData) gmem_get(heap,
  519.                         sizeof(CellData) * ptab->hdr.ncols);
  520.                 if (pline->pdata == NULL) {
  521.                         return(FALSE);
  522.                 }
  523.                 for (j = 0; j < ptab->hdr.ncols; j++) {
  524.                         cd = &pline->pdata[j];
  525.                         cd->props.valid = 0;
  526.                         cd->flags = 0;
  527.                         cd->nchars = ptab->pcolhdr[j].nchars;
  528.                         if (cd->nchars > 0) {
  529.                                 cd->ptext = gmem_get(heap, cd->nchars);
  530.                                 if (cd->ptext == NULL) {
  531.                                         return(FALSE);
  532.                                 }
  533.                         }
  534.                 }
  535.         }
  536. }
  537.  
  538. /***************************************************************************
  539.  * Function: gtab_deltable
  540.  *
  541.  * Purpose:
  542.  *
  543.  * Free up all table data structures. Called for new layout or new data.
  544.  */
  545. void
  546. gtab_deltable(HWND hwnd, lpTable ptab)
  547. {
  548.         HANDLE hHeap;
  549.         int ncols;
  550.  
  551.         if (ptab == NULL) {
  552.                 return;
  553.         }
  554.         hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
  555.         ncols = ptab->hdr.ncols;
  556.  
  557.         if (ptab->pcolhdr != NULL) {
  558.                 gmem_free(hHeap, (LPSTR) ptab->pcolhdr,
  559.                         sizeof(ColProps) * ncols);
  560.         }
  561.         if (ptab->pcellpos != NULL) {
  562.                 gmem_free(hHeap, (LPSTR) ptab->pcellpos,
  563.                         sizeof(CellPos) * ncols);
  564.         }
  565.         if (ptab->pdata != NULL) {
  566.                 gtab_freelinedata(hHeap, ptab);
  567.         }
  568.         gmem_free(hHeap, (LPSTR) ptab, sizeof(Table));
  569. }
  570.  
  571.  
  572. /***************************************************************************
  573.  * Function: gtab_buildtable
  574.  *
  575.  * Purpose:
  576.  *
  577.  * Build up a Table struct (excluding data allocation and
  578.  * anything to do with font or window size).
  579.  * Return ptr to this or NULL if error
  580.  */
  581. lpTable
  582. gtab_buildtable(HWND hwnd, DWORD id)
  583. {
  584.         lpTable ptab;
  585.         HANDLE hHeap;
  586.         int ncols, i;
  587.         ColPropsList cplist;
  588.  
  589.         hHeap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
  590.         ptab = (lpTable) gmem_get(hHeap, sizeof(Table));
  591.         if (ptab == NULL) {
  592.                 return(NULL);
  593.         }
  594.  
  595.         /* get the row/column count from owner window */
  596.         ptab->hdr.id = id;
  597.         ptab->hdr.props.valid = 0;
  598.         ptab->hdr.sendscroll = FALSE;
  599.         if (gtab_sendtq(hwnd, TQ_GETSIZE, (long) (LPSTR)&ptab->hdr) == FALSE) {
  600.                 return(NULL);
  601.         }
  602.  
  603.         ncols = ptab->hdr.ncols;
  604.         ptab->pcolhdr = (lpColProps) gmem_get(hHeap, sizeof(ColProps) * ncols);
  605.         if (ptab->pcolhdr == NULL) {
  606.                 /* should prob send TQ_CLOSE at this point */
  607.                 return(NULL);
  608.         }
  609.  
  610.         /* init col properties to default */
  611.         for (i=0; i < ncols; i++) {
  612.                 ptab->pcolhdr[i].props.valid = 0;
  613.                 ptab->pcolhdr[i].nchars = 0;
  614.         }
  615.         /* get the column props from owner */
  616.         cplist.plist = ptab->pcolhdr;
  617.         cplist.id = id;
  618.         cplist.startcol = 0;
  619.         cplist.ncols = ncols;
  620.         gtab_sendtq(hwnd, TQ_GETCOLPROPS, (long) (LPSTR)&cplist);
  621.  
  622.         /* init remaining fields */
  623.         ptab->pcellpos = (lpCellPos) gmem_get(hHeap, sizeof(CellPos) * ncols);
  624.         if (ptab->pcellpos == NULL) {
  625.                 return(NULL);
  626.         }
  627.  
  628.         ptab->scrollscale = 1;
  629.         ptab->scroll_dx = 0;
  630.         ptab->toprow = 0;
  631.         ptab->pdata = NULL;
  632.         ptab->nlines = 0;
  633.         ptab->trackmode = TRACK_NONE;
  634.  
  635.         /* we have to notify owner of the current selection
  636.          * whenever it is changed
  637.          */
  638.         ptab->select.id = id;
  639.         gtab_select(hwnd, ptab, 0, 0, 0, 0, TRUE);
  640.  
  641.         /* calc ave height/width, cell widths and min height.
  642.          * these change only when cell properties / col count changes -
  643.          * ie only on rebuild-header events
  644.          */
  645.         gtab_calcwidths(hwnd, ptab);
  646.         return(ptab);
  647. }
  648.  
  649. /***************************************************************************
  650.  * Function: gtab_setsize
  651.  *
  652.  * Purpose:
  653.  *
  654.  * Set sizes that are based on window size and scroll pos
  655.  * set:
  656.  *      winwidth
  657.  *      nlines
  658.  *      cellpos start, clip start/end
  659.  * Alloc linedata and init
  660.  */
  661. void
  662. gtab_setsize(HWND hwnd, lpTable ptab)
  663. {
  664.         RECT rc;
  665.         int nlines;
  666.         HANDLE heap;
  667.         long range, change;
  668.  
  669.         GetClientRect(hwnd, &rc);
  670.         ptab->winwidth = rc.right - rc.left;
  671.         nlines = (rc.bottom - rc.top) / ptab->rowheight;
  672.         /* nlines is the number of whole lines - add one extra
  673.          * for the partial line at the bottom
  674.          */
  675.         nlines += 1;
  676.  
  677.         /* alloc space for nlines of data - if nlines has changed */
  678.         if (nlines != ptab->nlines) {
  679.                 heap = (HANDLE) GetWindowLong(hwnd, WW_HEAP);
  680.                 gtab_freelinedata(heap, ptab);
  681.                 ptab->nlines = nlines;
  682.                 if (!gtab_alloclinedata(hwnd, heap, ptab)) {
  683.                         ptab->nlines = 0;
  684.                         return;
  685.                 }
  686.         }
  687.  
  688.         /* set scroll vertical range */
  689.         range = ptab->hdr.nrows - (ptab->nlines - 1);
  690.         if (range < 0) {
  691.                 range = 0;
  692.                 change =  -(ptab->toprow);
  693.         } else if (ptab->toprow > range) {
  694.                 change = range - ptab->toprow;
  695.         } else {
  696.                 change = 0;
  697.         }
  698.         /* the scroll range must be 16-bits for Win3
  699.          * scale until this is true
  700.          */
  701.         ptab->scrollscale = 1;
  702.         while (range > 32766) {
  703.                 ptab->scrollscale *= 16;
  704.                 range /= 16;
  705.         }
  706.  
  707.         SetScrollRange(hwnd, SB_VERT, 0, (int) range, TRUE);
  708.         gtab_dovscroll(hwnd, ptab, change);
  709.  
  710.         /* set horz scroll range */
  711.         range = ptab->rowwidth - ptab->winwidth;
  712.         if (range < 0) {
  713.                 range = 0;
  714.                 change = -(ptab->scroll_dx);
  715.         } else if (ptab->scroll_dx > range) {
  716.                 change = range - ptab->scroll_dx;
  717.         } else {
  718.                 change = 0;
  719.         }
  720.         /* horz scroll range will always be < 16 bits */
  721.         SetScrollRange(hwnd, SB_HORZ, 0, (int) range, TRUE);
  722.         gtab_dohscroll(hwnd, ptab, change);
  723. }
  724.  
  725. /***************************************************************************
  726.  * Function: gtab_calcwidths
  727.  *
  728.  * Purpose:
  729.  *
  730.  * Set column widths/height and totals (based on column props)
  731.  * - no assumption of window size (see gtab_setsize)
  732.  * sets avewidth,rowheight,cellpos.size,rowwidth (total of cellpos.size)
  733.  */
  734. void
  735. gtab_calcwidths(HWND hwnd, lpTable ptab)
  736. {
  737.         int i, cxtotal, cx, ave;
  738.         TEXTMETRIC tm, tmcol;
  739.         HDC hdc;
  740.         lpProps hdrprops, cellprops;
  741.         HFONT hfont;
  742.  
  743.         hdrprops = &ptab->hdr.props;
  744.         hdc = GetDC(hwnd);
  745.         if (hdrprops->valid & P_FONT) {
  746.                 hfont = SelectObject(hdc, hdrprops->hFont);
  747.         }
  748.         GetTextMetrics(hdc, &tm);
  749.         if (hdrprops->valid & P_FONT) {
  750.                 SelectObject(hdc, hfont);
  751.         }
  752.         ReleaseDC(hwnd, hdc);
  753.  
  754.         /* get width and height of average character */
  755.         ptab->avewidth = tm.tmAveCharWidth;
  756.         ptab->rowheight = tm.tmHeight + tm.tmExternalLeading;
  757.         if (hdrprops->valid & P_HEIGHT) {
  758.                 ptab->rowheight = hdrprops->height;
  759.         }
  760.  
  761.         /* set pixel width of each cell (and add up for row total)
  762.          * based on ave width * nr chars, unless P_WIDTH set
  763.          */
  764.         cxtotal = 0;
  765.         for (i = 0; i < ptab->hdr.ncols; i++) {
  766.                 cellprops = &ptab->pcolhdr[i].props;
  767.  
  768.                 if (cellprops->valid & P_WIDTH) {
  769.                         cx = cellprops->width;
  770.                 } else if (hdrprops->valid & P_WIDTH) {
  771.                         cx = hdrprops->width;
  772.                 } else {
  773.  
  774.                         if (cellprops->valid & P_FONT) {
  775.                                 hdc = GetDC(hwnd);
  776.                                 hfont = SelectObject(hdc, cellprops->hFont);
  777.                                 GetTextMetrics(hdc, &tmcol);
  778.                                 SelectObject(hdc, hfont);
  779.                                 ReleaseDC(hwnd, hdc);
  780.                                 ave = tmcol.tmAveCharWidth;
  781.                         } else {
  782.                                 ave = ptab->avewidth;
  783.                         }
  784.                         /* ave width * nchars */
  785.                         cx =  ptab->pcolhdr[i].nchars + 1;
  786.                         cx *= ave;
  787.                 }
  788.                 /* add 2 pixels for box lines */
  789.                 cx += 2;
  790.                 ptab->pcellpos[i].size = cx;
  791.                 cxtotal += cx;
  792.         }
  793.         ptab->rowwidth = cxtotal;
  794. }
  795.  
  796. /***************************************************************************
  797.  * Function: gtab_newsize
  798.  *
  799.  * Purpose:
  800.  *
  801.  * Called when row data + possible nrows changes.
  802.  * other changes are ignored
  803.  */
  804. void
  805. gtab_newsize(HWND hwnd, lpTable ptab)
  806. {
  807.         TableHdr hdr;
  808.  
  809.         /* get new row count */
  810.         hdr = ptab->hdr;
  811.         gtab_sendtq(hwnd, TQ_GETSIZE, (long) (LPSTR) &hdr);
  812.         if (hdr.nrows != ptab->hdr.nrows) {
  813.                 ptab->hdr.nrows = hdr.nrows;
  814.                 gtab_setsize(hwnd, ptab);
  815.         }
  816.  
  817.         gtab_invallines(hwnd, ptab, 0, ptab->nlines);
  818.  
  819.         InvalidateRect(hwnd, NULL, TRUE);
  820. }
  821.  
  822. void
  823. gtab_invallines(HWND hwnd, lpTable ptab, int start, int count)
  824. {
  825.         int i, j;
  826.  
  827.         for (i = start; i < start + count; i++) {
  828.                 for (j = 0; j < ptab->hdr.ncols; j++) {
  829.                         ptab->pdata[i].pdata[j].flags = 0;
  830.                 }
  831.         }
  832. }
  833.  
  834. /***************************************************************************
  835.  * Function: gtab_append
  836.  *
  837.  * Purpose:
  838.  *
  839.  * New rows have been added to the table. Adjust the scroll range and
  840.  * position, and redraw the rows if the end of the table is currently
  841.  * visible.
  842.  * rows = the new total row count.
  843.  */
  844. void
  845. gtab_append(HWND hwnd, lpTable ptab, int rows, DWORD id)
  846. {
  847.         long range;
  848.         long oldrows;
  849.         int line, nupdates;
  850.         RECT rc;
  851.  
  852.  
  853.         /* change to the new id */
  854.         ptab->hdr.id = id;
  855.         ptab->select.id = id;
  856.  
  857.         /* update the header, but remember the old nr of rows
  858.          * so we know where to start updating
  859.          */
  860.         oldrows = ptab->hdr.nrows;
  861.  
  862.         /* check that the new nr of rows is not smaller. this is
  863.          * illegal at this point and should be ignored
  864.          */
  865.         if (oldrows >= rows) {
  866.                 return; 
  867.         }
  868.  
  869.         ptab->hdr.nrows = rows;
  870.  
  871.         /* set the vertical scroll range */
  872.         range = rows - (ptab->nlines - 1);
  873.  
  874.         if (range < 0) {
  875.                 range = 0;      
  876.         }
  877.  
  878.         /* force the scroll range into 16-bits for win 3.1 */
  879.         ptab->scrollscale = 1;
  880.         while (range > 32766) {
  881.                 ptab->scrollscale *= 16;
  882.                 range /= 16;
  883.         }
  884.  
  885.         /* now set the scroll bar range and position */
  886.         SetScrollRange(hwnd, SB_VERT, 0, (int) range, TRUE);
  887.         if (range > 0) {
  888.                 SetScrollPos(hwnd, SB_VERT,
  889.                         (int) (ptab->toprow / ptab->scrollscale), TRUE);
  890.         }
  891.  
  892.         /* calculate which screen lines need to be updated - find what
  893.          * screen line the start of the new section is at
  894.          */
  895.         line = gtab_rowtoline(hwnd, ptab, oldrows);
  896.         if (line == -1) {
  897.                 /* not visible -> no more to do */
  898.                 return;
  899.         }
  900.  
  901.         /* how many lines to update - rest of screen or nr of
  902.          * new lines if less than rest of screen
  903.          */
  904.         nupdates = min((ptab->nlines - line), (int)(rows - oldrows));
  905.  
  906.         /* invalidate the screen line buffers to indicate data
  907.          * needs to be refetch from parent window
  908.          */
  909.         gtab_invallines(hwnd, ptab, line, nupdates);
  910.  
  911.         /* calculate the region of the screen to be repainted -
  912.          * left and right are same as window. top and bottom
  913.          * need to be calculated from screen line height
  914.          */
  915.         
  916.         GetClientRect(hwnd, &rc);
  917.         rc.top += line * ptab->rowheight;
  918.         rc.bottom = rc.top + (nupdates * ptab->rowheight);
  919.  
  920.         /* force a repaint of the updated region */
  921.         InvalidateRect(hwnd, &rc, TRUE);
  922. }
  923.         
  924.  
  925.  
  926.         
  927.