TreeView fix endless loop and colors with custom draw

Vitaliy Margolen wine-patch at kievinfo.com
Thu Apr 14 15:24:37 CDT 2005


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	14 Apr 2005 20:08:40 -0000
@@ -616,10 +616,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 +635,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 +645,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 +653,7 @@ TREEVIEW_SendCustomDrawItemNotify(TREEVI
 
     retval = TREEVIEW_SendRealNotify(infoPtr,
                           (WPARAM)nmcd->hdr.idFrom,
-			  (LPARAM)&nmcdhdr);
+			  (LPARAM)nmcdhdr);
 
     return (BOOL)retval;
 }
@@ -2114,9 +2112,17 @@ 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 (((tvItem->mask & TVIF_CHILDREN     ) && (originalItem.cChildren      != wineItem->cChildren     )   ) ||
+	    ((tvItem->mask & TVIF_IMAGE        ) && (originalItem.iImage         != wineItem->iImage        )&&
+	     wineItem->iImage         != I_IMAGECALLBACK                                                        ) ||
+	    ((tvItem->mask & TVIF_SELECTEDIMAGE) && (originalItem.iSelectedImage != wineItem->iSelectedImage)&&
+	     wineItem->iSelectedImage != I_IMAGECALLBACK                                                        ) ||
+	    ((tvItem->mask & TVIF_TEXT         ) && (originalItem.pszText        != wineItem->pszText       )&&
+	     wineItem->pszText        != LPSTR_TEXTCALLBACKW                                                    ) ||
+	    ((tvItem->mask & TVIF_INTEGRAL     ) && (originalItem.iIntegral      != wineItem->iIntegral     )   ) ||
+	    ((tvItem->mask & TVIF_STATE        ) && ((originalItem.state ^ wineItem->state) & tvItem->stateMask ))
+	)
         {
             if (tvItem->mask & TVIF_INTEGRAL)
 	    {
@@ -2265,10 +2271,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 +2345,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 +2378,8 @@ TREEVIEW_DrawItemLines(TREEVIEW_INFO *in
 	    }
 	}
     }
+    SelectObject(hdc, hbrOld);
+    DeleteObject(hbr);
 }
 
 static void
@@ -2380,12 +2387,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 +2442,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 +2457,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 +2518,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 +2586,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