TreeView fix endless loop and colors with custom draw

Vitaliy Margolen wine-patch at kievinfo.com
Sat Apr 16 11:47:40 CDT 2005


With changes suggested by AJ

Vitaliy Margolen

Thursday, April 14, 2005, 2:24:37 PM, Vitaliy Margolen wrote:

> Here is what I found:
> - Not all changes to items through SetItem should trigger redraw.
> - If app using callbacks for text and images it has to refresh control
>   explicitly when that information changes.
> - TreeView should keep color changes maid by app within draw notifies but only
>   for the current draw cycle and not permanently.

> Vitaliy Margolen

> changelog:
>  dlls/comctl32/treeview.c
>    Don't redraw if no information has changed
>    Fixed endless redraw loop if app using callback for images and/or text
>    Keep color changes made by app for the current draw cycle
-------------- next part --------------
Index: dlls/comctl32/treeview.c
===================================================================
RCS file: /home/wine/wine/dlls/comctl32/treeview.c,v
retrieving revision 1.170
diff -u -p -r1.170 treeview.c
--- dlls/comctl32/treeview.c	14 Apr 2005 13:56:12 -0000	1.170
+++ dlls/comctl32/treeview.c	16 Apr 2005 16:43:38 -0000
@@ -241,6 +241,39 @@ TREEVIEW_GetItemIndex(TREEVIEW_INFO *inf
     return DPA_GetPtrIndex(infoPtr->items, handle);
 }
 
+/* Checks if item has changed and needs to be redrawn */
+static inline BOOL item_changed (TREEVIEW_ITEM *tiOld, TREEVIEW_ITEM *tiNew, LPTVITEMEXW tvChange)
+{
+    /* Number of children has changed */
+    if ((tvChange->mask & TVIF_CHILDREN) && (tiOld->cChildren != tiNew->cChildren))
+	return TRUE;
+
+    /* Image has changed and it's not a callback */
+    if ((tvChange->mask & TVIF_IMAGE) && (tiOld->iImage != tiNew->iImage) &&
+	tiNew->iImage != I_IMAGECALLBACK)
+	return TRUE;
+
+    /* Selected image has changed and it's not a callback */
+    if ((tvChange->mask & TVIF_SELECTEDIMAGE) && (tiOld->iSelectedImage != tiNew->iSelectedImage) &&
+	tiNew->iSelectedImage != I_IMAGECALLBACK)
+	return TRUE;
+
+    /* Text has changed and it's not a callback */
+    if ((tvChange->mask & TVIF_TEXT) && (tiOld->pszText != tiNew->pszText) &&
+	tiNew->pszText != LPSTR_TEXTCALLBACKW)
+	return TRUE;
+
+    /* Indent has changed */
+    if ((tvChange->mask & TVIF_INTEGRAL) && (tiOld->iIntegral != tiNew->iIntegral))
+	return TRUE;
+
+    /* Item state has changed */
+    if ((tvChange->mask & TVIF_STATE) && ((tiOld->state ^ tiNew->state) & tvChange->stateMask ))
+	return TRUE;
+
+    return FALSE;
+}
+
 /***************************************************************************
  * This method checks that handle is an item for this tree.
  */
@@ -616,10 +649,10 @@ TREEVIEW_SendCustomDrawNotify(TREEVIEW_I
 
 static BOOL
 TREEVIEW_SendCustomDrawItemNotify(TREEVIEW_INFO *infoPtr, HDC hdc,
-				  TREEVIEW_ITEM *wineItem, UINT uItemDrawState)
+				  TREEVIEW_ITEM *wineItem, UINT uItemDrawState,
+				  NMTVCUSTOMDRAW *nmcdhdr)
 {
     HWND hwnd = infoPtr->hwnd;
-    NMTVCUSTOMDRAW nmcdhdr;
     LPNMCUSTOMDRAW nmcd;
     DWORD dwDrawStage, dwItemSpec;
     UINT uItemState;
@@ -635,7 +668,7 @@ TREEVIEW_SendCustomDrawItemNotify(TREEVI
     if (wineItem == infoPtr->hotItem)
 	uItemState |= CDIS_HOT;
 
-    nmcd = &nmcdhdr.nmcd;
+    nmcd = &nmcdhdr->nmcd;
     nmcd->hdr.hwndFrom = hwnd;
     nmcd->hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID);
     nmcd->hdr.code = NM_CUSTOMDRAW;
@@ -645,9 +678,7 @@ TREEVIEW_SendCustomDrawItemNotify(TREEVI
     nmcd->dwItemSpec = dwItemSpec;
     nmcd->uItemState = uItemState;
     nmcd->lItemlParam = wineItem->lParam;
-    nmcdhdr.clrText = infoPtr->clrText;
-    nmcdhdr.clrTextBk = infoPtr->clrBk;
-    nmcdhdr.iLevel = wineItem->iLevel;
+    nmcdhdr->iLevel = wineItem->iLevel;
 
     TRACE("drawstage:%lx hdc:%p item:%lx, itemstate:%x, lItemlParam:%lx\n",
 	  nmcd->dwDrawStage, nmcd->hdc, nmcd->dwItemSpec,
@@ -655,7 +686,7 @@ TREEVIEW_SendCustomDrawItemNotify(TREEVI
 
     retval = TREEVIEW_SendRealNotify(infoPtr,
                           (WPARAM)nmcd->hdr.idFrom,
-			  (LPARAM)&nmcdhdr);
+			  (LPARAM)nmcdhdr);
 
     return (BOOL)retval;
 }
@@ -2114,9 +2145,8 @@ TREEVIEW_SetItemT(TREEVIEW_INFO *infoPtr
 	/* The refresh updates everything, but we can't wait until then. */
 	TREEVIEW_ComputeItemInternalMetrics(infoPtr, wineItem);
 
-        /* if any of the items values changed, redraw the item */
-        if(memcmp(&originalItem, wineItem, sizeof(TREEVIEW_ITEM)) ||
-           (tvItem->stateMask & TVIS_BOLD))
+        /* if any of the item's values changed and it's not a callback, redraw the item */
+        if (item_changed(&originalItem, wineItem, tvItem))
         {
             if (tvItem->mask & TVIF_INTEGRAL)
 	    {
@@ -2265,10 +2295,14 @@ TREEVIEW_DrawItemLines(TREEVIEW_INFO *in
     BOOL lar = ((infoPtr->dwStyle
 		 & (TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS))
 		> TVS_LINESATROOT);
+    HBRUSH hbr, hbrOld;
 
     if (!lar && item->iLevel == 0)
 	return;
 
+    hbr    = CreateSolidBrush(infoPtr->clrBk);
+    hbrOld = SelectObject(hdc, hbr);
+    
     centerx = (item->linesOffset + item->stateOffset) / 2;
     centery = (item->rect.top + item->rect.bottom) / 2;
 
@@ -2335,15 +2369,10 @@ TREEVIEW_DrawItemLines(TREEVIEW_INFO *in
 
 	    HPEN hNewPen  = CreatePen(PS_SOLID, 0, infoPtr->clrLine);
 	    HPEN hOldPen  = SelectObject(hdc, hNewPen);
-	    HBRUSH hbr    = CreateSolidBrush(infoPtr->clrBk);
-	    HBRUSH hbrOld = SelectObject(hdc, hbr);
 
 	    Rectangle(hdc, centerx - rectsize - 1, centery - rectsize - 1,
 		      centerx + rectsize + 2, centery + rectsize + 2);
 
-	    SelectObject(hdc, hbrOld);
-	    DeleteObject(hbr);
-
 	    SelectObject(hdc, hOldPen);
 	    DeleteObject(hNewPen);
 
@@ -2373,6 +2402,8 @@ TREEVIEW_DrawItemLines(TREEVIEW_INFO *in
 	    }
 	}
     }
+    SelectObject(hdc, hbrOld);
+    DeleteObject(hbr);
 }
 
 static void
@@ -2380,12 +2411,50 @@ TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr
 {
     INT cditem;
     HFONT hOldFont;
+    COLORREF oldTextColor, oldTextBkColor;
     int centery;
-
-    hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
+    BOOL inFocus = (GetFocus() == infoPtr->hwnd);
+    NMTVCUSTOMDRAW nmcdhdr;
 
     TREEVIEW_UpdateDispInfo(infoPtr, wineItem, CALLBACK_MASK_ALL);
 
+    /* - If item is drop target or it is selected and window is in focus -
+     * use blue background (COLOR_HIGHLIGHT).
+     * - If item is selected, window is not in focus, but it has style
+     * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
+     * - Otherwise - use background color
+     */
+    if ((wineItem->state & TVIS_DROPHILITED) || ((wineItem == infoPtr->focusedItem) && !(wineItem->state & TVIS_SELECTED)) ||
+	((wineItem->state & TVIS_SELECTED) && (!infoPtr->focusedItem) &&
+	 (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
+    {
+	if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
+	{
+	    nmcdhdr.clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
+	    nmcdhdr.clrText   = GetSysColor(COLOR_HIGHLIGHTTEXT);
+	}
+	else
+	{
+	    nmcdhdr.clrTextBk = GetSysColor(COLOR_BTNFACE);
+	    if (infoPtr->clrText == -1)
+		nmcdhdr.clrText = GetSysColor(COLOR_WINDOWTEXT);
+	    else
+		nmcdhdr.clrText = infoPtr->clrText;
+	}
+    }
+    else
+    {
+	nmcdhdr.clrTextBk = infoPtr->clrBk;
+	if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (wineItem == infoPtr->hotItem))
+	    nmcdhdr.clrText = comctl32_color.clrHighlight;
+	else if (infoPtr->clrText == -1)
+	    nmcdhdr.clrText = GetSysColor(COLOR_WINDOWTEXT);
+	else
+	    nmcdhdr.clrText = infoPtr->clrText;
+    }
+
+    hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, wineItem));
+
     /* The custom draw handler can query the text rectangle,
      * so get ready. */
     /* should already be known, set to 0 when changed */
@@ -2397,7 +2466,7 @@ TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr
     if (infoPtr->cdmode & CDRF_NOTIFYITEMDRAW)
     {
 	cditem = TREEVIEW_SendCustomDrawItemNotify
-	    (infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT);
+	    (infoPtr, hdc, wineItem, CDDS_ITEMPREPAINT, &nmcdhdr);
 	TRACE("prepaint:cditem-app returns 0x%x\n", cditem);
 
 	if (cditem & CDRF_SKIPDEFAULT)
@@ -2412,6 +2481,10 @@ TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr
 
     TREEVIEW_DrawItemLines(infoPtr, hdc, wineItem);
 
+    /* Set colors. Custom draw handler can change these so we do this after it. */
+    oldTextColor = SetTextColor(hdc, nmcdhdr.clrText);
+    oldTextBkColor = SetBkColor(hdc, nmcdhdr.clrTextBk);
+
     centery = (wineItem->rect.top + wineItem->rect.bottom) / 2;
 
     /*
@@ -2469,87 +2542,31 @@ TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr
     {
 	if (wineItem->pszText)
 	{
-	    COLORREF oldTextColor = 0;
-	    INT oldBkMode;
-	    HBRUSH hbrBk = 0;
-	    BOOL inFocus = (GetFocus() == infoPtr->hwnd);
 	    RECT rcText;
 
-	    oldBkMode = SetBkMode(hdc, TRANSPARENT);
-
-	    /* - If item is drop target or it is selected and window is in focus -
-	     * use blue background (COLOR_HIGHLIGHT).
-	     * - If item is selected, window is not in focus, but it has style
-	     * TVS_SHOWSELALWAYS - use grey background (COLOR_BTNFACE)
-	     * - Otherwise - don't fill background
-	     */
-	    if ((wineItem->state & TVIS_DROPHILITED) || ((wineItem == infoPtr->focusedItem) && !(wineItem->state & TVIS_SELECTED)) ||
-		((wineItem->state & TVIS_SELECTED) && (!infoPtr->focusedItem) &&
-		 (inFocus || (infoPtr->dwStyle & TVS_SHOWSELALWAYS))))
-	    {
-		if ((wineItem->state & TVIS_DROPHILITED) || inFocus)
-		{
-		    hbrBk = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
-		    oldTextColor =
-			SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
-		}
-		else
-		{
-		    hbrBk = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
-
-		    if (infoPtr->clrText == -1)
-			oldTextColor =
-			    SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
-		    else
-			oldTextColor = SetTextColor(hdc, infoPtr->clrText);
-		}
-	    }
-	    else
-	    {
-		if ((infoPtr->dwStyle & TVS_TRACKSELECT) && (wineItem == infoPtr->hotItem))
-		    oldTextColor = SetTextColor(hdc, comctl32_color.clrHighlight);
-		else if (infoPtr->clrText == -1)
-		    oldTextColor =
-			SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
-		else
-		    oldTextColor = SetTextColor(hdc, infoPtr->clrText);
-	    }
-
 	    rcText.top = wineItem->rect.top;
 	    rcText.bottom = wineItem->rect.bottom;
 	    rcText.left = wineItem->textOffset;
 	    rcText.right = rcText.left + wineItem->textWidth + 4;
 
-	    if (hbrBk)
-	    {
-		FillRect(hdc, &rcText, hbrBk);
-		DeleteObject(hbrBk);
-	    }
+	    TRACE("drawing text %s at (%ld,%ld)-(%ld,%ld)\n",
+		  debugstr_w(wineItem->pszText),
+		  rcText.left, rcText.top, rcText.right, rcText.bottom);
 
+	    /* Draw it */
+	    ExtTextOutW(hdc, rcText.left + 2, rcText.top + 1,
+		        ETO_CLIPPED | ETO_OPAQUE,
+			&rcText,
+		        wineItem->pszText,
+		        lstrlenW(wineItem->pszText),
+			NULL);
+			
 	    /* Draw the box around the selected item */
 	    if ((wineItem == infoPtr->selectedItem) && inFocus)
 	    {
 		DrawFocusRect(hdc,&rcText);
 	    }
 
-	    InflateRect(&rcText, -2, -1); /* allow for the focus rect */
-
-	    TRACE("drawing text %s at (%ld,%ld)-(%ld,%ld)\n",
-		  debugstr_w(wineItem->pszText),
-		  rcText.left, rcText.top, rcText.right, rcText.bottom);
-
-	    /* Draw it */
-	    DrawTextW(hdc,
-		      wineItem->pszText,
-		      lstrlenW(wineItem->pszText),
-		      &rcText,
-		      DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX);
-
-	    /* Restore the hdc state */
-	    SetTextColor(hdc, oldTextColor);
-
-	    if (oldBkMode != TRANSPARENT)
-		SetBkMode(hdc, oldBkMode);
 	}
     }
 
@@ -2593,10 +2610,13 @@ TREEVIEW_DrawItem(TREEVIEW_INFO *infoPtr
     if (cditem & CDRF_NOTIFYPOSTPAINT)
     {
 	cditem = TREEVIEW_SendCustomDrawItemNotify
-	    (infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT);
+	    (infoPtr, hdc, wineItem, CDDS_ITEMPOSTPAINT, &nmcdhdr);
 	TRACE("postpaint:cditem-app returns 0x%x\n", cditem);
     }
 
+    /* Restore the hdc state */
+    SetTextColor(hdc, oldTextColor);
+    SetBkColor(hdc, oldTextBkColor);
     SelectObject(hdc, hOldFont);
 }
 


More information about the wine-patches mailing list