Static control [10/10]: Support the Windows XP mode of the static control

Michael Kaufmann hallo at michael-kaufmann.ch
Wed Jan 11 16:45:48 CST 2006


Changelog:
   - Detect if the image of the current process has a manifest. If yes,
enable some Windows XP-specific features (as described in MSDN)
   - Destruction of the control window: Destroy bitmaps loaded at control
creation time (removed a resource leak)
   - Always stretch bitmaps and icons to the whole client area
   - Windows XP mode: Different bitmap background, different icon 
background.
   - Add some notes


-------------- next part --------------
--- static-old.c	2006-01-11 22:57:48.000000000 +0100
+++ static.c	2006-01-11 22:58:43.000000000 +0100
@@ -25,14 +25,22 @@
  * Unless otherwise noted, we believe this code to be complete, as per
  * the specification mentioned above.
  * If you discover missing features, or bugs, please note them below.
- * 
- * TODO:
  *
- *   Styles
- *   - SS_RIGHTJUST
+ * Notes:
+ *   - Windows XP introduced new behavior: The background of centered
+ *     icons and bitmaps is painted differently. This is only done if
+ *     a manifest is present.
+ *   - The application is responsible to delete the old icon/bitmap/metafile
+ *     when it sends STM_SETICON or STM_SETIMAGE. The static control deletes
+ *     only bitmaps which were loaded at control creation time.
+ *   - Controls with SS_SIMPLE but without SS_NOPREFIX:
+ *     The text should not be changed. Windows doesn't clear the
+ *     client rectangle, so the new text must be larger than the old one.
+ *   - The SS_RIGHTJUST style is currently not implemented by Windows
+ *     (or it does something different than documented).
  *
- *   Messages
- *   - STM_SETIMAGE: IMAGE_CURSOR
+ * TODO:
+ *   - Animated cursors
  */
 
 #include <stdarg.h>
@@ -62,7 +70,11 @@
 /* offsets for GetWindowLong for static private information */
 #define HFONT_GWL_OFFSET    0
 #define HICON_GWL_OFFSET    (sizeof(HFONT))
-#define STATIC_EXTRA_BYTES  (HICON_GWL_OFFSET + sizeof(HICON))
+#define FLAGS_GWL_OFFSET    (HICON_GWL_OFFSET + sizeof(HICON))
+#define STATIC_EXTRA_BYTES  (FLAGS_GWL_OFFSET + sizeof(LONG_PTR))
+
+#define FLAG_MANIFEST      0x01
+#define FLAG_BITMAP_OWNER  0x02
 
 typedef void (*pfPaint)( HWND hwnd, HDC hdc, DWORD style );
 
@@ -357,6 +369,23 @@
 }
 
 /***********************************************************************
+ *           hasManifest
+ *
+ * Tests if the image associated with the current process contains
+ * a manifest.
+ */
+static BOOL hasManifest( void )
+{
+    /* Usually the resource ID is 1.
+       There are some more IDs for special purposes
+       (look for "Using Windows XP Visual Styles" on MSDN) */
+    /* FIXME: Support manifests in external files and parse the manfiest */
+    return (FindResourceW(NULL, MAKEINTRESOURCEW(1),
+                MAKEINTRESOURCEW(RT_MANIFEST)) != NULL);
+}
+
+
+/***********************************************************************
  *           StaticWndProc_common
  */
 static LRESULT StaticWndProc_common( HWND hwnd, UINT uMsg, WPARAM wParam,
@@ -378,19 +407,27 @@
         break;
 
     case WM_NCDESTROY:
-        if (style == SS_ICON) {
-/*
- * FIXME
- *           DestroyIcon32( STATIC_SetIcon( wndPtr, 0 ) );
- *
- * We don't want to do this yet because DestroyIcon32 is broken. If the icon
- * had already been loaded by the application the last thing we want to do is
- * GlobalFree16 the handle.
- */
-            break;
+        /* SS_ICON:
+           The initially loaded icons are shared. If the application sets
+           an own icon, it must destroy the icon itself.
+           
+           SS_ENHMETAFILE:
+           The static control doesn't own the handle, because it was always
+           set by STM_SETIMAGE. The application has to free it.
+           
+           SS_BITMAP:
+           The bitmap must be destroyed if the application has not changed
+           it with STM_SETIMAGE.
+        */
+        if ((style == SS_BITMAP) &&
+            (GetWindowLongPtrW( hwnd, FLAGS_GWL_OFFSET ) &
+            FLAG_BITMAP_OWNER) != 0)
+        {
+            HBITMAP bitmap = STATIC_SetBitmap(hwnd, NULL, full_style);
+            if (bitmap) DeleteObject(bitmap);
         }
-        else return unicode ? DefWindowProcW(hwnd, uMsg, wParam, lParam) :
-                              DefWindowProcA(hwnd, uMsg, wParam, lParam);
+        return unicode ? DefWindowProcW(hwnd, uMsg, wParam, lParam) :
+                         DefWindowProcA(hwnd, uMsg, wParam, lParam);
 
     case WM_PRINTCLIENT:
     case WM_PAINT:
@@ -426,11 +463,17 @@
         {
             LPCSTR textA;
             LPCWSTR textW;
+            LONG_PTR flags;
     
             if (full_style & SS_SUNKEN)
                 SetWindowLongW( hwnd, GWL_EXSTYLE,
                                 GetWindowLongW( hwnd, GWL_EXSTYLE ) | WS_EX_STATICEDGE );
 
+            /* Initialize the flags */
+            flags = FLAG_BITMAP_OWNER;
+            if (hasManifest()) flags |= FLAG_MANIFEST;
+            SetWindowLongPtrW( hwnd, FLAGS_GWL_OFFSET, flags );
+
             if(unicode)
             {
                 textA = NULL;
@@ -529,14 +572,17 @@
     case STM_SETIMAGE:
         switch(wParam) {
 	case IMAGE_BITMAP:
+	{
+	    LONG_PTR flags = GetWindowLongPtrW( hwnd, FLAGS_GWL_OFFSET );
+	    flags &= ~FLAG_BITMAP_OWNER;
+	    SetWindowLongPtrW( hwnd, FLAGS_GWL_OFFSET, flags );
 	    lResult = (LRESULT)STATIC_SetBitmap( hwnd, (HBITMAP)lParam, full_style );
 	    break;
-	case IMAGE_CURSOR:
-	    FIXME("STM_SETIMAGE: Unhandled type IMAGE_CURSOR\n");
-	    break;
+	}   
 	case IMAGE_ENHMETAFILE:
 	    lResult = (LRESULT)STATIC_SetEnhMetaFile( hwnd, (HENHMETAFILE)lParam, full_style );
 	    break;
+	case IMAGE_CURSOR:
 	case IMAGE_ICON:
 	    lResult = (LRESULT)STATIC_SetIcon( hwnd, (HICON)lParam, full_style );
 	    break;
@@ -595,7 +641,7 @@
   GetClientRect( hwnd, &dis.rcItem );
 
   font = (HFONT)GetWindowLongPtrW( hwnd, HFONT_GWL_OFFSET );
-  if (font) oldFont = SelectObject( hdc, font );
+  if (font) oldFont = (HFONT)SelectObject( hdc, font );
   SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC, (WPARAM)hdc, (LPARAM)hwnd );
   SendMessageW( GetParent(hwnd), WM_DRAWITEM, id, (LPARAM)&dis );
   if (font) SelectObject( hdc, oldFont );
@@ -733,66 +779,124 @@
 
 static void STATIC_PaintIconfn( HWND hwnd, HDC hdc, DWORD style )
 {
-    RECT rc;
+    RECT rc, iconRect;
     HBRUSH hbrush;
     HICON hIcon;
-    INT x, y;
+    CURSORICONINFO * info;
 
     GetClientRect( hwnd, &rc );
-    hbrush = (HBRUSH)SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC,
-				   (WPARAM)hdc, (LPARAM)hwnd );
-    FillRect( hdc, &rc, hbrush );
+    hbrush = STATIC_SendWmCtlColorStatic(hwnd, hdc);
     hIcon = (HICON)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET );
-    if (style & SS_CENTERIMAGE)
+    info = hIcon ? (CURSORICONINFO *)GlobalLock16(HICON_16(hIcon)) : NULL;
+    if (!hIcon || !info)
     {
-        CURSORICONINFO *info = hIcon ? (CURSORICONINFO *)GlobalLock16(HICON_16(hIcon)) : NULL;
-        x = (rc.right - rc.left)/2 - (info ? info->nWidth/2 : 0);
-        y = (rc.bottom - rc.top)/2 - (info ? info->nHeight/2 : 0);
+        FillRect(hdc, &rc, hbrush);
     }
     else
     {
-        x = rc.left;
-        y = rc.top;
+        if (style & SS_CENTERIMAGE)
+        {
+            BOOL hasManifest = (GetWindowLongPtrW( hwnd, FLAGS_GWL_OFFSET ) &
+                                FLAG_MANIFEST) != 0;
+            iconRect.left = (rc.right - rc.left) / 2 - info->nWidth / 2;
+            iconRect.top = (rc.bottom - rc.top) / 2 - info->nHeight / 2;
+            iconRect.right = iconRect.left + info->nWidth;
+            iconRect.bottom = iconRect.top + info->nHeight;
+            if (hasManifest)
+                FillRect( hdc, &rc, hbrush );
+            else
+                FillRect( hdc, &iconRect, hbrush );
+            DrawIcon( hdc, iconRect.left, iconRect.top, hIcon );
+        }
+        else
+        {
+            FillRect( hdc, &rc, hbrush );
+            DrawIconEx( hdc, rc.left, rc.top, hIcon, rc.right - rc.left,
+                        rc.bottom - rc.top, 0, NULL, DI_NORMAL );
+        }
     }
-    if (hIcon)
-        DrawIcon( hdc, x, y, hIcon );
+    if (info) GlobalUnlock16(HICON_16(hIcon));
 }
 
 static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, DWORD style )
 {
     HDC hMemDC;
     HBITMAP hBitmap, oldbitmap;
+    HBRUSH hbrush;
+    BOOL hasManifest = (GetWindowLongPtrW( hwnd, FLAGS_GWL_OFFSET ) &
+                        FLAG_MANIFEST) != 0;
 
     /* message is still sent, even if the returned brush is not used */
-    SendMessageW( GetParent(hwnd), WM_CTLCOLORSTATIC,
-				   (WPARAM)hdc, (LPARAM)hwnd );
+    hbrush = STATIC_SendWmCtlColorStatic(hwnd, hdc);
 
-    if ((hBitmap = (HBITMAP)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET )))
+    if ((hBitmap = (HBITMAP)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ))
+         && (GetObjectType(hBitmap) == OBJ_BITMAP)
+         && (hMemDC = CreateCompatibleDC( hdc )))
     {
-        INT x, y;
         BITMAP bm;
-
-        if(GetObjectType(hBitmap) != OBJ_BITMAP) return;
-        if (!(hMemDC = CreateCompatibleDC( hdc ))) return;
+        RECT rcClient;
+	
 	GetObjectW(hBitmap, sizeof(bm), &bm);
 	oldbitmap = SelectObject(hMemDC, hBitmap);
+
+	if (hasManifest)
+	{
+	    /* Set the background color for monochrome bitmaps
+	       to the color of the background brush */
+	    LOGBRUSH brush;
+	    if (GetObjectW( hbrush, sizeof(brush), &brush ))
+	    {
+	        if (brush.lbStyle == BS_SOLID)
+	            SetBkColor(hdc, brush.lbColor);
+	    }
+	}
+	GetClientRect(hwnd, &rcClient);
         if (style & SS_CENTERIMAGE)
         {
-            RECT rcClient;
-            GetClientRect(hwnd, &rcClient);
+            INT x, y;
             x = (rcClient.right - rcClient.left)/2 - bm.bmWidth/2;
             y = (rcClient.bottom - rcClient.top)/2 - bm.bmHeight/2;
+            if (hasManifest)
+            {
+                FillRect( hdc, &rcClient, hbrush );
+            }
+            else
+            {
+                /* Use the color of the top-left pixel to fill the
+                   client area */
+                HBRUSH fillBrush;
+                COLORREF topLeftColor = GetPixel(hMemDC, 0, 0);
+                if (topLeftColor != CLR_INVALID)
+                {
+                    fillBrush = CreateSolidBrush(topLeftColor);
+                    if (fillBrush)
+                    {
+                        FillRect( hdc, &rcClient, fillBrush );
+                        DeleteObject(fillBrush);
+                    }
+                }
+            }
+            BitBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0,
+                   SRCCOPY);
         }
         else
         {
-            x = 0;
-            y = 0;
+            StretchBlt(hdc, 0, 0, rcClient.right - rcClient.left,
+                       rcClient.bottom - rcClient.top, hMemDC,
+                       0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
         }
-        BitBlt(hdc, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0,
-	       SRCCOPY);
 	SelectObject(hMemDC, oldbitmap);
 	DeleteDC(hMemDC);
     }
+    else
+    {
+        if (hasManifest)
+        {
+             RECT rcClient;
+             GetClientRect( hwnd, &rcClient );
+             FillRect( hdc, &rcClient, hbrush );
+        }
+    }
 }
 
 



More information about the wine-patches mailing list