/******************************************************************************\ * This is a part of the Microsoft Source Code Samples. * Copyright (C) 1993-1997 Microsoft Corporation. * All rights reserved. * This source code is only intended as a supplement to * Microsoft Development Tools and/or WinHelp documentation. * See these sources for detailed information regarding the * Microsoft samples programs. \******************************************************************************/ /****************************** Module Header ******************************* * Module Name: TPAINT.C * * Paint functions * * Functions: * * GetTextExtent() * gtab_updatecontig() * gtab_delcr() * gtab_updateline() * gtab_updatecontig() * gtab_boxcell() * gtab_paintcell() * gtab_paint() * gtab_vsep() * gtab_hsep() * gtab_drawvertline() * gtab_invertsel() * * Comments: * * See table.h for interface design. * ****************************************************************************/ #include #include #include #include "gutils.h" #include "table.h" #include "tpriv.h" /*************************************************************************** * Function: GetTextExtent * * Purpose: * * Calls GetTextExtentPoint - for ease of porting. */ int GetTextExtent(HDC hdc, LPSTR text, int len) { SIZE sz; GetTextExtentPoint(hdc, text, len, &sz); return(sz.cx); } void gtab_updatecontig(HWND hwnd, lpTable ptab, int line, int cell1, int count); /*************************************************************************** * Function: gtab_delcr * * Purpose: * * change all cr/lf chars in input text to spaces */ void gtab_delcr(LPSTR ptext) { LPSTR chp; if (ptext == NULL) { return; } for(chp = ptext; (chp = strchr(chp, '\r')) != NULL; ) { *chp = ' '; } for(chp = ptext; (chp = strchr(chp, '\n')) != NULL; ) { *chp = ' '; } } /*************************************************************************** * Function: gtab_updateline * * Purpose: * * Ensures that all visible cells in the given line have valid * text and property contents. loop through the cells, picking out * contiguous blocks of visible, invalid cells and call * gtab_updatecontig to update these from the owner window. */ void gtab_updateline(HWND hwnd, lpTable ptab, int line) { lpCellPos ppos; int cell1, cellcount; lpLineData pline; lpCellData cd; int i; pline = &ptab->pdata[line]; cell1 = 0; cellcount = 0; for (i = 0; i < ptab->hdr.ncols; i++) { ppos = &ptab->pcellpos[i]; cd = &pline->pdata[i]; if (ppos->clipstart < ppos->clipend) { if ((cd->flags & CELL_VALID) == 0) { /* add a cell to the list to be updated*/ if (cellcount++ == 0) { cell1 = i; } } else { /* this cell already valid - so end of * a contig block. if the contig * block just ended contained cells to update, * do it now */ if (cellcount > 0) { gtab_updatecontig(hwnd, ptab, line, cell1, cellcount); } cellcount = 0; } } /* cell not visible - end of a contig block. If it was a * non-empty contig block, then update it now. */ if (cellcount > 0) { gtab_updatecontig(hwnd, ptab, line, cell1, cellcount); cellcount = 0; } } if (cellcount > 0) { gtab_updatecontig(hwnd, ptab, line, cell1, cellcount); cellcount = 0; } } /*************************************************************************** * Function: gtab_updatecontig * * Purpose: * * Updates a contiguous block of invalid cells by calling the owner window */ void gtab_updatecontig(HWND hwnd, lpTable ptab, int line, int cell1, int count) { lpLineData pline; lpCellData cd; CellDataList list; lpProps colprops; int i; pline = &ptab->pdata[line]; cd = &pline->pdata[cell1]; list.id = ptab->hdr.id; list.row = gtab_linetorow(hwnd, ptab, line); list.startcell = cell1; list.ncells = count; list.plist = cd; /* clear out prop flags */ for (i = 0; i < count; i++) { cd[i].props.valid = 0; if (cd[i].nchars > 0) { cd[i].ptext[0] = '\0'; } } if (list.row < ptab->hdr.nrows) { gtab_sendtq(hwnd, TQ_GETDATA, (long) (LPSTR) &list); } /* for each cell, mark valid and set properties */ for (i = 0; i < count; i++) { cd[i].flags |= CELL_VALID; gtab_delcr(cd[i].ptext); /* fetch properties from hdr and colhdr */ colprops = &ptab->pcolhdr[i + cell1].props; if (!(cd[i].props.valid & P_FCOLOUR)) { if (colprops->valid & P_FCOLOUR) { cd[i].props.valid |= P_FCOLOUR; cd[i].props.forecolour = colprops->forecolour; } else if (ptab->hdr.props.valid & P_FCOLOUR) { cd[i].props.valid |= P_FCOLOUR; cd[i].props.forecolour = ptab->hdr.props.forecolour; } } if (!(cd[i].props.valid & P_BCOLOUR)) { if (colprops->valid & P_BCOLOUR) { cd[i].props.valid |= P_BCOLOUR; cd[i].props.backcolour = colprops->backcolour; } else if (ptab->hdr.props.valid & P_BCOLOUR) { cd[i].props.valid |= P_BCOLOUR; cd[i].props.backcolour = ptab->hdr.props.backcolour; } } if (!(cd[i].props.valid & P_FONT)) { if (colprops->valid & P_FONT) { cd[i].props.valid |= P_FONT; cd[i].props.hFont = colprops->hFont; } else if (ptab->hdr.props.valid & P_FONT) { cd[i].props.valid |= P_FONT; cd[i].props.hFont = ptab->hdr.props.hFont; } } if (!(cd[i].props.valid & P_ALIGN)) { if (colprops->valid & P_ALIGN) { cd[i].props.valid |= P_ALIGN; cd[i].props.alignment = colprops->alignment; } else if (ptab->hdr.props.valid & P_ALIGN) { cd[i].props.valid |= P_ALIGN; cd[i].props.alignment = ptab->hdr.props.alignment; } } if (!(cd[i].props.valid & P_BOX)) { if (colprops->valid & P_BOX) { cd[i].props.valid |= P_BOX; cd[i].props.box = colprops->box; } else if (ptab->hdr.props.valid & P_BOX) { cd[i].props.valid |= P_BOX; cd[i].props.box = ptab->hdr.props.box; } } /* you can't set width/height per cell - this * is ignored at cell level. */ } } /*************************************************************************** * Function: gtab_boxcell * * Purpose: * * Draws box around a cell in a table. */ void gtab_boxcell(HWND hwnd, HDC hdc, LPRECT rcp, LPRECT pclip, UINT boxmode) { if (boxmode & P_BOXTOP) { MoveToEx(hdc, max(rcp->left, pclip->left), max(rcp->top, pclip->top), NULL); LineTo(hdc, min(rcp->right, pclip->right), max(rcp->top, pclip->top)); } if (boxmode & P_BOXBOTTOM) { MoveToEx(hdc, max(rcp->left, pclip->left), min(rcp->bottom, pclip->bottom), NULL); LineTo(hdc, min(rcp->right, pclip->right), min(rcp->bottom, pclip->bottom)); } if (boxmode & P_BOXLEFT) { MoveToEx(hdc, max(rcp->left, pclip->left), max(rcp->top, pclip->top), NULL); MoveToEx(hdc, max(rcp->left, pclip->left), min(rcp->bottom, pclip->bottom), NULL); } if (boxmode & P_BOXRIGHT) { MoveToEx(hdc, min(rcp->right, pclip->right), max(rcp->top, pclip->top), NULL); LineTo(hdc, min(rcp->right, pclip->right), min(rcp->bottom, pclip->bottom)); } } /*************************************************************************** * Function: gtab_paintcell * * Purpose: * * Paints a cell. */ void gtab_paintcell(HWND hwnd, HDC hdc, lpTable ptab, int line, int cell) { lpLineData pline; lpCellData cd; lpCellPos ppos; RECT rc, rcbox; int cx, x, y, tabwidth; UINT align; LPSTR chp, tabp; DWORD fcol, bkcol; HFONT hfont; TEXTMETRIC tm; HBRUSH hbr; /* init pointers to cell text and properties */ pline = &ptab->pdata[line]; cd = &pline->pdata[cell]; ppos = &ptab->pcellpos[cell]; /* clip all output to this rectangle */ rc.top = pline->linepos.clipstart; rc.bottom = pline->linepos.clipend; rc.left = ppos->clipstart; rc.right = ppos->clipend; /* check cell properties and colours */ if (cd->props.valid & P_ALIGN) { align = cd->props.alignment; } else { align = P_LEFT; } if (cd->props.valid & P_FONT) { hfont = SelectObject(hdc, cd->props.hFont); GetTextMetrics(hdc, &tm); tabwidth = tm.tmAveCharWidth * 8; } else { tabwidth = ptab->avewidth * 8; } /* set colours if not default */ if (cd->props.valid & P_FCOLOUR) { fcol = SetTextColor(hdc, cd->props.forecolour); } if (cd->props.valid & P_BCOLOUR) { /* there is a non-default background colour. * create a brush and fill the entire cell with it */ hbr = CreateSolidBrush(cd->props.backcolour); FillRect(hdc, &rc, hbr); DeleteObject(hbr); /* also set colour as background colour for the text */ bkcol = SetBkColor(hdc, cd->props.backcolour); } /* calc offset of text within cell for right-align or centering */ if (align == P_LEFT) { cx = ptab->avewidth/2; } else { if (cd->ptext == NULL) { cx = 0; } else { cx = LOWORD(GetTextExtent(hdc, cd->ptext, lstrlen(cd->ptext))); } if (align == P_CENTRE) { cx = (ppos->size - cx) / 2; } else { cx = ppos->size - cx - (ptab->avewidth/2); } } cx += ppos->start; /* expand tabs on output */ x = 0; y = pline->linepos.start; for (chp = cd->ptext; ((chp != NULL) && ((tabp = strchr(chp, '\t')) != NULL)); ) { /* perform output upto tab char */ ExtTextOut(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, tabp-chp, NULL); /* advance past the tab */ x += LOWORD(GetTextExtent(hdc, chp, tabp - chp)); x = ( (x + tabwidth) / tabwidth) * tabwidth; chp = ++tabp; } /*no more tabs - output rest of string */ if (chp != NULL) { ExtTextOut(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, lstrlen(chp), NULL); } /* reset colours to original if not default */ if (cd->props.valid & P_FCOLOUR) { SetTextColor(hdc, fcol); } if (cd->props.valid & P_BCOLOUR) { SetBkColor(hdc, bkcol); } if (cd->props.valid & P_FONT) { SelectObject(hdc, hfont); } /* now box cell if marked */ if (cd->props.valid & P_BOX) { if (cd->props.box != 0) { rcbox.top = pline->linepos.start; rcbox.bottom = rcbox.top + pline->linepos.size; rcbox.left = ppos->start; rcbox.right = ppos->start + ppos->size; gtab_boxcell(hwnd, hdc, &rcbox, &rc, cd->props.box); } } } /*************************************************************************** * Function: gtab_paint * * Purpose: * * Fetch and paint the specified line */ void gtab_paint(HWND hwnd, HDC hdc, lpTable ptab, int line) { lpCellPos ppos; int i; gtab_updateline(hwnd, ptab, line); for (i = 0; i < ptab->hdr.ncols; i++) { ppos = &ptab->pcellpos[i]; if (ppos->clipstart < ppos->clipend) { gtab_paintcell(hwnd, hdc, ptab, line, i); } } } /*************************************************************************** * Function: gtab_vsep * * Purpose: * */ void gtab_vsep(HWND hwnd, lpTable ptab, HDC hdc) { int x; RECT rc; if (ptab->hdr.fixedcols < 1) { return; } x = ptab->pcellpos[ptab->hdr.fixedcols - 1].clipend+1; GetClientRect(hwnd, &rc); MoveToEx(hdc, x, rc.top, NULL); LineTo(hdc, x, rc.bottom); } /*************************************************************************** * Function: gtab_hsep * * Purpose: */ void gtab_hsep(HWND hwnd, lpTable ptab, HDC hdc) { int y; RECT rc; if (ptab->hdr.fixedrows < 1) { return; } y = ptab->rowheight * ptab->hdr.fixedrows; GetClientRect(hwnd, &rc); MoveToEx(hdc, rc.left, y-1, NULL); LineTo(hdc, rc.right, y-1); } /*************************************************************************** * Function: gtab_drawverline * * Purpose: * * Draw in (inverting) the dotted selection lines for tracking a col width */ void gtab_drawvertline(HWND hwnd, lpTable ptab) { RECT rc; HDC hdc; HPEN hpen; hdc = GetDC(hwnd); SetROP2(hdc, R2_XORPEN); hpen = SelectObject(hdc, hpenDotted); GetClientRect(hwnd, &rc); MoveToEx(hdc, ptab->trackline1, rc.top, NULL); LineTo(hdc, ptab->trackline1, rc.bottom); if (ptab->trackline2 != -1) { MoveToEx(hdc, ptab->trackline2, rc.top, NULL); LineTo(hdc, ptab->trackline2, rc.bottom); } SelectObject(hdc, hpen); ReleaseDC(hwnd, hdc); } /*************************************************************************** * Function: gtab_invertsel * * Purpose: * * Mark the selected line, if visible, in the style chosen by the * client app. This can be TM_SOLID, meaning an inversion of * the whole selected area or TM_FOCUS, meaning, inversion of the first * cell, and then a dotted focus rectangle for the rest. * * This function inverts either style, and so will turn the selection * both on and off. */ void gtab_invertsel(HWND hwnd, lpTable ptab, HDC hdc_in) { HDC hdc; int line; RECT rc; int lastcell; /* is row visible on screen ? */ line = gtab_rowtoline(hwnd, ptab, ptab->select.startrow); if (line < 0) { return; } /* selection mode includes a flag TM_FOCUS indicating we should * use a focus rect instead of the traditional inversion for * selections in this table. This interferes with multiple backgrnd * colours less. However we still do inversion for fixedcols. */ lastcell = (int)(ptab->select.startcell + ptab->select.ncells - 1); rc.top = ptab->pdata[line].linepos.clipstart; rc.bottom = ptab->pdata[line].linepos.clipend; /* * invert the whole area for TM_SOLID or just the first * cell for TM_FOCUS */ rc.left = ptab->pcellpos[ptab->select.startcell].clipstart; if (ptab->hdr.selectmode & TM_FOCUS) { rc.right = ptab->pcellpos[ptab->select.startcell].clipend; }else { rc.right = ptab->pcellpos[lastcell].clipend; } if (hdc_in == NULL) { hdc = GetDC(hwnd); } else { hdc = hdc_in; } InvertRect(hdc, &rc); /* * draw focus rectangle around remaining cells on this line, if there * are any */ if (ptab->hdr.selectmode & TM_FOCUS) { if (ptab->select.ncells > 1) { rc.left = ptab->pcellpos[ptab->select.startcell+1].clipstart; rc.right = ptab->pcellpos[lastcell].clipend; DrawFocusRect(hdc, &rc); } } if (hdc_in == NULL) { ReleaseDC(hwnd, hdc); } }