mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
889 lines
26 KiB
889 lines
26 KiB
#include "ctlspriv.h"
|
|
#include "treeview.h"
|
|
#include "image.h"
|
|
|
|
|
|
void NEAR TV_GetBackgroundBrush(PTREE pTree, HDC hdc)
|
|
{
|
|
|
|
#ifdef WIN32
|
|
if (pTree->style & WS_DISABLED)
|
|
pTree->hbrBk = FORWARD_WM_CTLCOLORSTATIC(pTree->hwndParent, hdc, pTree->hwnd, SendMessage);
|
|
else
|
|
pTree->hbrBk = FORWARD_WM_CTLCOLOREDIT(pTree->hwndParent, hdc, pTree->hwnd, SendMessage);
|
|
#else
|
|
pTree->hbrBk = FORWARD_WM_CTLCOLOR(pTree->hwndParent, hdc, pTree->hwnd,
|
|
(pTree->style & WS_DISABLED)? CTLCOLOR_STATIC : CTLCOLOR_EDIT,
|
|
SendMessage);
|
|
#endif
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Draws a horizontal or vertical dotted line from the given (x,y) location
|
|
// for the given length (c).
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NEAR TV_DrawDottedLine(HDC hdc, int x, int y, int c, BOOL fVert)
|
|
{
|
|
while (c > 0)
|
|
{
|
|
PatBlt(hdc, x, y, 1, 1, PATCOPY);
|
|
if (fVert)
|
|
y += 2;
|
|
else
|
|
x += 2;
|
|
c -= 2;
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Draws a plus or minus sign centered around the given (x,y) location and
|
|
// extending out from that location the given distance (c).
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NEAR TV_DrawPlusMinus(HDC hdc, int x, int y, int c, HBRUSH hbrSign, HBRUSH hbrBox, HBRUSH hbrBk, BOOL fPlus)
|
|
{
|
|
int n;
|
|
int p = (c * 7) / 10;
|
|
|
|
n = p * 2 + 1;
|
|
|
|
SelectObject(hdc, hbrSign);
|
|
|
|
if (p >= 5)
|
|
{
|
|
PatBlt(hdc, x - p, y - 1, n, 3, PATCOPY);
|
|
if (fPlus)
|
|
PatBlt(hdc, x - 1, y - p, 3, n, PATCOPY);
|
|
|
|
SelectObject(hdc, hbrBk);
|
|
p--;
|
|
n -= 2;
|
|
}
|
|
|
|
PatBlt(hdc, x - p, y, n, 1, PATCOPY);
|
|
if (fPlus)
|
|
PatBlt(hdc, x, y - p, 1, n, PATCOPY);
|
|
|
|
n = c * 2 + 1;
|
|
|
|
SelectObject(hdc, hbrBox);
|
|
|
|
PatBlt(hdc, x - c, y - c, n, 1, PATCOPY);
|
|
PatBlt(hdc, x - c, y - c, 1, n, PATCOPY);
|
|
PatBlt(hdc, x - c, y + c, n, 1, PATCOPY);
|
|
PatBlt(hdc, x + c, y - c, 1, n, PATCOPY);
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Create the bitmaps for the indent area of the tree as follows
|
|
// if fHasLines && fHasButtons --> 7 bitmaps
|
|
// if fHasLines && !fHasButtons --> 3 bitmaps
|
|
// if !fHasLines && fHasButtons --> 2 bitmaps
|
|
//
|
|
// sets hStartBmp, hBmp, hdcBits
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NEAR TV_CreateIndentBmps(PTREE pTree)
|
|
{
|
|
int cnt;
|
|
RECT rc;
|
|
HBRUSH hbrOld;
|
|
int xMid, yMid;
|
|
int x, c;
|
|
HBITMAP hBmpOld;
|
|
HDC hdc;
|
|
|
|
if (pTree->fRedraw)
|
|
InvalidateRect(pTree->hwnd, NULL, TRUE);
|
|
|
|
if (pTree->style & TVS_HASLINES)
|
|
{
|
|
if (pTree->style & TVS_HASBUTTONS)
|
|
cnt = 7; // | |- L |+ L+ |- L-
|
|
else
|
|
cnt = 3; // | |- L
|
|
|
|
if (pTree->style & TVS_LINESATROOT) {
|
|
if (pTree->style & TVS_HASBUTTONS)
|
|
cnt += 3; // - -+ --
|
|
else
|
|
cnt += 1; // -
|
|
}
|
|
}
|
|
else if (pTree->style & TVS_HASBUTTONS)
|
|
cnt = 2;
|
|
else
|
|
return;
|
|
|
|
if (!pTree->hdcBits)
|
|
pTree->hdcBits = CreateCompatibleDC(NULL);
|
|
|
|
hdc = pTree->hdcBits;
|
|
|
|
// Get a new background brush, just like an Edit does.
|
|
|
|
TV_GetBackgroundBrush(pTree, hdc);
|
|
|
|
hBmpOld = pTree->hBmp;
|
|
pTree->hBmp = CreateColorBitmap(cnt * pTree->cxIndent, pTree->cyItem);
|
|
if (hBmpOld) {
|
|
SelectObject(hdc, pTree->hBmp);
|
|
DeleteObject(hBmpOld);
|
|
} else
|
|
pTree->hStartBmp = SelectObject(hdc, pTree->hBmp);
|
|
|
|
hbrOld = SelectObject(hdc, g_hbrGrayText);
|
|
|
|
rc.top = 0;
|
|
rc.left = 0;
|
|
rc.right = cnt * pTree->cxIndent;
|
|
rc.bottom = pTree->cyItem;
|
|
|
|
FillRect(hdc, &rc, pTree->hbrBk);
|
|
x = 0;
|
|
|
|
if (pTree->hImageList)
|
|
xMid = (pTree->cxImage - MAGIC_INDENT) / 2;
|
|
else
|
|
xMid = pTree->cxIndent / 2;
|
|
|
|
yMid = ((pTree->cyItem / 2) + 1) & ~1;
|
|
|
|
c = (min(xMid, yMid)) / 2;
|
|
|
|
if (pTree->style & TVS_HASLINES)
|
|
{
|
|
TV_DrawDottedLine(hdc, x + xMid, 0, pTree->cyItem, TRUE);
|
|
x += pTree->cxIndent;
|
|
|
|
TV_DrawDottedLine(hdc, x + xMid, 0, pTree->cyItem, TRUE);
|
|
TV_DrawDottedLine(hdc, x + xMid, yMid, pTree->cxIndent - xMid, FALSE);
|
|
x += pTree->cxIndent;
|
|
|
|
TV_DrawDottedLine(hdc, x + xMid, 0, yMid, TRUE);
|
|
TV_DrawDottedLine(hdc, x + xMid, yMid, pTree->cxIndent - xMid, FALSE);
|
|
x += pTree->cxIndent;
|
|
}
|
|
|
|
if (pTree->style & TVS_HASBUTTONS)
|
|
{
|
|
BOOL fPlus = TRUE;
|
|
|
|
x += xMid;
|
|
|
|
doDrawPlusMinus:
|
|
TV_DrawPlusMinus(hdc, x, yMid, c, g_hbrWindowText, g_hbrGrayText, pTree->hbrBk, fPlus);
|
|
|
|
if (pTree->style & TVS_HASLINES)
|
|
{
|
|
TV_DrawDottedLine(hdc, x, 0, yMid - c, TRUE);
|
|
TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
|
|
TV_DrawDottedLine(hdc, x, yMid + c, yMid - c, TRUE);
|
|
|
|
x += pTree->cxIndent;
|
|
|
|
TV_DrawPlusMinus(hdc, x, yMid, c, g_hbrWindowText, g_hbrGrayText, pTree->hbrBk, fPlus);
|
|
|
|
TV_DrawDottedLine(hdc, x, 0, yMid - c, TRUE);
|
|
TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
|
|
}
|
|
|
|
x += pTree->cxIndent;
|
|
|
|
if (fPlus)
|
|
{
|
|
fPlus = FALSE;
|
|
goto doDrawPlusMinus;
|
|
}
|
|
x -= xMid;
|
|
}
|
|
|
|
if (pTree->style & TVS_LINESATROOT) {
|
|
|
|
// -
|
|
TV_DrawDottedLine(hdc, x + xMid, yMid, pTree->cxIndent - xMid, FALSE);
|
|
x += pTree->cxIndent;
|
|
|
|
if (pTree->style & TVS_HASBUTTONS) {
|
|
x += xMid;
|
|
TV_DrawPlusMinus(hdc, x, yMid, c, g_hbrWindowText, g_hbrGrayText, pTree->hbrBk, TRUE);
|
|
TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
|
|
x += pTree->cxIndent;
|
|
|
|
TV_DrawPlusMinus(hdc, x, yMid, c, g_hbrWindowText, g_hbrGrayText, pTree->hbrBk, FALSE);
|
|
TV_DrawDottedLine(hdc, x + c, yMid, pTree->cxIndent - xMid - c, FALSE);
|
|
// uncomment if there's more to be added
|
|
//x += pTree->cxIndent - xMid;
|
|
|
|
}
|
|
}
|
|
|
|
if (hbrOld)
|
|
SelectObject(pTree->hdcBits, hbrOld);
|
|
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// fills in a TV_ITEM structure based by coying data from the item or
|
|
// by calling the callback to get it.
|
|
//
|
|
// in:
|
|
// hItem item to get TV_ITEM struct for
|
|
// mask which bits of the TV_ITEM struct you want (TVIF_ flags)
|
|
// out:
|
|
// lpItem TV_ITEM filled in
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NEAR TV_GetItem(PTREE pTree, HTREEITEM hItem, UINT mask, LPTV_ITEM lpItem)
|
|
{
|
|
TV_DISPINFO nm;
|
|
|
|
if (!hItem || !lpItem)
|
|
return;
|
|
|
|
nm.item.mask = 0;
|
|
|
|
// We need to check the mask to see if lpItem->pszText is valid
|
|
if (mask & TVIF_TEXT) {
|
|
if (hItem->lpstr == LPSTR_TEXTCALLBACK) {
|
|
nm.item.mask |= TVIF_TEXT;
|
|
// caller had to fill in pszText and cchTextMax with valid data
|
|
nm.item.pszText = lpItem->pszText;
|
|
nm.item.cchTextMax = lpItem->cchTextMax;
|
|
nm.item.pszText[0] = 0;
|
|
} else {
|
|
Assert(hItem->lpstr);
|
|
// we could do this but this is dangerous (when responding
|
|
// to TVM_GETITEM we would be giving the app a pointer to our data)
|
|
// lpItem->pszText = hItem->lpstr;
|
|
lstrcpyn(lpItem->pszText, hItem->lpstr, lpItem->cchTextMax);
|
|
}
|
|
}
|
|
|
|
if (mask & TVIF_IMAGE) {
|
|
if (hItem->iImage == (WORD)I_IMAGECALLBACK)
|
|
nm.item.mask |= TVIF_IMAGE;
|
|
else
|
|
lpItem->iImage = hItem->iImage;
|
|
}
|
|
|
|
if (mask & TVIF_SELECTEDIMAGE) {
|
|
if (hItem->iSelectedImage == (WORD)I_IMAGECALLBACK)
|
|
nm.item.mask |= TVIF_SELECTEDIMAGE;
|
|
else
|
|
lpItem->iSelectedImage = hItem->iSelectedImage;
|
|
}
|
|
|
|
if (mask & TVIF_CHILDREN) {
|
|
switch (hItem->fKids) {
|
|
case KIDS_COMPUTE:
|
|
lpItem->cChildren = hItem->hKids ? 1 : 0;// the actual count doesn't matter
|
|
break;
|
|
|
|
case KIDS_FORCE_YES:
|
|
lpItem->cChildren = 1;// the actual count doesn't matter
|
|
break;
|
|
|
|
case KIDS_FORCE_NO:
|
|
lpItem->cChildren = 0;
|
|
break;
|
|
|
|
case KIDS_CALLBACK:
|
|
nm.item.mask |= TVIF_CHILDREN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// copy out constant parameters (and prepare for callback)
|
|
lpItem->state = nm.item.state = hItem->state;
|
|
lpItem->lParam = nm.item.lParam = hItem->lParam;
|
|
|
|
// any items need to be filled in by callback?
|
|
if (nm.item.mask & (TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN)) {
|
|
nm.item.hItem = hItem;
|
|
|
|
SendNotifyEx(pTree->hwndParent, pTree->hwnd, TVN_GETDISPINFO, &nm.hdr, pTree->bUnicode);
|
|
|
|
// copy out things that may have been filled in on the callback
|
|
if (nm.item.mask & TVIF_CHILDREN)
|
|
lpItem->cChildren = nm.item.cChildren;
|
|
if (nm.item.mask & TVIF_IMAGE)
|
|
lpItem->iImage = nm.item.iImage;
|
|
if (nm.item.mask & TVIF_SELECTEDIMAGE)
|
|
lpItem->iSelectedImage = nm.item.iSelectedImage;
|
|
// callback may have redirected pszText to point into its own buffer
|
|
if (nm.item.mask & TVIF_TEXT)
|
|
lpItem->pszText = nm.item.pszText;
|
|
if (nm.item.mask & TVIF_STATE)
|
|
lpItem->state = (nm.item.state & nm.item.stateMask) | (lpItem->state & ~nm.item.stateMask);
|
|
|
|
if (nm.item.mask & TVIF_DI_SETITEM) {
|
|
|
|
if (nm.item.mask & TVIF_TEXT)
|
|
if (nm.item.pszText && (nm.item.pszText != LPSTR_TEXTCALLBACK)) {
|
|
Assert(hItem->lpstr == LPSTR_TEXTCALLBACK);
|
|
hItem->lpstr = NULL;
|
|
Str_Set(&hItem->lpstr, nm.item.pszText);
|
|
}
|
|
if (nm.item.mask & TVIF_STATE)
|
|
hItem->state = lpItem->state;
|
|
if (nm.item.mask & TVIF_IMAGE)
|
|
hItem->iImage = lpItem->iImage;
|
|
if (nm.item.mask & TVIF_SELECTEDIMAGE)
|
|
hItem->iSelectedImage = lpItem->iSelectedImage;
|
|
if (nm.item.mask & TVIF_CHILDREN) {
|
|
switch(nm.item.cChildren) {
|
|
case I_CHILDRENCALLBACK:
|
|
hItem->fKids = KIDS_CALLBACK;
|
|
break;
|
|
|
|
case 0:
|
|
hItem->fKids = KIDS_FORCE_NO;
|
|
break;
|
|
|
|
default:
|
|
hItem->fKids = KIDS_FORCE_YES;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// this is temporary until after NT SUR and the nashville commctrl merge
|
|
DWORD NEAR PASCAL CICustomDrawNotify(LPCONTROLINFO lpci, DWORD dwStage, LPNMCUSTOMDRAW lpnmcd, BOOL bUnicode)
|
|
{
|
|
DWORD dwRet = CDRF_DODEFAULT;
|
|
|
|
|
|
// bail if...
|
|
|
|
|
|
// this is an item notification, but an item notification wasn't asked for
|
|
if ((dwStage & CDDS_ITEM) && !(lpci->dwCustom & CDRF_NOTIFYITEMDRAW)) {
|
|
return dwRet;
|
|
}
|
|
|
|
lpnmcd->dwDrawStage = dwStage;
|
|
dwRet = SendNotifyEx(lpci->hwndParent, lpci->hwnd, NM_CUSTOMDRAW, &lpnmcd->hdr, bUnicode);
|
|
|
|
return dwRet;
|
|
}
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Draws the given item starting at the given (x,y) and extending down and to
|
|
// the right.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NEAR TV_DrawItem(PTREE pTree, HTREEITEM hItem, HDC hdc, int x, int y, UINT flags)
|
|
{
|
|
int iLevel = hItem->iLevel;
|
|
UINT cxIndent = pTree->cxIndent;
|
|
COLORREF rgbOldBack = 0, rgbOldText;
|
|
RECT rc;
|
|
int iBack, iText;
|
|
HTREEITEM hItemSave = hItem;
|
|
LPTSTR lpstr;
|
|
int cch;
|
|
UINT etoFlags = ETO_OPAQUE;
|
|
TV_ITEM ti;
|
|
TCHAR szTemp[MAX_PATH];
|
|
int iState = 0;
|
|
HFONT hFont; //$BOLD
|
|
DWORD dwRet;
|
|
NMTVCUSTOMDRAW nmcd;
|
|
BOOL fItemFocused = ((pTree->fFocus) && (hItem == pTree->hCaret));
|
|
DWORD clrTextTemp, clrTextBkTemp;
|
|
|
|
rc.top = y;
|
|
rc.bottom = rc.top + pTree->cyItem;
|
|
rc.left = 0;
|
|
rc.right = pTree->cxWnd;
|
|
|
|
if (flags & TVDI_ERASE) {
|
|
// Opaque the whole item
|
|
FillRect(hdc, &rc, pTree->hbrBk);
|
|
}
|
|
|
|
|
|
// make sure the callbacks don't invalidate this item
|
|
pTree->hItemPainting = hItem;
|
|
|
|
ti.pszText = szTemp;
|
|
ti.cchTextMax = ARRAYSIZE(szTemp);
|
|
ti.stateMask = TVIS_OVERLAYMASK | TVIS_CUT | TVIS_BOLD; //$BOLD
|
|
TV_GetItem(pTree, hItem, TVIF_IMAGE | TVIF_STATE | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_CHILDREN | TVIF_PARAM, &ti);
|
|
|
|
pTree->hItemPainting = NULL;
|
|
|
|
|
|
////////////////
|
|
// set up the HDC
|
|
if ((ti.state & TVIS_DROPHILITED) ||
|
|
(!pTree->hDropTarget &&
|
|
((ti.state & TVIS_SELECTED) &&
|
|
(pTree->fFocus || (pTree->style & TVS_SHOWSELALWAYS))) &&
|
|
!(flags & TVDI_GRAYCTL))) {
|
|
// selected
|
|
iBack = COLOR_HIGHLIGHT;
|
|
iText = COLOR_HIGHLIGHTTEXT;
|
|
} else {
|
|
// not selected
|
|
iBack = (flags & TVDI_GRAYCTL) ? COLOR_3DFACE : COLOR_WINDOW;
|
|
iText = ((ti.state & TVIS_DISABLED) || (flags & TVDI_GRAYCTL)) ? COLOR_GRAYTEXT : COLOR_WINDOWTEXT;
|
|
}
|
|
|
|
nmcd.clrText = clrTextTemp = GetSysColor(iText);
|
|
nmcd.clrTextBk = clrTextBkTemp = GetSysColor(iBack);
|
|
|
|
|
|
if (iBack)
|
|
rgbOldBack = SetBkColor(hdc, nmcd.clrTextBk);
|
|
rgbOldText = SetTextColor(hdc, nmcd.clrText);
|
|
|
|
// if forcing black and transparent, do so. dc's BkMode should
|
|
// already be set to TRANSPARENT by caller
|
|
if (flags & TVDI_TRANSTEXT)
|
|
{
|
|
nmcd.clrText = clrTextTemp =0x000000;
|
|
SetTextColor(hdc, nmcd.clrText);
|
|
etoFlags = 0; // don't opaque nothin'
|
|
}
|
|
|
|
// Figure out which font to use.
|
|
if (ti.state & TVIS_BOLD) {
|
|
hFont = pTree->hFontBold;
|
|
} else {
|
|
hFont = pTree->hFont;
|
|
|
|
}
|
|
hFont = SelectObject(hdc, hFont);
|
|
// End HDC setup
|
|
////////////////
|
|
|
|
|
|
// notify on custom draw then do it!
|
|
nmcd.nmcd.hdc = hdc;
|
|
nmcd.nmcd.dwItemSpec = (DWORD)hItem;
|
|
nmcd.nmcd.uItemState = 0;
|
|
nmcd.nmcd.rc = rc;
|
|
if ((ti.state & TVIS_SELECTED) || (pTree->style & TVS_SHOWSELALWAYS))
|
|
nmcd.nmcd.uItemState |= CDIS_SELECTED;
|
|
if (fItemFocused)
|
|
nmcd.nmcd.uItemState |= CDIS_FOCUS;
|
|
nmcd.nmcd.lItemlParam = ti.lParam;
|
|
|
|
dwRet = CICustomDrawNotify((LPCONTROLINFO)pTree, CDDS_ITEMPREPAINT, &nmcd.nmcd, pTree->bUnicode);
|
|
if (dwRet & CDRF_SKIPDEFAULT)
|
|
return;
|
|
|
|
fItemFocused = (nmcd.nmcd.uItemState & CDIS_FOCUS);
|
|
if (nmcd.nmcd.uItemState & CDIS_SELECTED)
|
|
ti.state |= TVIS_SELECTED;
|
|
else
|
|
ti.state &= ~TVIS_SELECTED;
|
|
|
|
if (nmcd.clrTextBk != clrTextBkTemp)
|
|
SetBkColor(hdc, nmcd.clrTextBk);
|
|
|
|
if (nmcd.clrText != clrTextTemp)
|
|
SetTextColor(hdc, nmcd.clrText);
|
|
|
|
|
|
if (flags & TVDI_NOTREE)
|
|
iLevel = 0;
|
|
else if ((pTree->style & (TVS_HASLINES | TVS_HASBUTTONS)) &&
|
|
(pTree->style & TVS_LINESATROOT))
|
|
// Make room for the "plus" at the front of the tree
|
|
x += cxIndent;
|
|
|
|
|
|
x += iLevel * cxIndent;
|
|
|
|
// draw image
|
|
if (!(flags & TVDI_NOTREE))
|
|
{
|
|
|
|
if (pTree->himlState) {
|
|
iState = TV_StateIndex(&ti);
|
|
// this sucks. in the treeview, 0 for the state image index
|
|
// means draw nothing... the 0th item is unused.
|
|
// the listview is 0 based and uses the 0th item.
|
|
if (iState) {
|
|
ImageList_Draw(pTree->himlState, iState, hdc, x,
|
|
y + (pTree->cyItem - pTree->cyState), ILD_NORMAL);
|
|
x += pTree->cxState;
|
|
}
|
|
}
|
|
|
|
if (pTree->hImageList) {
|
|
UINT fStyle = 0;
|
|
COLORREF rgb = CLR_HILIGHT;
|
|
|
|
int i = (ti.state & TVIS_SELECTED) ? ti.iSelectedImage : ti.iImage;
|
|
|
|
if (ti.state & TVIS_CUT) {
|
|
fStyle |= ILD_BLEND50;
|
|
rgb = ImageList_GetBkColor(pTree->hImageList);
|
|
}
|
|
|
|
ImageList_DrawEx(pTree->hImageList, i, hdc,
|
|
x, y + ((pTree->cyItem - pTree->cyImage) / 2), 0, 0,
|
|
CLR_DEFAULT, rgb,
|
|
fStyle | (ti.state & TVIS_OVERLAYMASK));
|
|
|
|
}
|
|
}
|
|
|
|
if (pTree->hImageList) {
|
|
// even if not drawing image, draw text in right place
|
|
x += pTree->cxImage;
|
|
}
|
|
|
|
// draw text
|
|
lpstr = ti.pszText;
|
|
cch = lstrlen(lpstr);
|
|
|
|
if (!hItem->iWidth || (hItem->lpstr == LPSTR_TEXTCALLBACK))
|
|
{
|
|
TV_ComputeItemWidth(pTree, hItem, hdc); //$BOLD
|
|
}
|
|
|
|
rc.left = x;
|
|
rc.right = x + hItem->iWidth;
|
|
|
|
// Draw the text, unless it's the one we are editing
|
|
if (pTree->htiEdit != hItem || !IsWindow(pTree->hwndEdit) || !IsWindowVisible(pTree->hwndEdit))
|
|
{
|
|
ExtTextOut(hdc, x + g_cxLabelMargin, y + ((pTree->cyItem - pTree->cyText) / 2) + g_cyBorder,
|
|
etoFlags, &rc, lpstr, cch, NULL);
|
|
|
|
// Draw the focus rect, if appropriate.
|
|
if (pTree->fFocus && fItemFocused && (hItem == pTree->hCaret) &&
|
|
!(flags & (TVDI_TRANSTEXT | TVDI_GRAYCTL)))
|
|
DrawFocusRect(hdc, &rc);
|
|
}
|
|
|
|
if (iBack)
|
|
SetBkColor(hdc, rgbOldBack);
|
|
SetTextColor(hdc, rgbOldText);
|
|
|
|
// Restore the original font. //$BOLD
|
|
SelectObject(hdc, hFont); //$BOLD
|
|
|
|
// Notice that we should have opaque'd the rest of the line above if no tree
|
|
if (!(flags & TVDI_NOTREE))
|
|
{
|
|
if (pTree->hImageList)
|
|
x -= pTree->cxImage;
|
|
|
|
if (iState)
|
|
x -= pTree->cxState;
|
|
|
|
if (pTree->style & TVS_HASLINES)
|
|
{
|
|
int i;
|
|
|
|
x -= cxIndent;
|
|
if (iLevel-- || (pTree->style & TVS_LINESATROOT))
|
|
{
|
|
// HACK: Special case the first root
|
|
// We will draw a "last" sibling button upside down
|
|
if (iLevel == -1 && hItem == hItem->hParent->hKids)
|
|
{
|
|
if (hItem->hNext) {
|
|
i = 2;
|
|
if (ti.cChildren && (pTree->style & TVS_HASBUTTONS))
|
|
{
|
|
i += 2;
|
|
if (ti.state & TVIS_EXPANDED)
|
|
i += 2;
|
|
}
|
|
|
|
StretchBlt(hdc, x, y + pTree->cyItem, cxIndent, -pTree->cyItem, pTree->hdcBits, i * cxIndent, 0, cxIndent, pTree->cyItem, SRCCOPY);
|
|
i = -1;
|
|
}
|
|
else
|
|
{
|
|
// first root no siblings
|
|
// if there's no other item, draw just the button if button mode,
|
|
if (pTree->style & TVS_HASBUTTONS)
|
|
{
|
|
if (ti.cChildren) {
|
|
// hasbuttons, has lines, lines at root
|
|
i = (ti.state & TVIS_EXPANDED) ? 9 : 8;
|
|
} else {
|
|
i = 7;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = 3;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = (hItem->hNext) ? 1 : 2;
|
|
if (ti.cChildren && (pTree->style & TVS_HASBUTTONS))
|
|
{
|
|
i += 2;
|
|
if (ti.state & TVIS_EXPANDED)
|
|
i += 2;
|
|
}
|
|
|
|
}
|
|
|
|
if (i != -1)
|
|
BitBlt(hdc, x, y, cxIndent, pTree->cyItem, pTree->hdcBits, i * cxIndent, 0, SRCCOPY);
|
|
|
|
while ((--iLevel >= 0) || ((pTree->style & TVS_LINESATROOT) && iLevel >= -1))
|
|
{
|
|
hItem = hItem->hParent;
|
|
x -= cxIndent;
|
|
if (hItem->hNext)
|
|
BitBlt(hdc, x, y, cxIndent, pTree->cyItem, pTree->hdcBits, 0, 0, SRCCOPY);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pTree->style & TVS_HASBUTTONS) && (iLevel || pTree->style & TVS_LINESATROOT)
|
|
&& ti.cChildren)
|
|
{
|
|
int i = (ti.state & TVIS_EXPANDED) ? cxIndent : 0;
|
|
|
|
x -= cxIndent;
|
|
BitBlt(hdc, x, y, cxIndent, pTree->cyItem, pTree->hdcBits, i, 0, SRCCOPY);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwRet & CDRF_NOTIFYPOSTPAINT) {
|
|
nmcd.nmcd.dwItemSpec = (DWORD)hItemSave;
|
|
CICustomDrawNotify((LPCONTROLINFO)pTree, CDDS_ITEMPOSTPAINT, &nmcd.nmcd, pTree->bUnicode);
|
|
}
|
|
}
|
|
|
|
|
|
void NEAR TV_DrawTree(PTREE pTree, HDC hdc, BOOL fErase, LPRECT lprc)
|
|
{
|
|
int x, y;
|
|
int iStart, iCnt;
|
|
UINT uFlags;
|
|
RECT rc;
|
|
NMCUSTOMDRAW nmcd;
|
|
|
|
if (!pTree->fRedraw)
|
|
return;
|
|
|
|
|
|
x = -pTree->xPos;
|
|
|
|
TV_GetBackgroundBrush(pTree, hdc);
|
|
|
|
rc = *lprc;
|
|
|
|
if ((pTree->cItems == 0) || (!pTree->hTop))
|
|
goto ClearAndReturn;
|
|
|
|
iStart = lprc->top / pTree->cyItem;
|
|
y = iStart * pTree->cyItem;
|
|
|
|
Assert(ITEM_VISIBLE(pTree->hTop));
|
|
|
|
iCnt = pTree->cShowing - pTree->hTop->iShownIndex;
|
|
|
|
nmcd.hdc = hdc;
|
|
/// not implemented yet
|
|
//if (ptb->ci.hwnd == GetFocus())
|
|
//nmcd.uItemState = CDIS_FOCUS;
|
|
//else
|
|
nmcd.uItemState = 0;
|
|
nmcd.lItemlParam = 0;
|
|
pTree->dwCustom = CICustomDrawNotify((LPCONTROLINFO)pTree, CDDS_PREPAINT, &nmcd, pTree->bUnicode);
|
|
if (!(pTree->dwCustom & CDRF_SKIPDEFAULT)) {
|
|
|
|
if (iStart < iCnt)
|
|
{
|
|
HTREEITEM hItem;
|
|
HFONT hOldFont;
|
|
int cVisible = min(iCnt, ((lprc->bottom / pTree->cyItem) + 1)) - iStart;
|
|
|
|
for (hItem = pTree->hTop; hItem && iStart; iStart--)
|
|
hItem = TV_GetNextVisItem(hItem);
|
|
|
|
hOldFont = pTree->hFont ? SelectObject(hdc, pTree->hFont) : NULL;
|
|
|
|
// TVDI_* for all items
|
|
uFlags = (pTree->style & WS_DISABLED) ? TVDI_GRAYCTL : 0;
|
|
if (fErase)
|
|
uFlags |= TVDI_ERASE;
|
|
|
|
// BUGBUG: I've seen this code fault, getting NULL back from
|
|
// TV_GetNextVisItem()
|
|
|
|
// loop from the first visible item until either all visible items are
|
|
// drawn or there are no more items to draw
|
|
while (cVisible)
|
|
{
|
|
|
|
TV_DrawItem(pTree, hItem, hdc, x, y, uFlags);
|
|
|
|
y += pTree->cyItem;
|
|
|
|
// if there is still room for more visible items, figure out the next
|
|
// item to be drawn
|
|
if (--cVisible)
|
|
hItem = TV_GetNextVisItem(hItem);
|
|
}
|
|
|
|
if (hOldFont)
|
|
SelectObject(hdc, hOldFont);
|
|
|
|
rc.top = y;
|
|
}
|
|
|
|
|
|
ClearAndReturn:
|
|
if (fErase)
|
|
// Opaque out everything we have not drawn explicitly
|
|
FillRect(hdc, &rc, pTree->hbrBk);
|
|
|
|
// notify parent afterwards if they want us to
|
|
if (pTree->dwCustom & CDRF_NOTIFYPOSTPAINT) {
|
|
CICustomDrawNotify((LPCONTROLINFO)pTree, CDDS_POSTPAINT, &nmcd, pTree->bUnicode);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// Set up for paint, call DrawTree, and clean up after paint.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void NEAR TV_Paint(PTREE pTree, HDC hdc)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
|
|
if (hdc)
|
|
{
|
|
// hdc != 0 indicates a subclassed paint -- use the hdc passed in
|
|
SetRect(&ps.rcPaint, 0, 0, pTree->cxWnd, pTree->cyWnd);
|
|
TV_DrawTree(pTree, hdc, TRUE, &ps.rcPaint);
|
|
}
|
|
else
|
|
{
|
|
BeginPaint(pTree->hwnd, &ps);
|
|
TV_DrawTree(pTree, ps.hdc, ps.fErase, &ps.rcPaint);
|
|
EndPaint(pTree->hwnd, &ps);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Create an imagelist to be used for dragging.
|
|
//
|
|
// 1) create mask and image bitmap matching the select bounds size
|
|
// 2) draw the text to both bitmaps (in black for now)
|
|
// 3) create an imagelist with these bitmaps
|
|
// 4) make a dithered copy of the image onto the new imagelist
|
|
// ----------------------------------------------------------------------------
|
|
|
|
HIMAGELIST NEAR TV_CreateDragImage(PTREE pTree, HTREEITEM hItem)
|
|
{
|
|
HDC hdcMem = NULL;
|
|
HBITMAP hbmImage = NULL;
|
|
HBITMAP hbmMask = NULL;
|
|
HBITMAP hbmOld;
|
|
HIMAGELIST himl = NULL;
|
|
int dx, dy;
|
|
int iSrc;
|
|
|
|
TV_ITEM ti;
|
|
|
|
// BUGBUG??? we know it's already been drawn, so is iWidth valid???
|
|
dx = hItem->iWidth + pTree->cxImage;
|
|
dy = pTree->cyItem;
|
|
|
|
if (!(hdcMem = CreateCompatibleDC(NULL)))
|
|
goto CDI_Exit;
|
|
if (!(hbmImage = CreateColorBitmap(dx, dy)))
|
|
goto CDI_Exit;
|
|
if (!(hbmMask = CreateMonoBitmap(dx, dy)))
|
|
goto CDI_Exit;
|
|
|
|
// prepare for drawing the item
|
|
if (pTree->hFont)
|
|
SelectObject(hdcMem, pTree->hFont);
|
|
SetBkMode(hdcMem, TRANSPARENT);
|
|
|
|
/*
|
|
** draw the text to both bitmaps
|
|
*/
|
|
hbmOld = SelectObject(hdcMem, hbmImage);
|
|
// fill image with black for transparency
|
|
PatBlt(hdcMem, 0, 0, dx, dy, BLACKNESS);
|
|
TV_DrawItem(pTree, hItem, hdcMem, 0, 0,
|
|
TVDI_NOIMAGE | TVDI_NOTREE | TVDI_TRANSTEXT);
|
|
SelectObject(hdcMem, hbmMask);
|
|
// fill mask with white for transparency
|
|
PatBlt(hdcMem, 0, 0, dx, dy, WHITENESS);
|
|
TV_DrawItem(pTree, hItem, hdcMem, 0, 0,
|
|
TVDI_NOIMAGE | TVDI_NOTREE | TVDI_TRANSTEXT);
|
|
|
|
// unselect objects that we used
|
|
SelectObject(hdcMem, hbmOld);
|
|
SelectObject(hdcMem, g_hfontSystem);
|
|
|
|
/*
|
|
** make an image list that for now only has the text
|
|
*/
|
|
//
|
|
// BUGBUG: To fix a pri-1 M7 bug, we create a shared image list.
|
|
//
|
|
if (!(himl = ImageList_Create(dx, dy, ILC_MASK, 1, 0)))
|
|
goto CDI_Exit;
|
|
ImageList_SetBkColor(himl, CLR_NONE);
|
|
ImageList_Add(himl, hbmImage, hbmMask);
|
|
|
|
/*
|
|
** make a dithered copy of the image part onto our bitmaps
|
|
** (need both bitmap and mask to be dithered)
|
|
*/
|
|
TV_GetItem(pTree, hItem, TVIF_IMAGE, &ti);
|
|
iSrc = ti.iImage;
|
|
|
|
ImageList_CopyDitherImage(himl, 0, 0, (pTree->cyItem - pTree->cyImage) / 2,
|
|
pTree->hImageList, iSrc, hItem->state & TVIS_OVERLAYMASK);
|
|
|
|
CDI_Exit:
|
|
if (hdcMem)
|
|
DeleteObject(hdcMem);
|
|
if (hbmImage)
|
|
DeleteObject(hbmImage);
|
|
if (hbmMask)
|
|
DeleteObject(hbmMask);
|
|
|
|
return himl;
|
|
}
|