TABLE.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:31k
源码类别:

Windows编程

开发平台:

Visual C++

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