753 lines
18 KiB
C
753 lines
18 KiB
C
|
/*
|
||
|
* standard table class.
|
||
|
*
|
||
|
* paint functions.
|
||
|
*
|
||
|
* see table.h for interface description
|
||
|
*/
|
||
|
|
||
|
#include <string.h>
|
||
|
|
||
|
|
||
|
#include "windows.h"
|
||
|
#include "commdlg.h"
|
||
|
#include "gutils.h"
|
||
|
|
||
|
#include "table.h"
|
||
|
#include "tpriv.h"
|
||
|
|
||
|
#ifdef WIN32
|
||
|
|
||
|
int
|
||
|
GetTextExtent(HDC hdc, LPSTR text, INT len)
|
||
|
{
|
||
|
SIZE sz;
|
||
|
|
||
|
GetTextExtentPoint(hdc, text, len, &sz);
|
||
|
return(sz.cx);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void gtab_updatecontig(HWND hwnd, lpTable ptab, int line, int cell1, int count);
|
||
|
|
||
|
/* change all cr/lf chars in input text to nul chars (used to be spaces, not sure why) */
|
||
|
void gtab_delcr(LPSTR ptext)
|
||
|
{
|
||
|
LPSTR chp;
|
||
|
|
||
|
if (ptext == NULL) {
|
||
|
return;
|
||
|
}
|
||
|
for(chp = ptext; (chp = My_mbschr(chp, '\r')) != NULL; ) {
|
||
|
*chp = '\0';
|
||
|
}
|
||
|
for(chp = ptext; (chp = My_mbschr(chp, '\n')) != NULL; ) {
|
||
|
*chp = '\0';
|
||
|
}
|
||
|
}
|
||
|
void gtab_delcrW(LPWSTR pwzText)
|
||
|
{
|
||
|
LPWSTR pwch;
|
||
|
|
||
|
if (pwzText)
|
||
|
for(pwch = pwzText; *pwch; pwch++)
|
||
|
if (*pwch == '\r' || *pwch == '\n')
|
||
|
*pwch = 0;
|
||
|
}
|
||
|
|
||
|
/* ensure 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;
|
||
|
if (line < ptab->hdr.nrows) {
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* update 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;
|
||
|
COLORREF rgb;
|
||
|
|
||
|
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 */
|
||
|
rgb = GetSysColor(COLOR_WINDOW);
|
||
|
for (i = 0; i < count; i++) {
|
||
|
cd[i].props.valid = P_BCOLOUR;
|
||
|
cd[i].props.backcolour = rgb;
|
||
|
if (cd[i].nchars > 0) {
|
||
|
cd[i].ptext[0] = '\0';
|
||
|
if (cd[i].pwzText) {
|
||
|
cd[i].pwzText[0] = '\0';
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (list.row < ptab->hdr.nrows) {
|
||
|
gtab_sendtq(hwnd, TQ_GETDATA, (LPARAM) &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);
|
||
|
gtab_delcrW(cd[i].pwzText);
|
||
|
/* 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_FCOLOURWS)) {
|
||
|
if (colprops->valid & P_FCOLOURWS) {
|
||
|
cd[i].props.valid |= P_FCOLOURWS;
|
||
|
cd[i].props.forecolourws = colprops->forecolourws;
|
||
|
} else if (ptab->hdr.props.valid & P_FCOLOURWS) {
|
||
|
cd[i].props.valid |= P_FCOLOURWS;
|
||
|
cd[i].props.forecolourws =
|
||
|
ptab->hdr.props.forecolourws;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
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));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gtab_paintcell(HWND hwnd, HDC hdc, lpTable ptab, int line, int cell, BOOL show_whitespace)
|
||
|
{
|
||
|
lpLineData pline;
|
||
|
lpCellData cd;
|
||
|
lpCellPos ppos;
|
||
|
RECT rc, rcbox;
|
||
|
int cx, x, y;
|
||
|
UINT align;
|
||
|
LPSTR chp, tabp;
|
||
|
LPWSTR pwch, pwchBreak;
|
||
|
DWORD fcol, fcolOld, fcolws;
|
||
|
DWORD bkcol, bkcolOld;
|
||
|
HFONT hfont, hfontOld;
|
||
|
HBRUSH hbr;
|
||
|
char szCharSet[] = "\t ";
|
||
|
WCHAR wzCharSet[] = L"\t ";
|
||
|
char szSpaceReplace[] = { (char) 183, (char) 0 };
|
||
|
char szTabReplace[] = { (char) 187, (char) 0 };
|
||
|
int cxSpaceReplace;
|
||
|
int cxTabReplace;
|
||
|
SIZE size;
|
||
|
TEXTMETRIC tm;
|
||
|
int yOfs;
|
||
|
|
||
|
fcol = 0; bkcol = 0; /* eliminate spurious diagnostic, generate worse code */
|
||
|
hfont = 0; /* eliminate spurious diagnostic, generate worse code */
|
||
|
/* init pointers to cell text and properties */
|
||
|
pline = &ptab->pdata[line];
|
||
|
cd = &pline->pdata[cell];
|
||
|
ppos = &ptab->pcellpos[cell];
|
||
|
|
||
|
/* draw gutter */
|
||
|
rc.top = pline->linepos.clipstart;
|
||
|
rc.bottom = pline->linepos.clipend;
|
||
|
rc.left = (cell > 0) ? ptab->pcellpos[cell - 1].clipend : 0;
|
||
|
rc.right = ppos->clipstart;
|
||
|
if (cell > ptab->hdr.fixedcols && ptab->hdr.fixedcols < ptab->hdr.ncols) {
|
||
|
rc.left = max(rc.left, ptab->pcellpos[ptab->hdr.fixedcols].clipstart);
|
||
|
}
|
||
|
if (ptab->hdr.fixedcols > 0 && cell == ptab->hdr.fixedcols) {
|
||
|
rc.right--;
|
||
|
}
|
||
|
if (rc.right > rc.left) {
|
||
|
FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
|
||
|
}
|
||
|
|
||
|
/* 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) {
|
||
|
hfontOld = SelectObject(hdc, cd->props.hFont);
|
||
|
}
|
||
|
|
||
|
// get y offset to center text vertically within cell
|
||
|
GetTextMetrics(hdc, &tm);
|
||
|
yOfs = (rc.bottom - rc.top - tm.tmHeight) / 2;
|
||
|
|
||
|
/* set replacement chars and char widths */
|
||
|
cxSpaceReplace = GetTextExtent(hdc, szSpaceReplace, 1);
|
||
|
cxTabReplace = cxSpaceReplace * ptab->tabchars;
|
||
|
|
||
|
/* set colours if not default */
|
||
|
if (cd->props.valid & P_FCOLOUR) {
|
||
|
fcol = cd->props.forecolour;
|
||
|
fcolOld = SetTextColor(hdc, fcol);
|
||
|
}
|
||
|
if (cd->props.valid & P_FCOLOURWS) {
|
||
|
fcolws = cd->props.forecolourws;
|
||
|
}
|
||
|
else {
|
||
|
fcolws = fcol;
|
||
|
}
|
||
|
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);
|
||
|
if (hbr)
|
||
|
{
|
||
|
FillRect(hdc, &rc, hbr);
|
||
|
DeleteObject(hbr);
|
||
|
}
|
||
|
|
||
|
/* also set colour as background colour for the text */
|
||
|
bkcolOld = 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 {
|
||
|
cx = 0;
|
||
|
|
||
|
if (cd->pwzText) {
|
||
|
GetTextExtentPoint32W(hdc, cd->pwzText, wcslen(cd->pwzText), &size);
|
||
|
} else if (cd->ptext) {
|
||
|
GetTextExtentPoint32A(hdc, cd->ptext, lstrlen(cd->ptext), &size);
|
||
|
}
|
||
|
cx = size.cx;
|
||
|
|
||
|
if (align == P_CENTRE) {
|
||
|
cx = (ppos->size - cx) / 2;
|
||
|
} else {
|
||
|
cx = ppos->size - cx - (ptab->avewidth/2);
|
||
|
}
|
||
|
}
|
||
|
cx += ppos->start;
|
||
|
|
||
|
/* expand tabs on output and show whitespace on output */
|
||
|
x = 0;
|
||
|
y = pline->linepos.start + yOfs;
|
||
|
|
||
|
/* set search string for strpbrk fn;
|
||
|
don't search for space chars unless we're showing whitespace */
|
||
|
if (!show_whitespace)
|
||
|
{
|
||
|
szCharSet[1] = '\0';
|
||
|
wzCharSet[1] = '\0';
|
||
|
}
|
||
|
|
||
|
// determine the string to display (ansi/unicode). if we have a string
|
||
|
// and it's not empty, then loop and display it.
|
||
|
chp = cd->ptext;
|
||
|
pwch = cd->pwzText;
|
||
|
if (pwch ? *pwch : (chp && *chp))
|
||
|
{
|
||
|
while (TRUE)
|
||
|
{
|
||
|
if (pwch)
|
||
|
{
|
||
|
pwchBreak = wcspbrk(pwch, wzCharSet);
|
||
|
if (!pwchBreak)
|
||
|
pwchBreak = pwch + wcslen(pwch);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
tabp = My_mbspbrk(chp, szCharSet);
|
||
|
if (!tabp)
|
||
|
tabp = chp + lstrlen(chp);
|
||
|
}
|
||
|
|
||
|
/* perform output up to tab/space char */
|
||
|
if (pwch)
|
||
|
ExtTextOutW(hdc, x+cx, y, ETO_CLIPPED, &rc, pwch, (UINT)(pwchBreak-pwch), NULL);
|
||
|
else
|
||
|
ExtTextOutA(hdc, x+cx, y, ETO_CLIPPED, &rc, chp, (UINT)(tabp-chp), NULL);
|
||
|
|
||
|
/* advance past the tab */
|
||
|
if (pwch)
|
||
|
{
|
||
|
GetTextExtentPoint32W(hdc, pwch, (UINT)(pwchBreak - pwch), &size);
|
||
|
pwch = pwchBreak;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
GetTextExtentPoint32A(hdc, chp, (UINT)(tabp - chp), &size);
|
||
|
chp = tabp;
|
||
|
}
|
||
|
x += size.cx;
|
||
|
|
||
|
// bail if we hit null terminator
|
||
|
if (pwch ? !*pwch : !*chp)
|
||
|
break;
|
||
|
|
||
|
/* handle tab chars */
|
||
|
while (pwch ? (*pwch == '\t') : (*chp == '\t'))
|
||
|
{
|
||
|
/* print replacement char */
|
||
|
if (show_whitespace)
|
||
|
{
|
||
|
SetTextColor(hdc, fcolws);
|
||
|
ExtTextOut(hdc, x + cx, y, ETO_CLIPPED, &rc, szTabReplace, 1, NULL);
|
||
|
SetTextColor(hdc, fcol);
|
||
|
}
|
||
|
|
||
|
/* advance past the tab */
|
||
|
x += cxTabReplace - (x % cxTabReplace);
|
||
|
if (pwch)
|
||
|
pwch = ++pwchBreak;
|
||
|
else
|
||
|
chp = ++tabp;
|
||
|
}
|
||
|
|
||
|
/* handle space chars */
|
||
|
if (show_whitespace)
|
||
|
{
|
||
|
while (pwch ? (*pwch == ' ') : (*chp == ' '))
|
||
|
{
|
||
|
/* replace space char with visible char */
|
||
|
SetTextColor(hdc, fcolws);
|
||
|
ExtTextOut(hdc, x + cx, y, ETO_CLIPPED, &rc, szSpaceReplace, 1, NULL);
|
||
|
SetTextColor(hdc, fcol);
|
||
|
x += cxSpaceReplace;
|
||
|
if (pwch)
|
||
|
pwch = ++pwchBreak;
|
||
|
else
|
||
|
chp = ++tabp;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* reset colours to original if not default */
|
||
|
if (cd->props.valid & P_FCOLOUR) {
|
||
|
SetTextColor(hdc, fcolOld);
|
||
|
}
|
||
|
if (cd->props.valid & P_BCOLOUR) {
|
||
|
SetBkColor(hdc, bkcolOld);
|
||
|
}
|
||
|
if (cd->props.valid & P_FONT) {
|
||
|
SelectObject(hdc, hfontOld);
|
||
|
}
|
||
|
|
||
|
/* now box cell if marked */
|
||
|
if ((cd->props.valid & P_BOX)) {
|
||
|
if (cd->props.box != 0) {
|
||
|
// rcbox.top = pline->linepos.start;
|
||
|
rcbox.top = y;
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* fetch and paint the specified line */
|
||
|
void
|
||
|
gtab_paintline(HWND hwnd, HDC hdc, lpTable ptab, int line, BOOL show_whitespace)
|
||
|
{
|
||
|
lpCellPos ppos;
|
||
|
int i;
|
||
|
RECT rc;
|
||
|
if (line < ptab->hdr.nrows) {
|
||
|
GetClientRect(hwnd, &rc);
|
||
|
|
||
|
gtab_updateline(hwnd, ptab, line);
|
||
|
|
||
|
for (i = 0; i < ptab->hdr.ncols; i++) {
|
||
|
ppos = &ptab->pcellpos[i];
|
||
|
/* show whitespace iff the flag is set
|
||
|
and we're painting the main text column */
|
||
|
if (ppos->clipstart < ppos->clipend) {
|
||
|
gtab_paintcell(hwnd, hdc, ptab, line, i,
|
||
|
(show_whitespace && (i == 2)));
|
||
|
}
|
||
|
}
|
||
|
rc.top = ptab->pdata[line].linepos.clipstart;
|
||
|
rc.bottom = ptab->pdata[line].linepos.clipend;
|
||
|
rc.left = ppos->clipend;
|
||
|
FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void
|
||
|
gtab_paint(HWND hwnd)
|
||
|
{
|
||
|
PAINTSTRUCT ps;
|
||
|
HDC hDC = BeginPaint(hwnd, &ps);
|
||
|
int y, y2, i;
|
||
|
|
||
|
lpTable ptab = (lpTable) GetWindowLongPtr(hwnd, WL_TABLE);
|
||
|
if (!ptab || (ptab->hdr.nrows == 0)) {
|
||
|
FillRect(hDC, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
|
||
|
} else {
|
||
|
/* separator lines between fixed rows/columns
|
||
|
* (ie headers) and the rest - if enabled
|
||
|
*/
|
||
|
/* paint here first for good impression,
|
||
|
* and again after to clean up!!
|
||
|
*/
|
||
|
if (ptab->hdr.vseparator) {
|
||
|
gtab_vsep(hwnd, ptab, hDC);
|
||
|
}
|
||
|
if (ptab->hdr.hseparator) {
|
||
|
gtab_hsep(hwnd, ptab, hDC);
|
||
|
}
|
||
|
|
||
|
/* paint only the rows that need painting */
|
||
|
for (i = 0; i < ptab->nlines; i++) {
|
||
|
y = ptab->pdata[i].linepos.start;
|
||
|
y2 = y + ptab->pdata[i].linepos.size;
|
||
|
if ( (y <= ps.rcPaint.bottom) &&
|
||
|
(y2 >= ps.rcPaint.top)) {
|
||
|
gtab_paintline(hwnd, hDC, ptab, i, ptab->show_whitespace);
|
||
|
}
|
||
|
}
|
||
|
if (ptab->hdr.vseparator) {
|
||
|
gtab_vsep(hwnd, ptab, hDC);
|
||
|
}
|
||
|
if (ptab->hdr.hseparator) {
|
||
|
gtab_hsep(hwnd, ptab, hDC);
|
||
|
}
|
||
|
if (ptab->selvisible) {
|
||
|
gtab_invertsel(hwnd, ptab, hDC);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EndPaint(hwnd, &ps);
|
||
|
}
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/* 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);
|
||
|
if (hdc)
|
||
|
{
|
||
|
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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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 firstline, lastline;
|
||
|
long startrow, lastrow, toprow, bottomrow;
|
||
|
RECT rc;
|
||
|
int lastcell;
|
||
|
|
||
|
|
||
|
|
||
|
/* get the selection start and end rows ordered vertically */
|
||
|
if (ptab->select.nrows == 0) {
|
||
|
return;
|
||
|
} else if (ptab->select.nrows < 0) {
|
||
|
startrow = ptab->select.startrow + ptab->select.nrows + 1;
|
||
|
lastrow = ptab->select.startrow;
|
||
|
} else {
|
||
|
startrow = ptab->select.startrow;
|
||
|
lastrow = ptab->select.startrow + ptab->select.nrows -1;
|
||
|
}
|
||
|
|
||
|
/* is selected area (or part of it) visible on screen ? */
|
||
|
firstline = gtab_rowtoline(hwnd, ptab, startrow);
|
||
|
lastline = gtab_rowtoline(hwnd, ptab, lastrow);
|
||
|
|
||
|
|
||
|
if (firstline < 0) {
|
||
|
toprow = gtab_linetorow(hwnd, ptab,
|
||
|
ptab->hdr.fixedselectable ? 0: ptab->hdr.fixedrows);
|
||
|
if ((toprow >= startrow) &&
|
||
|
(toprow <= lastrow)) {
|
||
|
firstline = gtab_rowtoline(hwnd, ptab, toprow);
|
||
|
} else {
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
toprow = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (lastline < 0) {
|
||
|
bottomrow = gtab_linetorow(hwnd, ptab, ptab->nlines-1);
|
||
|
if ((bottomrow <= lastrow) &&
|
||
|
(bottomrow >=startrow)) {
|
||
|
lastline = gtab_rowtoline(hwnd, ptab, bottomrow);
|
||
|
} else {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
rc.top = ptab->pdata[firstline].linepos.clipstart;
|
||
|
rc.bottom = ptab->pdata[lastline].linepos.clipend;
|
||
|
|
||
|
|
||
|
|
||
|
/* 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);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* 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);
|
||
|
if (!hdc)
|
||
|
return;
|
||
|
} 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) {
|
||
|
/*
|
||
|
* now this is a real fudge. if we are drawing TM_FOCUS
|
||
|
* selection, and the real top line is off the top of the
|
||
|
* window, then the top of the focus rect will be drawn at
|
||
|
* the top of our window. If we then scroll up one line,
|
||
|
* a new focus rect will be drawn, but the old top of focus
|
||
|
* rect line will still be there as junk on the
|
||
|
* screen. To fix this, we have 2 choices: we undo the selection
|
||
|
* before every scroll (too slow) or we set the focus rect a little
|
||
|
* bigger if the real top line is off-window, so that the top line
|
||
|
* is clipped (as it should be). This latter is what we do here
|
||
|
*/
|
||
|
if (toprow > startrow) {
|
||
|
rc.top--;
|
||
|
}
|
||
|
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);
|
||
|
}
|
||
|
}
|