From fda8f2fbae32645fc52af8319cfea4b93da4f47b Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Thu, 14 Jun 2018 21:31:45 -0400 Subject: [PATCH] Started a separate file just for drawing tables; integrated text. --- windows/table.cpp | 66 ----------- windows/tabledraw.cpp | 251 ++++++++++++++++++++++++++++++++++++++++++ windows/tabletext.cpp | 62 ----------- 3 files changed, 251 insertions(+), 128 deletions(-) create mode 100644 windows/tabledraw.cpp diff --git a/windows/table.cpp b/windows/table.cpp index ea3257de..19bacd4b 100644 --- a/windows/table.cpp +++ b/windows/table.cpp @@ -128,72 +128,6 @@ COLORREF uiprivTableBlendedColorFromModel(uiTable *t, NMLVCUSTOMDRAW *nm, int mo return blend(nm->clrTextBk, r, g, b, a); } -static HRESULT fillSubitemDrawParams(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivSubitemDrawParams *dp) -{ - LRESULT state; - HWND header; - RECT r; - HRESULT hr; - - // note: nm->nmcd.uItemState CDIS_SELECTED is unreliable for the listview configuration we have - state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); - dp->selected = (state & LVIS_SELECTED) != 0; - - header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); - dp->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); - - if (nm->iSubItem == 0) { - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_BOUNDS; - if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { - logLastError(L"LVM_GETITEMRECT LVIR_BOUNDS"); - return E_FAIL; - } - dp->bounds = r; - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_ICON; - if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { - logLastError(L"LVM_GETITEMRECT LVIR_ICON"); - return E_FAIL; - } - dp->icon = r; - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_LABEL; - if (SendMessageW(t->hwnd, LVM_GETITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == FALSE) { - logLastError(L"LVM_GETITEMRECT LVIR_LABEL"); - return E_FAIL; - } - dp->label = r; - return S_OK; - } - - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_BOUNDS; - r.top = nm->iSubItem; - if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { - logLastError(L"LVM_GETSUBITEMRECT LVIR_BOUNDS"); - return E_FAIL; - } - dp->bounds = r; - ZeroMemory(&r, sizeof (RECT)); - r.left = LVIR_ICON; - r.top = nm->iSubItem; - if (SendMessageW(t->hwnd, LVM_GETSUBITEMRECT, nm->nmcd.dwItemSpec, (LPARAM) (&r)) == 0) { - logLastError(L"LVM_GETSUBITEMRECT LVIR_ICON"); - return E_FAIL; - } - dp->icon = r; - // LVIR_LABEL is treated as LVIR_BOUNDS for LVM_GETSUBITEMRECT, but it doesn't matter because the label rect is uses isn't what we want anyway - // there's a hardocded 2-logical unit gap between the icon and text for subitems, AND the text starts being drawn (in the background) one bitmap margin to the right of that - // with normal items, there's no gap, and only the 2-logical unit gap after the background starts (TODO confirm this part) - // let's copy that to look nicer, even if it's not "accurate" - // TODO check against accessibility - dp->label = dp->bounds; - // because we want the 2 extra logical units to be included with the background, we don't include them here - dp->label.left = dp->icon.right; - return S_OK; -} - static LRESULT onNM_CUSTOMDRAW(uiTable *t, NMLVCUSTOMDRAW *nm) { uiprivTableColumnParams *p; diff --git a/windows/tabledraw.cpp b/windows/tabledraw.cpp new file mode 100644 index 00000000..3036ac30 --- /dev/null +++ b/windows/tabledraw.cpp @@ -0,0 +1,251 @@ +// 14 june 2018 +#include "uipriv_windows.hpp" +#include "table.hpp" + +struct drawState { + uiTable *t; + uiTableModel *m; + uiprivTableColumnParams *p; + + HDC dc; + int iItem; + int iSubItem; + BOOL hasText; + BOOL hasImage; + BOOL selected; + BOOL focused; + + RECT itemBounds; + RECT itemIcon; + RECT itemText; + RECT subitemBounds; + RECT subitemIcon; + RECT subitemText; + + COLORREF bgColor; + HBRUSH bgBrush; + BOOL freeBgBrush; + COLORREF textColor; + HBRUSH textBrush; + BOOL freeTextBrush; + + LRESULT bitmapMargins; + int cxIcon; + int cyIcon; + + RECT realTextRect; + RECT focusRect; +}; + +static HRESULT computeAndDrawTextRect(struct drawState *s) +{ + RECT r; + + r = s->subitemText; + if (!s->hasText && !s->hasImage) + r = s->subitemBounds; + + if (FillRect(s->dc, &r, s->bgBrush) == 0) { + logLastError(L"FillRect()"); + return E_FAIL; + } + UnionRect(&(s->focusRect), &(s->focusRect), &r); + + s->realTextRect = r; + // TODO confirm whether this really happens on column 0 as well + if (s->hasImage && s->iSubItem != 0) + // Normally there's this many hard-coded logical units + // of blank space, followed by the background, followed + // by a bitmap margin's worth of space. This looks bad, + // so we overrule that to start the background immediately + // and the text after the hard-coded amount. + s->realTextRect.left += 2; + else if (s->iSubItem != 0) + // In the case of subitem text without an image, we draw + // text one bitmap margin away from the left edge. + s->realTextRect.left += s->bitmapMargin; + return S_OK; +} + +static HRESULT drawTextPart(struct drawState *s) +{ + COLORREF prevText; + int prevMode; + RECT r; + uiTableData *data; + WCHAR *wstr; + + if (!s->hasText) + return S_OK; + + prevText = SetTextColor(s->dc, s->textColor); + if (prevText == CLR_INVALID) { + logLastError(L"SetTextColor()"); + return E_FAIL; + } + prevMode = SetBkMode(s->dc, TRANSPARENT); + if (prevMode == 0) { + logLastError(L"SetBkMode()"); + return E_FAIL; + } + + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, s->p->textModelColumn); + wstr = toUTF16(uiTableDataString(data)); + uiFreeTableData(data); + // These flags are a menagerie of flags from various sources: + // guessing, the Windows 2000 source leak, various custom + // draw examples on the web, etc. + // TODO find the real correct flags + if (DrawTextW(s->dc, wstr, -1, &(s->realTextRect), DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { + uiprivFree(wstr); + logLastError(L"DrawTextW()"); + return E_FAIL; + } + uiprivFree(wstr); + + // TODO decide once and for all what to compare to here and with SelectObject() + if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) { + logLastError(L"SetBkMode() prev"); + return E_FAIL; + } + if (SetTextColor(nm->nmcd.hdc, prevText) != color) { + logLastError(L"SetTextColor() prev"); + return E_FAIL; + } + return S_OK; +} + +static HRESULT freeDrawState(struct drawState *s) +{ + HRESULT hr, hrret; + + hrret = S_OK; + if (p->freeTextBrush) { + if (DeleteObject(p->textBrush) == 0) { + logLastError(L"DeleteObject()"); + hrret = E_FAIL; + // continue cleaning up anyway + } + p->freeTextBrush = NO; + } + if (p->freeBgBrush) { + if (DeleteObject(p->bgBrush) == 0) { + logLastError(L"DeleteObject()"); + hrret = E_FAIL; + // continue cleaning up anyway + } + p->freeBgBrush = NO; + } + return hrret; +} + +static HRESULT itemRect(HRESULT hr, uiTable *t, UINT uMsg, WPARAM wParam, LONG left, LONG top, LRESULT bad, RECT *r) +{ + if (hr != S_OK) + return hr; + ZeroMemory(r, sizeof (RECT)); + r->left = left; + r->top = top; + if (SendMessageW(t->hwnd, uMsg, wParam, (LPARAM) r) == bad) { + logLastError(L"itemRect() message"); + return E_FAIL; + } + return S_OK; +} + +static HRESULT fillDrawState(struct drawState *s, uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p) +{ + LRESULT state; + HWND header; + HRESULT hr; + + ZeroMemory(s, sizeof (struct drawState)); + s->t = t; + s->m = t->model; + s->p = p; + + s->dc = nm->nmcd.hdc; + s->iItem = nm->nmcd.dwItemSpec; + s->iSubItem = nm->iSubItem; + s->hasText = p->textModelColumn != -1; + s->hasImage = (p->imageModelColumn != -1) || (p->checkboxModelColumn != -1); + // nm->nmcd.uItemState CDIS_SELECTED is unreliable for the + // listview configuration we have, so we must do this. + state = SendMessageW(t->hwnd, LVM_GETITEMSTATE, nm->nmcd.dwItemSpec, LVIS_SELECTED); + dp->selected = (state & LVIS_SELECTED) != 0; + dp->focused = (nm->nmcd.uiTemState & CDIS_FOCUSED) != 0; + + hr = itemRect(S_OK, t, LVM_GETITEMRECT, LVIR_BOUNDS, + 0, &(s->itemBounds)); + hr = itemRect(hr, t, LVM_GETITEMRECT, LVIR_ICON, + 0, &(s->itemIcon)); + hr = itemRect(hr, t, LVM_GETITEMRECT, LVIR_LABEL, + 0, &(s->itemLabel)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, LVIR_BOUNDS, + s->iSubItem, &(s->subitemBounds)); + hr = itemRect(hr, t, LVM_GETSUBITEMRECT, LVIR_ICON, + s->iSubItem, &(s->subitemIcon)); + if (hr != S_OK) + goto fail; + // LVM_GETSUBITEMRECT treats LVIR_LABEL as the same as + // LVIR_BOUNDS, so we can't use that directly. Instead, let's + // assume the text is immediately after the icon. The correct + // rect will be determined by computeAndDrawTextRect() + // above. + s->subitemLabel = s->subitemBounds; + s->subitemLabel.left = s->subitemIcon.right; + + if (s->selected) { + s->bgColor = GetSysColor(COLOR_HIGHLIGHT); + s->bgBrush = GetSysColorBrush(COLOR_HIGHLIGHT); + s->textColor = GetSysColor(COLOR_HIGHLIGHTTEXT); + s->textBrush = GetSysColorBrush(COLOR_HIGHLIGHTTEXT); + } else { + uiTableData *data; + + s->bgColor = GetSysColor(COLOR_WINDOW); + s->bgBrush = GetSysColorBrush(COLOR_WINDOW); + if (t->backgroundColumn != -1) { + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, t->backgroundColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + s->bgColor = blend(s->bgColor, r, g, b, a); + s->bgBrush = CreateSolidBrush(s->bgBrush); + if (s->bgBrush == NULL) { + logLastError(L"CreateSolidBrush()"); + hr = E_FAIL; + goto fail; + } + s->freeBgBrush = TRUE; + } + } + s->textColor = GetSysColor(COLOR_WINDOWTEXT); + s->textBrush = GetSysColorBrush(COLOR_WINDOWTEXT); + if (p->textParams.ColorModelColumn != -1) { + data = (*(s->m->mh->CellValue))(s->m->mh, s->m, s->iItem, p->textParams.ColorModelColumn); + if (data != NULL) { + uiTableDataColor(data, &r, &g, &b, &a); + uiFreeTableData(data); + s->textColor = blend(s->bgColor, r, g, b, a); + s->textBrush = CreateSolidBrush(s->textColor); + if (s->textBrush == NULL) { + logLastError(L"CreateSolidBrush()"); + hr = E_FAIL; + goto fail; + } + s->freeTextBrush = TRUE; + } + } + } + + header = (HWND) SendMessageW(t->hwnd, LVM_GETHEADER, 0, 0); + s->bitmapMargin = SendMessageW(header, HDM_GETBITMAPMARGIN, 0, 0); + // TODO + + return S_OK; +fail: + // ignore the error; we need to return the one we got above + freeDrawState(s); + return hr; +} diff --git a/windows/tabletext.cpp b/windows/tabletext.cpp index 04a38b5f..d1feb336 100644 --- a/windows/tabletext.cpp +++ b/windows/tabletext.cpp @@ -27,65 +27,3 @@ HRESULT uiprivLVN_GETDISPINFOText(uiTable *t, NMLVDISPINFOW *nm, uiprivTableColu uiprivFree(wstr); return S_OK; } - -HRESULT uiprivNM_CUSTOMDRAWText(uiTable *t, NMLVCUSTOMDRAW *nm, uiprivTableColumnParams *p, uiprivSubitemDrawParams *dp) -{ - COLORREF color; - COLORREF prev; - int prevMode; - RECT r; - uiTableData *data; - WCHAR *wstr; - - if (p->textModelColumn == -1) - return S_OK; - - if (dp->selected) - color = GetSysColor(COLOR_HIGHLIGHTTEXT); - else if (p->textParams.ColorModelColumn != -1) - color = uiprivTableBlendedColorFromModel(t, nm, p->textParams.ColorModelColumn, COLOR_WINDOWTEXT); - else - color = GetSysColor(COLOR_WINDOWTEXT); - prev = SetTextColor(nm->nmcd.hdc, color); - if (prev == CLR_INVALID) { - logLastError(L"SetTextColor()"); - return E_FAIL; - } - prevMode = SetBkMode(nm->nmcd.hdc, TRANSPARENT); - if (prevMode == 0) { - logLastError(L"SetBkMode()"); - return E_FAIL; - } - - r = dp->label; - if (p->imageModelColumn != -1 || p->checkboxModelColumn != -1) - // text is actually drawn two logical units to the right of the beginning of the text rect - // TODO confirm this for the first column on both image and imageless cases - // TODO actually this whole thing is wrong for imageless columns - r.left += 2; - else - r.left = dp->bounds.left + dp->bitmapMargin; - - data = (*(t->model->mh->CellValue))(t->model->mh, t->model, nm->nmcd.dwItemSpec, p->textModelColumn); - wstr = toUTF16(uiTableDataString(data)); - uiFreeTableData(data); - // these flags are a menagerie of flags from various sources: guessing, the Windows 2000 source leak, various custom draw examples on the web, etc. - // TODO find the real correct flags - if (DrawTextW(nm->nmcd.hdc, wstr, -1, &r, DT_LEFT | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX | DT_EDITCONTROL) == 0) { - uiprivFree(wstr); - logLastError(L"DrawTextW()"); - return E_FAIL; - } - uiprivFree(wstr); - - // TODO decide once and for all what to compare to here and with SelectObject() - if (SetBkMode(nm->nmcd.hdc, prevMode) != TRANSPARENT) { - logLastError(L"SetBkMode() prev"); - return E_FAIL; - } - if (SetTextColor(nm->nmcd.hdc, prev) != color) { - logLastError(L"SetTextColor() prev"); - return E_FAIL; - } - return S_OK; -}