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 / tprint.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  22KB  |  680 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: TPRINT.C
  14. *
  15. * Print functions.
  16. *
  17. * Functions:
  18. *
  19. * gtab_print()
  20. * gtab_printsetup()
  21. * gtab_prtwidths()
  22. * gtab_printjob()
  23. * AbortProc()
  24. * AbortDlg()
  25. * gtab_printpage()
  26. * gtab_setrects()
  27. * gtab_printhead()
  28. *
  29. * Comments:
  30. *
  31. * See table.h for interface description.
  32. *
  33. ****************************************************************************/
  34.  
  35. #include <string.h>
  36. #include <windows.h>
  37. #include <commdlg.h>
  38.  
  39. #include "gutils.h"
  40. #include "gutilsrc.h"
  41. #include "table.h"
  42. #include "tpriv.h"
  43.  
  44. /* in tpaint.c, calls GetTextExtentPoint */
  45. extern int GetTextExtent(HDC, LPSTR, int);
  46.  
  47. extern HANDLE hLibInst;
  48.  
  49. /* function prototypes */
  50. lpTable gtab_printsetup(HWND hwnd, lpTable ptab, HANDLE heap,
  51.         lpPrintContext pcontext);
  52. BOOL gtab_prtwidths(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext
  53.         pcontext);
  54. void gtab_printjob(HWND hwnd, lpTable ptab, lpPrintContext pcontext);
  55. int APIENTRY AbortProc(HDC hpr, int code);
  56. int APIENTRY AbortDlg(HWND hdlg, UINT msg, UINT wParam, LONG lParam);
  57. BOOL gtab_printpage(HWND hwnd, lpTable ptab, lpPrintContext pcontext, int page);
  58. void gtab_setrects(lpPrintContext pcontext, LPRECT rcinner, LPRECT rcouter);
  59. void gtab_printhead(HWND hwnd, HDC hdc, lpTable ptab, lpTitle head, int page);
  60.  
  61.  
  62. /***************************************************************************
  63.  * Function: gtab_print
  64.  *
  65.  * Purpose:
  66.  *
  67.  * Prints a table.
  68.  */
  69. void
  70. gtab_print(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext pcontext)
  71. {
  72.         BOOL fNoContext, fNoMargin, fNoPD;
  73.         lpTable ptab_prt;
  74.  
  75.         fNoContext = FALSE;
  76.         fNoPD = FALSE;
  77.         fNoMargin = FALSE;
  78.  
  79.         if (pcontext == NULL) {
  80.                 fNoContext = TRUE;
  81.                 pcontext = (lpPrintContext) gmem_get(heap,
  82.                         sizeof(PrintContext));
  83.                 pcontext->head = pcontext->foot = NULL;
  84.                 pcontext->margin = NULL;
  85.                 pcontext->pd = NULL;
  86.                 pcontext->id = 0;
  87.         }
  88.         if (pcontext->pd == NULL) {
  89.                 fNoPD = TRUE;
  90.         }
  91.         if (pcontext->margin == NULL) {
  92.                 fNoMargin = TRUE;
  93.         }
  94.         ptab_prt = gtab_printsetup(hwnd, ptab, heap, pcontext);
  95.  
  96.         if (ptab_prt != NULL) {
  97.                 gtab_printjob(hwnd, ptab_prt, pcontext);
  98.  
  99.                 gtab_deltable(hwnd, ptab_prt);
  100.         }
  101.         if (fNoMargin) {
  102.                 gmem_free(heap, (LPSTR)pcontext->margin,
  103.                         sizeof(Margin));
  104.                 pcontext->margin = NULL;
  105.         }
  106.         if (fNoPD) {
  107.                 if (pcontext->pd->hDevMode != NULL) {
  108.                         GlobalFree(pcontext->pd->hDevMode);
  109.                 }
  110.                 if (pcontext->pd->hDevNames != NULL) {
  111.                         GlobalFree(pcontext->pd->hDevNames);
  112.                 }
  113.                 gmem_free(heap, (LPSTR) pcontext->pd, sizeof(PRINTDLG));
  114.                 pcontext->pd = NULL;
  115.         }
  116.         if (fNoContext) {
  117.                 gmem_free(heap, (LPSTR) pcontext, sizeof(PrintContext));
  118.         }
  119. }
  120.  
  121.  
  122.  
  123. /***************************************************************************
  124.  * Function: gtab_printsetup
  125.  *
  126.  * Purpose:
  127.  *
  128.  * Sets up printercontext - builds lpTable for printer, incl. sizing
  129.  * and initialises pcontext fields that may be null.
  130.  */
  131. lpTable
  132. gtab_printsetup(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext pcontext)
  133. {
  134.         lpTable pprttab;
  135.         PRINTDLG FAR * pd;
  136.         int ncols, i;
  137.         ColPropsList cplist;
  138.  
  139.         /* set fields for context that user left null */
  140.         if (pcontext->margin == NULL) {
  141.                 pcontext->margin = (lpMargin) gmem_get(heap, sizeof(Margin));
  142.                 if (pcontext->margin == NULL) {
  143.                         return(NULL);
  144.                 }
  145.                 pcontext->margin->left = 10;
  146.                 pcontext->margin->right = 10;
  147.                 pcontext->margin->top = 15;
  148.                 pcontext->margin->bottom = 15;
  149.                 pcontext->margin->topinner = 15;
  150.                 pcontext->margin->bottominner = 15;
  151.         }
  152.  
  153.         if (pcontext->pd == NULL) {
  154.                 pd = (PRINTDLG FAR *) gmem_get(heap, sizeof(PRINTDLG));
  155.                 if (pd == NULL) {
  156.                         return(NULL);
  157.                 }
  158.                 pcontext->pd = pd;
  159.  
  160.                 pd->lStructSize = sizeof(PRINTDLG);
  161.                 pd->hwndOwner = hwnd;
  162.                 pd->hDevMode = (HANDLE) NULL;
  163.                 pd->hDevNames = (HANDLE) NULL;
  164.                 pd->Flags = PD_RETURNDC|PD_RETURNDEFAULT;
  165.  
  166.                 if (PrintDlg(pd) == FALSE) {
  167.                         return(NULL);
  168.                 }
  169.         }
  170.  
  171.         /* now create a Table struct by querying the owner */
  172.         pprttab = (lpTable) gmem_get(heap, sizeof(Table));
  173.  
  174.         if (pprttab == NULL) {
  175.                 return(NULL);
  176.         }
  177.         pprttab->hdr = ptab->hdr;
  178.  
  179.         /* get the row/column count from owner window */
  180.         if (pcontext->id == 0) {
  181.                 pprttab->hdr.id = ptab->hdr.id;
  182.         } else {
  183.                 pprttab->hdr.id = pcontext->id;
  184.         }
  185.         pprttab->hdr.props.valid = 0;
  186.         pprttab->hdr.sendscroll = FALSE;
  187.         if (gtab_sendtq(hwnd, TQ_GETSIZE, (long) (LPSTR)&pprttab->hdr) == FALSE) {
  188.                 return(NULL);
  189.         }
  190.  
  191.         /* alloc and init the col data structs */
  192.         ncols = pprttab->hdr.ncols;
  193.         pprttab->pcolhdr = (lpColProps) gmem_get(heap, sizeof(ColProps) * ncols);
  194.         if (pprttab->pcolhdr == NULL) {
  195.                 gmem_free(heap, (LPSTR)pprttab, sizeof(Table));
  196.                 return(NULL);
  197.         }
  198.  
  199.         /* init col properties to default */
  200.         for (i=0; i < ncols; i++) {
  201.                 pprttab->pcolhdr[i].props.valid = 0;
  202.                 pprttab->pcolhdr[i].nchars = 0;
  203.         }
  204.         /* get the column props from owner */
  205.         cplist.plist = pprttab->pcolhdr;
  206.         cplist.id = pprttab->hdr.id;
  207.         cplist.startcol = 0;
  208.         cplist.ncols = ncols;
  209.         gtab_sendtq(hwnd, TQ_GETCOLPROPS, (long) (LPSTR)&cplist);
  210.  
  211.  
  212.         pprttab->scrollscale = 1;
  213.         pprttab->pcellpos = (lpCellPos) gmem_get(heap,
  214.                 sizeof(CellPos) * ptab->hdr.ncols);
  215.         if (pprttab->pcellpos == NULL) {
  216.                 gmem_free(heap, (LPSTR) pprttab->pcolhdr, sizeof(ColProps) * ncols);
  217.                 gmem_free(heap, (LPSTR)pprttab, sizeof(Table));
  218.                 return(NULL);
  219.         }
  220.                 
  221.  
  222.         pprttab->pdata = NULL;
  223.         pprttab->nlines = 0;
  224.  
  225.         if (!gtab_prtwidths(hwnd, pprttab, heap, pcontext)) {
  226.                 gmem_free(heap, (LPSTR)pprttab->pcellpos,
  227.                         sizeof(CellPos) * ptab->hdr.ncols);
  228.                 gmem_free(heap, (LPSTR)pprttab, sizeof(Table));
  229.                 return(NULL);
  230.         }
  231.         return(pprttab);
  232. }
  233.  
  234.  
  235. /***************************************************************************
  236.  * Function: gtab_prtwidths
  237.  *
  238.  * Purpose:
  239.  *
  240.  * Calc the height/width settings and alloc line data 
  241.  */
  242. BOOL
  243. gtab_prtwidths(HWND hwnd, lpTable ptab, HANDLE heap, lpPrintContext pcontext)
  244. {
  245.         TEXTMETRIC tm;
  246.         int cx, cxtotal, i, curx, cury;
  247.         lpProps hdrprops, cellprops;
  248.         lpCellPos xpos, ypos;
  249.         RECT rcinner, rcouter;
  250.  
  251.         hdrprops = &ptab->hdr.props;
  252.         GetTextMetrics(pcontext->pd->hDC, &tm);
  253.         ptab->avewidth = tm.tmAveCharWidth;
  254.         ptab->rowheight = tm.tmHeight + tm.tmExternalLeading;
  255.         if (hdrprops->valid & P_HEIGHT) {
  256.                 ptab->rowheight = hdrprops->height;
  257.         }
  258.  
  259.         /* set sizes for headers */
  260.         gtab_setrects(pcontext, &rcinner, &rcouter);
  261.  
  262.         /* set width/pos for each col. */
  263.         cxtotal = 0;
  264.         curx = rcinner.left;
  265.         for (i = 0; i < ptab->hdr.ncols; i++) {
  266.                 cellprops = &ptab->pcolhdr[i].props;
  267.                 xpos = &ptab->pcellpos[i];
  268.  
  269.                 if (cellprops->valid & P_WIDTH) {
  270.                         cx = cellprops->width;
  271.                 } else if (hdrprops->valid & P_WIDTH) {
  272.                         cx = hdrprops->width;
  273.                 } else {
  274.                         cx = ptab->pcolhdr[i].nchars + 1;
  275.                         cx *= ptab->avewidth;
  276.                 }
  277.                 /* add 2 for intercol spacing */
  278.                 cx += 2;
  279.  
  280.                 xpos->size = cx;
  281.                 xpos->start = curx + 1;
  282.                 xpos->clipstart = xpos->start;
  283.                 xpos->clipend = xpos->start + xpos->size - 2;
  284.                 xpos->clipend = min(xpos->clipend, rcinner.right);
  285.  
  286.                 cxtotal += xpos->size;
  287.                 curx += xpos->size;
  288.         }
  289.         ptab->rowwidth = cxtotal;
  290.  
  291.         if(pcontext->head != NULL) {
  292.                 xpos = &pcontext->head->xpos;
  293.                 ypos = &pcontext->head->ypos;
  294.  
  295.                 xpos->start = rcouter.left + 1;
  296.                 xpos->clipstart = rcouter.left + 1;
  297.                 xpos->clipend = rcouter.right - 1;
  298.                 xpos->size = rcouter.right - rcouter.left;
  299.  
  300.                 ypos->start = rcouter.top;
  301.                 ypos->clipstart = rcouter.top;
  302.                 ypos->clipend = rcinner.top;
  303.                 ypos->size = ptab->rowheight;
  304.         }
  305.  
  306.         if (pcontext->foot != NULL) {
  307.                 xpos = &pcontext->foot->xpos;
  308.                 ypos = &pcontext->foot->ypos;
  309.  
  310.                 xpos->start = rcouter.left + 1;
  311.                 xpos->clipstart = rcouter.left + 1;
  312.                 xpos->clipend = rcouter.right - 1;
  313.                 xpos->size = rcouter.right - rcouter.left;
  314.  
  315.                 ypos->start = rcouter.bottom - ptab->rowheight;
  316.                 ypos->clipstart = rcinner.bottom;
  317.                 ypos->clipend = rcouter.bottom;
  318.                 ypos->size = ptab->rowheight;
  319.         }
  320.  
  321.         /* set nr of lines per page */
  322.         ptab->nlines = (rcinner.bottom - rcinner.top) / ptab->rowheight;
  323.         if (!gtab_alloclinedata(hwnd, heap, ptab)) {
  324.                 return(FALSE);
  325.         }
  326.         /* set line positions */
  327.         cury = rcinner.top;
  328.         for (i = 0; i < ptab->nlines; i++) {
  329.                 ypos = &ptab->pdata[i].linepos;
  330.                 ypos->start = cury;
  331.                 ypos->clipstart = ypos->start;
  332.                 ypos->clipend = ypos->start + ypos->size;
  333.                 ypos->clipend = min(ypos->clipend, rcinner.bottom);
  334.                 cury += ypos->size;
  335.         }
  336.         return(TRUE);
  337. }
  338.  
  339.  
  340. /* static information for this module */
  341. BOOL bAbort;
  342. FARPROC lpAbortProc;
  343. DLGPROC lpAbortDlg;
  344. HWND hAbortWnd;
  345. int npage;
  346. int pages;
  347.  
  348. /***************************************************************************
  349.  * Function: gtab_printjob
  350.  *
  351.  * Purpose:
  352.  *
  353.  * Sets up print job and dialogs
  354.  */ 
  355. void
  356. gtab_printjob(HWND hwnd, lpTable ptab, lpPrintContext pcontext)
  357. {
  358.         int moveables;
  359.         int endpage;
  360.         int startpage = 1;
  361.         HDC hpr;
  362.         int status;
  363.         HANDLE hcurs;
  364.         static char str[256];
  365.         DOCINFO di;
  366.  
  367.         CHAR szPage[60];  /* for LoadString */
  368.  
  369.  
  370.         hcurs = SetCursor(LoadCursor(NULL, IDC_WAIT));
  371.  
  372.         moveables = ptab->nlines - ptab->hdr.fixedrows;
  373.         pages = (int) (ptab->hdr.nrows - ptab->hdr.fixedrows + moveables - 1)
  374.                         / moveables;
  375.         endpage = pages;
  376.  
  377.         if (pcontext->pd->Flags & PD_PAGENUMS) {
  378.                 startpage = pcontext->pd->nFromPage;
  379.                 endpage = pcontext->pd->nToPage;
  380.         }
  381.         hpr = pcontext->pd->hDC;
  382.  
  383.         lpAbortDlg = (DLGPROC) MakeProcInstance((WNDPROC) AbortDlg, hLibInst);
  384.         lpAbortProc = (FARPROC) MakeProcInstance((WNDPROC)AbortProc, hLibInst);
  385.  
  386.         SetAbortProc(hpr, (ABORTPROC) lpAbortProc);
  387.  
  388.         ZeroMemory( &di, sizeof( DOCINFO ) );
  389.         di.lpszDocName = "Table";
  390.         di.cbSize = sizeof( DOCINFO );
  391.         di.lpszOutput = NULL;
  392.  
  393.         StartDoc(hpr, &di);
  394.  
  395.         bAbort = FALSE;
  396.  
  397.         /* add abort modeless dialog later!! */
  398.         hAbortWnd = CreateDialog(hLibInst, "GABRTDLG", hwnd, lpAbortDlg);
  399.         if (hAbortWnd != NULL) {
  400.                 ShowWindow(hAbortWnd, SW_NORMAL);
  401.                 EnableWindow(hwnd, FALSE);
  402.         }
  403.         SetCursor(hcurs);
  404.  
  405.         for (npage = startpage; npage<=endpage; npage++) {
  406.                 LoadString(hLibInst, IDS_PAGE_STR, szPage, sizeof(szPage));
  407.                 wsprintf(str, szPage,  npage, pages);
  408.                 SetDlgItemText(hAbortWnd, IDC_LPAGENR, str);
  409.                 status = gtab_printpage(hwnd, ptab, pcontext, npage);
  410.                 if (status < 0) {
  411.                         AbortDoc(hpr);
  412.                         break;
  413.                 }
  414.         }
  415.         if (status >= 0) {
  416.                 EndDoc(hpr);
  417.         }
  418.         
  419.         if (hAbortWnd != NULL) {
  420.                 EnableWindow(hwnd, TRUE);
  421.                 DestroyWindow(hAbortWnd);
  422.         }
  423.         FreeProcInstance((WNDPROC) lpAbortDlg);
  424.         FreeProcInstance(lpAbortProc);
  425.  
  426.         DeleteDC(hpr);
  427. }
  428.  
  429. /***************************************************************************
  430.  * Function: AbortProc
  431.  *
  432.  * Purpose:
  433.  *
  434.  * Abort procedure for print job
  435.  */
  436. int APIENTRY
  437. AbortProc(HDC hpr, int code)
  438. {
  439.  
  440.         MSG msg;
  441.  
  442.         if (!hAbortWnd) {
  443.                 return(TRUE);
  444.         }
  445.         while (!bAbort && PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
  446.                 if (!IsDialogMessage(hAbortWnd, &msg)) {
  447.                         TranslateMessage(&msg);
  448.                         DispatchMessage(&msg);
  449.                 }
  450.         }
  451.         return(!bAbort);
  452. }
  453.  
  454. /***************************************************************************
  455.  * Function: AbortDlg
  456.  *
  457.  * Purpose:
  458.  *
  459.  * Dialog for abort procedure
  460.  */
  461. int APIENTRY
  462. AbortDlg(HWND hdlg, UINT msg, UINT wParam, LONG lParam)
  463. {
  464.         switch(msg) {
  465.  
  466.         case WM_COMMAND:
  467.                 bAbort = TRUE;
  468.                 DestroyWindow (hdlg);
  469.                 return TRUE;
  470.  
  471.         case WM_INITDIALOG:
  472.                 return TRUE;
  473.         }
  474.         return(FALSE);
  475. }
  476.  
  477. /***************************************************************************
  478.  * Function: gtab_printpage
  479.  *
  480.  * Purpose:
  481.  *
  482.  * Print a single page. page number is 1-based
  483.  */
  484. BOOL
  485. gtab_printpage(HWND hwnd, lpTable ptab, lpPrintContext pcontext, int page)
  486. {
  487.         HDC hpr;
  488.         int moveables, i;
  489.         int x1, y1, x2, y2;
  490.  
  491.         hpr = pcontext->pd->hDC;
  492.         StartPage(hpr);
  493.  
  494.         moveables = ptab->nlines - ptab->hdr.fixedrows;
  495.         ptab->toprow = moveables * (page-1);
  496.         gtab_invallines(hwnd, ptab, ptab->hdr.fixedrows, moveables);
  497.  
  498.         for (i =0; i < ptab->nlines; i++) {
  499.                 gtab_paint(hwnd, hpr, ptab, i);
  500.         }
  501.         if ((ptab->hdr.vseparator) && (ptab->hdr.fixedcols > 0)) {
  502.                 x1 = ptab->pcellpos[ptab->hdr.fixedcols -1].clipend+1;
  503.                 y1 = ptab->pdata[0].linepos.clipstart;
  504.                 y2 = ptab->pdata[ptab->nlines-1].linepos.clipend;
  505.                 MoveToEx(hpr, x1, y1, NULL);
  506.                 LineTo(hpr, x1, y2);
  507.         }
  508.         if ((ptab->hdr.hseparator) && (ptab->hdr.fixedrows > 0)) {
  509.                 y1 = ptab->pdata[ptab->hdr.fixedrows-1].linepos.clipend;
  510.                 x1 = ptab->pcellpos[0].clipstart;
  511.                 x2 = ptab->pcellpos[ptab->hdr.ncols-1].clipend;
  512.                 MoveToEx(hpr, x1, y1, NULL);
  513.                 LineTo(hpr, x2, y1);
  514.         }
  515.  
  516.         if (pcontext->head != NULL) {
  517.                 gtab_printhead(hwnd, hpr, ptab, pcontext->head, page);
  518.         }
  519.         if (pcontext->foot != NULL) {
  520.                 gtab_printhead(hwnd, hpr, ptab, pcontext->foot, page);
  521.         }
  522.  
  523.         return(EndPage(hpr));
  524. }
  525.  
  526.  
  527. /***************************************************************************
  528.  * Function: gtab_setrects
  529.  *
  530.  * Purpose:
  531.  *
  532.  * Calculate the outline positions in pixels for the headers
  533.  * (outer rect) and for the page itself (inner rect). Based on
  534.  * page size and PrintContext margin info (which is in millimetres).
  535.  */
  536. void
  537. gtab_setrects(lpPrintContext pcontext, LPRECT rcinner, LPRECT rcouter)
  538. {
  539.         HDC hpr;
  540.         int hpixels, hmms;
  541.         int vpixels, vmms;
  542.         int h_pixpermm, v_pixpermm;
  543.  
  544.         hpr = pcontext->pd->hDC;
  545.         hpixels = GetDeviceCaps(hpr, HORZRES);
  546.         vpixels = GetDeviceCaps(hpr, VERTRES);
  547.         vmms = GetDeviceCaps(hpr, VERTSIZE);
  548.         hmms = GetDeviceCaps(hpr, HORZSIZE);
  549.  
  550.         h_pixpermm = hpixels / hmms;
  551.         v_pixpermm = vpixels / vmms;
  552.  
  553.         rcouter->top = (pcontext->margin->top * v_pixpermm);
  554.         rcouter->bottom = vpixels - (pcontext->margin->bottom * v_pixpermm);
  555.         rcouter->left = (pcontext->margin->left * h_pixpermm);
  556.         rcouter->right = hpixels - (pcontext->margin->right * h_pixpermm);
  557.  
  558.         rcinner->left = rcouter->left;
  559.         rcinner->right = rcouter->right;
  560.         rcinner->top = rcouter->top +
  561.                 (pcontext->margin->topinner * v_pixpermm);
  562.         rcinner->bottom = rcouter->bottom -
  563.                 (pcontext->margin->bottominner * v_pixpermm);
  564. }
  565.  
  566.  
  567. /***************************************************************************
  568.  * Function: gtab_printhead
  569.  *
  570.  * Purpose:
  571.  *
  572.  * Print header information
  573.  */
  574. void
  575. gtab_printhead(HWND hwnd, HDC hdc, lpTable ptab, lpTitle head, int page)
  576. {
  577.         RECT rc, rcbox;
  578.         int i, cx, x, y, tab;
  579.         UINT align;
  580.         LPSTR chp, tabp;
  581.         DWORD fcol, bkcol;
  582.         char str[256];
  583.  
  584.         rc.top = head->ypos.clipstart;
  585.         rc.bottom = head->ypos.clipend;
  586.         rc.left = head->xpos.clipstart;
  587.         rc.right = head->xpos.clipend;
  588.  
  589.         /* update page number */
  590.         chp = str;
  591.         for (i = 0; i < lstrlen(head->ptext); i++) {
  592.                 switch(head->ptext[i]) {
  593.  
  594.                 case '#':
  595.                         chp += wsprintf(chp, "%d", page);
  596.                         break;
  597.                 
  598.                 case '$':
  599.                         chp += wsprintf(chp, "%d", pages);
  600.                         break;
  601.                 
  602.                 default:
  603.                         if (IsDBCSLeadByte(head->ptext[i]) &&
  604.                             head->ptext[i+1]) {
  605.                                 *chp = head->ptext[i];
  606.                                 chp++;
  607.                                 i++;
  608.                         }
  609.                         *chp++ = head->ptext[i];
  610.                         break;
  611.                 }
  612.         }
  613.         *chp = '\0';
  614.         chp = str;
  615.  
  616.         if (head->props.valid & P_ALIGN) {
  617.                 align = head->props.alignment;
  618.         } else {
  619.                 align = P_LEFT;
  620.         }
  621.  
  622.         /* set colours if not default */
  623.         if (head->props.valid & P_FCOLOUR) {
  624.                 fcol = SetTextColor(hdc, head->props.forecolour);
  625.         }
  626.         if (head->props.valid & P_BCOLOUR) {
  627.                 bkcol = SetBkColor(hdc, head->props.backcolour);
  628.         }
  629.  
  630.         /* calc offset of text within cell for right-align or centering */
  631.         if (align == P_LEFT) {
  632.                 cx = ptab->avewidth/2;
  633.         } else {
  634.                 cx = LOWORD(GetTextExtent(hdc, chp, lstrlen(chp)));
  635.                 if (align == P_CENTRE) {
  636.                         cx = (head->xpos.size - cx) / 2;
  637.                 } else {
  638.                         cx = head->xpos.size - cx - (ptab->avewidth/2);
  639.                 }
  640.         }
  641.         cx += head->xpos.start;
  642.  
  643.         /* expand tabs on output */
  644.         tab = ptab->avewidth * 8;
  645.         x = 0;
  646.         y = head->ypos.start;
  647.  
  648.         for ( ; (tabp = strchr(chp, '\t')) != NULL; ) {
  649.                 /* perform output upto tab char */
  650.                 ExtTextOut(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, tabp-chp, NULL);
  651.                 
  652.                 /* advance past the tab */
  653.                 x += LOWORD(GetTextExtent(hdc, chp, tabp - chp));
  654.                 x = ( (x + tab) / tab) * tab;
  655.                 chp = ++tabp;
  656.         }
  657.  
  658.         /*no more tabs - output rest of string */
  659.         ExtTextOut(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, lstrlen(chp), NULL);
  660.  
  661.         /* reset colours to original if not default */
  662.         if (head->props.valid & P_FCOLOUR) {
  663.                 SetTextColor(hdc, fcol);
  664.         }
  665.         if (head->props.valid & P_BCOLOUR) {
  666.                 SetBkColor(hdc, bkcol);
  667.         }
  668.  
  669.         /* now box cell if marked */
  670.         if (head->props.valid & P_BOX) {
  671.                 if (head->props.box != 0) {
  672.                         rcbox.top = head->ypos.start;
  673.                         rcbox.bottom = rcbox.top + head->ypos.size;
  674.                         rcbox.left = head->xpos.start;
  675.                         rcbox.right = rcbox.left + head->xpos.size;
  676.                         gtab_boxcell(hwnd, hdc, &rcbox, &rc, head->props.box);
  677.                 }
  678.         }
  679. }
  680.