[COMCTL32][MEDIA] Sync comctl32 to wine 5.0 (#6789)
authorJustin Miller <justin.miller@reactos.org>
Wed, 4 Sep 2024 04:54:05 +0000 (21:54 -0700)
committerGitHub <noreply@github.com>
Wed, 4 Sep 2024 04:54:05 +0000 (21:54 -0700)
For SOME reason comctl32 has been synched manually multiple times to different versions and different pots
This PR aims to fix that

With the exception of button.c which all in all is a massive fork over wines code entirely.
and datetime.c which is at wine 6.0

Comctl32 is now at wine-5.0

52 files changed:
dll/win32/comctl32/button.c
dll/win32/comctl32/combo.c
dll/win32/comctl32/comboex.c
dll/win32/comctl32/comctl32.h
dll/win32/comctl32/comctl32undoc.c
dll/win32/comctl32/commctrl.c
dll/win32/comctl32/edit.c
dll/win32/comctl32/header.c
dll/win32/comctl32/hotkey.c
dll/win32/comctl32/idb_cmdlink.bmp [new file with mode: 0644]
dll/win32/comctl32/imagelist.c
dll/win32/comctl32/ipaddress.c
dll/win32/comctl32/listbox.c
dll/win32/comctl32/listview.c
dll/win32/comctl32/monthcal.c
dll/win32/comctl32/pager.c
dll/win32/comctl32/propsheet.c
dll/win32/comctl32/rebar.c
dll/win32/comctl32/static.c
dll/win32/comctl32/status.c
dll/win32/comctl32/string.c
dll/win32/comctl32/syslink.c
dll/win32/comctl32/taskdialog.c
dll/win32/comctl32/theming.c
dll/win32/comctl32/toolbar.c
dll/win32/comctl32/tooltips.c
dll/win32/comctl32/trackbar.c
dll/win32/comctl32/treeview.c
dll/win32/comctl32/updown.c
media/doc/WINESYNC.txt
modules/rostests/winetests/comctl32/combo.c
modules/rostests/winetests/comctl32/edit.c
modules/rostests/winetests/comctl32/imagelist.c
modules/rostests/winetests/comctl32/listbox.c
modules/rostests/winetests/comctl32/listview.c
modules/rostests/winetests/comctl32/misc.c
modules/rostests/winetests/comctl32/msg.h
modules/rostests/winetests/comctl32/pager.c
modules/rostests/winetests/comctl32/precomp.h
modules/rostests/winetests/comctl32/propsheet.c
modules/rostests/winetests/comctl32/rebar.c
modules/rostests/winetests/comctl32/resources.h
modules/rostests/winetests/comctl32/static.c
modules/rostests/winetests/comctl32/subclass.c
modules/rostests/winetests/comctl32/taskdialog.c
modules/rostests/winetests/comctl32/toolbar.c
modules/rostests/winetests/comctl32/tooltips.c
modules/rostests/winetests/comctl32/treeview.c
modules/rostests/winetests/comctl32/updown.c
modules/rostests/winetests/comctl32/v6util.h
sdk/include/psdk/winuser.h
sdk/tools/winesync/comctl32.cfg [new file with mode: 0644]

index 9002697..c979483 100644 (file)
@@ -93,8 +93,11 @@ WINE_DEFAULT_DEBUG_CHANNEL(button);
 typedef struct _BUTTON_INFO
 {
     HWND        hwnd;
+    HWND        parent;
     LONG        state;
     HFONT       font;
+    WCHAR      *note;
+    INT         note_length;
     union
     {
         HICON   icon;
@@ -256,6 +259,14 @@ HRGN set_control_clipping( HDC hdc, const RECT *rect )
     return hrgn;
 }
 
+static WCHAR *heap_strndupW(const WCHAR *src, size_t length)
+{
+    size_t size = (length + 1) * sizeof(WCHAR);
+    WCHAR *dst = heap_alloc(size);
+    if (dst) memcpy(dst, src, size);
+    return dst;
+}
+
 /**********************************************************************
  * Convert button styles to flags used by DrawText.
  */
@@ -642,6 +653,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
 
     case WM_NCDESTROY:
         SetWindowLongPtrW( hWnd, 0, 0 );
+        heap_free(infoPtr->note);
         heap_free(infoPtr);
         break;
 
@@ -1024,6 +1036,81 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L
         return 1; /* success. FIXME: check text length */
     }
 
+    case BCM_SETNOTE:
+    {
+        WCHAR *note = (WCHAR *)lParam;
+        if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
+        {
+            SetLastError(ERROR_NOT_SUPPORTED);
+            return FALSE;
+        }
+
+        heap_free(infoPtr->note);
+        if (note)
+        {
+            infoPtr->note_length = lstrlenW(note);
+            infoPtr->note = heap_strndupW(note, infoPtr->note_length);
+        }
+
+        if (!note || !infoPtr->note)
+        {
+            infoPtr->note_length = 0;
+            infoPtr->note = heap_alloc_zero(sizeof(WCHAR));
+        }
+
+        SetLastError(NO_ERROR);
+        return TRUE;
+    }
+
+    case BCM_GETNOTE:
+    {
+        DWORD *size = (DWORD *)wParam;
+        WCHAR *buffer = (WCHAR *)lParam;
+        INT length = 0;
+
+        if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
+        {
+            SetLastError(ERROR_NOT_SUPPORTED);
+            return FALSE;
+        }
+
+        if (!buffer || !size || !infoPtr->note)
+        {
+            SetLastError(ERROR_INVALID_PARAMETER);
+            return FALSE;
+        }
+
+        if (*size > 0)
+        {
+            length = min(*size - 1, infoPtr->note_length);
+            memcpy(buffer, infoPtr->note, length * sizeof(WCHAR));
+            buffer[length] = '\0';
+        }
+
+        if (*size < infoPtr->note_length + 1)
+        {
+            *size = infoPtr->note_length + 1;
+            SetLastError(ERROR_INSUFFICIENT_BUFFER);
+            return FALSE;
+        }
+        else
+        {
+            SetLastError(NO_ERROR);
+            return TRUE;
+        }
+    }
+
+    case BCM_GETNOTELENGTH:
+    {
+        if (btn_type != BS_COMMANDLINK && btn_type != BS_DEFCOMMANDLINK)
+        {
+            SetLastError(ERROR_NOT_SUPPORTED);
+            return 0;
+        }
+
+        return infoPtr->note_length;
+    }
+
     case WM_SETFONT:
         infoPtr->font = (HFONT)wParam;
         if (lParam) InvalidateRect(hWnd, NULL, TRUE);
index 0c67b2e..e083a58 100644 (file)
@@ -32,7 +32,6 @@
 #include "uxtheme.h"
 #include "vssym32.h"
 #include "commctrl.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "wine/heap.h"
 
@@ -79,6 +78,9 @@ static UINT   CBitHeight, CBitWidth;
 #define ID_CB_LISTBOX           1000
 #define ID_CB_EDIT              1001
 
+static void CBCalcPlacement(HEADCOMBO *combo);
+static void CBResetPos(HEADCOMBO *combo);
+
 /***********************************************************************
  *           COMBO_Init
  *
@@ -170,6 +172,25 @@ static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc )
     return 0;
 }
 
+static INT combo_get_text_height(const HEADCOMBO *combo)
+{
+    HDC hdc = GetDC(combo->self);
+    HFONT prev_font = 0;
+    TEXTMETRICW tm;
+
+    if (combo->hFont)
+        prev_font = SelectObject(hdc, combo->hFont);
+
+    GetTextMetricsW(hdc, &tm);
+
+    if (prev_font)
+        SelectObject(hdc, prev_font);
+
+    ReleaseDC(combo->self, hdc);
+
+    return tm.tmHeight + 4;
+}
+
 /***********************************************************************
  *           CBGetTextAreaHeight
  *
@@ -182,37 +203,18 @@ static LRESULT COMBO_NCDestroy( HEADCOMBO *lphc )
  * This height was determined through experimentation.
  * CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
  */
-static INT CBGetTextAreaHeight(
-  HWND        hwnd,
-  LPHEADCOMBO lphc)
+static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
 {
-  INT iTextItemHeight;
+  INT item_height, text_height;
 
-  if( lphc->editHeight ) /* explicitly set height */
+  if (clip_item_height && !CB_OWNERDRAWN(lphc))
   {
-    iTextItemHeight = lphc->editHeight;
+      text_height = combo_get_text_height(lphc);
+      if (lphc->item_height < text_height)
+          lphc->item_height = text_height;
   }
-  else
-  {
-    TEXTMETRICW tm;
-    HDC         hDC       = GetDC(hwnd);
-    HFONT       hPrevFont = 0;
-    INT         baseUnitY;
-
-    if (lphc->hFont)
-      hPrevFont = SelectObject( hDC, lphc->hFont );
-
-    GetTextMetricsW(hDC, &tm);
-
-    baseUnitY = tm.tmHeight;
-
-    if( hPrevFont )
-      SelectObject( hDC, hPrevFont );
-
-    ReleaseDC(hwnd, hDC);
+  item_height = lphc->item_height;
 
-    iTextItemHeight = baseUnitY + 4;
-  }
 
   /*
    * Check the ownerdraw case if we haven't asked the parent the size
@@ -223,13 +225,13 @@ static INT CBGetTextAreaHeight(
   {
     MEASUREITEMSTRUCT measureItem;
     RECT              clientRect;
-    INT               originalItemHeight = iTextItemHeight;
+    INT               originalItemHeight = item_height;
     UINT id = (UINT)GetWindowLongPtrW( lphc->self, GWLP_ID );
 
     /*
      * We use the client rect for the width of the item.
      */
-    GetClientRect(hwnd, &clientRect);
+    GetClientRect(lphc->self, &clientRect);
 
     lphc->wState &= ~CBF_MEASUREITEM;
 
@@ -240,10 +242,10 @@ static INT CBGetTextAreaHeight(
     measureItem.CtlID      = id;
     measureItem.itemID     = -1;
     measureItem.itemWidth  = clientRect.right;
-    measureItem.itemHeight = iTextItemHeight - 6; /* ownerdrawn cb is taller */
+    measureItem.itemHeight = item_height - 6; /* ownerdrawn cb is taller */
     measureItem.itemData   = 0;
     SendMessageW(lphc->owner, WM_MEASUREITEM, id, (LPARAM)&measureItem);
-    iTextItemHeight = 6 + measureItem.itemHeight;
+    item_height = 6 + measureItem.itemHeight;
 
     /*
      * Send a second one in the case of a fixed ownerdraw list to calculate the
@@ -264,10 +266,10 @@ static INT CBGetTextAreaHeight(
     /*
      * Keep the size for the next time
      */
-    lphc->editHeight = iTextItemHeight;
+    lphc->item_height = item_height;
   }
 
-  return iTextItemHeight;
+  return item_height;
 }
 
 /***********************************************************************
@@ -277,13 +279,12 @@ static INT CBGetTextAreaHeight(
  * a re-arranging of the contents of the combobox and the recalculation
  * of the size of the "real" control window.
  */
-static void CBForceDummyResize(
-  LPHEADCOMBO lphc)
+static void CBForceDummyResize(LPHEADCOMBO lphc)
 {
   RECT windowRect;
   int newComboHeight;
 
-  newComboHeight = CBGetTextAreaHeight(lphc->self,lphc) + 2*COMBO_YBORDERSIZE();
+  newComboHeight = CBGetTextAreaHeight(lphc, FALSE) + 2*COMBO_YBORDERSIZE();
 
   GetWindowRect(lphc->self, &windowRect);
 
@@ -295,12 +296,17 @@ static void CBForceDummyResize(
    * this will cancel-out in the processing of the WM_WINDOWPOSCHANGING
    * message.
    */
+  lphc->wState |= CBF_NORESIZE;
   SetWindowPos( lphc->self,
                NULL,
                0, 0,
                windowRect.right  - windowRect.left,
                newComboHeight,
                SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
+  lphc->wState &= ~CBF_NORESIZE;
+
+  CBCalcPlacement(lphc);
+  CBResetPos(lphc);
 }
 
 /***********************************************************************
@@ -308,111 +314,70 @@ static void CBForceDummyResize(
  *
  * Set up component coordinates given valid lphc->RectCombo.
  */
-static void CBCalcPlacement(
-  HWND        hwnd,
-  LPHEADCOMBO lphc,
-  LPRECT      lprEdit,
-  LPRECT      lprButton,
-  LPRECT      lprLB)
+static void CBCalcPlacement(HEADCOMBO *combo)
 {
-  /*
-   * Again, start with the client rectangle.
-   */
-  GetClientRect(hwnd, lprEdit);
+    /* Start with the client rectangle. */
+    GetClientRect(combo->self, &combo->textRect);
 
-  /*
-   * Remove the borders
-   */
-  InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
+    /* Remove the borders */
+    InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
 
-  /*
-   * Chop off the bottom part to fit with the height of the text area.
-   */
-  lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
-
-  /*
-   * The button starts the same vertical position as the text area.
-   */
-  CopyRect(lprButton, lprEdit);
+    /* Chop off the bottom part to fit with the height of the text area. */
+    combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
 
-  /*
-   * If the combobox is "simple" there is no button.
-   */
-  if( CB_GETTYPE(lphc) == CBS_SIMPLE )
-    lprButton->left = lprButton->right = lprButton->bottom = 0;
-  else
-  {
-    /*
-     * Let's assume the combobox button is the same width as the
-     * scrollbar button.
-     * size the button horizontally and cut-off the text area.
-     */
-    lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
-    lprEdit->right  = lprButton->left;
-  }
+    /* The button starts the same vertical position as the text area. */
+    combo->buttonRect = combo->textRect;
 
-  /*
-   * In the case of a dropdown, there is an additional spacing between the
-   * text area and the button.
-   */
-  if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
-  {
-    lprEdit->right -= COMBO_EDITBUTTONSPACE();
-  }
+    /* If the combobox is "simple" there is no button. */
+    if (CB_GETTYPE(combo) == CBS_SIMPLE)
+        combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
+    else
+    {
+        /*
+         * Let's assume the combobox button is the same width as the
+         * scrollbar button.
+         * size the button horizontally and cut-off the text area.
+         */
+        combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
+        combo->textRect.right = combo->buttonRect.left;
+    }
 
-  /*
-   * If we have an edit control, we space it away from the borders slightly.
-   */
-  if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
-  {
-    InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
-  }
+    /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
+    if (CB_GETTYPE(combo) == CBS_DROPDOWN)
+        combo->textRect.right -= COMBO_EDITBUTTONSPACE();
 
-  /*
-   * Adjust the size of the listbox popup.
-   */
-  if( CB_GETTYPE(lphc) == CBS_SIMPLE )
-  {
-    /*
-     * Use the client rectangle to initialize the listbox rectangle
-     */
-    GetClientRect(hwnd, lprLB);
+    /* If we have an edit control, we space it away from the borders slightly. */
+    if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
+        InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
 
-    /*
-     * Then, chop-off the top part.
-     */
-    lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
-  }
-  else
-  {
-    /*
-     * Make sure the dropped width is as large as the combobox itself.
-     */
-    if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
+    /* Adjust the size of the listbox popup. */
+    if (CB_GETTYPE(combo) == CBS_SIMPLE)
     {
-      lprLB->right  = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
-
-      /*
-       * In the case of a dropdown, the popup listbox is offset to the right.
-       * so, we want to make sure it's flush with the right side of the
-       * combobox
-       */
-      if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
-       lprLB->right -= COMBO_EDITBUTTONSPACE();
+        GetClientRect(combo->self, &combo->droppedRect);
+        combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
     }
     else
-       lprLB->right = lprLB->left + lphc->droppedWidth;
-  }
-
-  /* don't allow negative window width */
-  if (lprEdit->right < lprEdit->left)
-    lprEdit->right = lprEdit->left;
+    {
+        /* Make sure the dropped width is as large as the combobox itself. */
+        if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
+        {
+            combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
 
-  TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
+            /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
+               with the right side of the combobox */
+            if (CB_GETTYPE(combo) == CBS_DROPDOWN)
+                combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
+        }
+        else
+            combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
+    }
 
-  TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
+    /* Disallow negative window width */
+    if (combo->textRect.right < combo->textRect.left)
+        combo->textRect.right = combo->textRect.left;
 
-  TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
+    TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
+            wine_dbgstr_rect(&combo->droppedRect));
 }
 
 /***********************************************************************
@@ -444,11 +409,9 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG
 
   lphc->owner = hwndParent;
 
-  /*
-   * The item height and dropped width are not set when the control
-   * is created.
-   */
-  lphc->droppedWidth = lphc->editHeight = 0;
+  lphc->droppedWidth = 0;
+
+  lphc->item_height = combo_get_text_height(lphc);
 
   /*
    * The first time we go through, we want to measure the ownerdraw item
@@ -473,7 +436,7 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG
        * recalculated.
        */
       GetClientRect( hwnd, &lphc->droppedRect );
-      CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
+      CBCalcPlacement(lphc);
 
       /*
        * Adjust the position of the popup listbox if it's necessary
@@ -589,10 +552,13 @@ static LRESULT COMBO_Create( HWND hwnd, LPHEADCOMBO lphc, HWND hwndParent, LONG
  *
  * Paint combo button (normal, pressed, and disabled states).
  */
-static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
+static void CBPaintButton(HEADCOMBO *lphc, HDC hdc)
 {
     UINT buttonState = DFCS_SCROLLCOMBOBOX;
 
+    if (IsRectEmpty(&lphc->buttonRect))
+        return;
+
     if( lphc->wState & CBF_NOREDRAW )
       return;
 
@@ -603,7 +569,7 @@ static void CBPaintButton( LPHEADCOMBO lphc, HDC hdc, RECT rectButton)
     if (CB_DISABLED(lphc))
        buttonState |= DFCS_INACTIVE;
 
-    DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
+    DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
 }
 
 /***********************************************************************
@@ -777,16 +743,13 @@ static void CBPaintText(HEADCOMBO *lphc, HDC hdc_paint)
 /***********************************************************************
  *           CBPaintBorder
  */
-static void CBPaintBorder(
-  HWND            hwnd,
-  const HEADCOMBO *lphc,
-  HDC             hdc)
+static void CBPaintBorder(const HEADCOMBO *lphc, HDC hdc)
 {
   RECT clientRect;
 
   if (CB_GETTYPE(lphc) != CBS_SIMPLE)
   {
-    GetClientRect(hwnd, &clientRect);
+    GetClientRect(lphc->self, &clientRect);
   }
   else
   {
@@ -857,10 +820,9 @@ static LRESULT COMBO_Paint(HEADCOMBO *lphc, HDC hdc)
     /*
      * In non 3.1 look, there is a sunken border on the combobox
      */
-    CBPaintBorder(lphc->self, lphc, hdc);
+    CBPaintBorder(lphc, hdc);
 
-    if (!IsRectEmpty(&lphc->buttonRect))
-        CBPaintButton(lphc, hdc, lphc->buttonRect);
+    CBPaintButton(lphc, hdc);
 
     /* paint the edit control padding area */
     if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
@@ -1391,50 +1353,45 @@ static LRESULT COMBO_GetText( HEADCOMBO *lphc, INT count, LPWSTR buf )
  * This function sets window positions according to the updated
  * component placement struct.
  */
-static void CBResetPos(
-  LPHEADCOMBO lphc,
-  const RECT  *rectEdit,
-  const RECT  *rectLB,
-  BOOL        bRedraw)
+static void CBResetPos(HEADCOMBO *combo)
 {
-   BOOL        bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
-
-   /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
-    * sizing messages */
-
-   if( lphc->wState & CBF_EDIT )
-     SetWindowPos( lphc->hWndEdit, 0,
-                  rectEdit->left, rectEdit->top,
-                  rectEdit->right - rectEdit->left,
-                  rectEdit->bottom - rectEdit->top,
-                       SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
-
-   SetWindowPos( lphc->hWndLBox, 0,
-                rectLB->left, rectLB->top,
-                 rectLB->right - rectLB->left,
-                rectLB->bottom - rectLB->top,
-                  SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
-
-   if( bDrop )
-   {
-       if( lphc->wState & CBF_DROPPED )
-       {
-           lphc->wState &= ~CBF_DROPPED;
-           ShowWindow( lphc->hWndLBox, SW_HIDE );
-       }
+    BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
+
+    /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
+     * sizing messages */
+    if (combo->wState & CBF_EDIT)
+        SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
+                combo->textRect.right - combo->textRect.left,
+                combo->textRect.bottom - combo->textRect.top,
+                SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
+
+    SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
+            combo->droppedRect.right - combo->droppedRect.left,
+            combo->droppedRect.bottom - combo->droppedRect.top,
+            SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
+
+    if (drop)
+    {
+        if (combo->wState & CBF_DROPPED)
+        {
+           combo->wState &= ~CBF_DROPPED;
+           ShowWindow(combo->hWndLBox, SW_HIDE);
+        }
 
-       if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
-           RedrawWindow( lphc->self, NULL, 0,
-                           RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
-   }
+        if (!(combo->wState & CBF_NOREDRAW))
+            RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
+    }
 }
 
 
 /***********************************************************************
  *           COMBO_Size
  */
-static void COMBO_Size( LPHEADCOMBO lphc )
+static void COMBO_Size( HEADCOMBO *lphc )
 {
+    if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
+        return;
+
   /*
    * Those controls are always the same height. So we have to make sure
    * they are not resized to another value.
@@ -1447,7 +1404,7 @@ static void COMBO_Size( LPHEADCOMBO lphc )
     GetWindowRect(lphc->self, &rc);
     curComboHeight = rc.bottom - rc.top;
     curComboWidth = rc.right - rc.left;
-    newComboHeight = CBGetTextAreaHeight(lphc->self, lphc) + 2*COMBO_YBORDERSIZE();
+    newComboHeight = CBGetTextAreaHeight(lphc, TRUE) + 2*COMBO_YBORDERSIZE();
 
     /*
      * Resizing a combobox has another side effect, it resizes the dropped
@@ -1467,18 +1424,18 @@ static void COMBO_Size( LPHEADCOMBO lphc )
     /*
      * Restore original height
      */
-    if( curComboHeight != newComboHeight )
-      SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
-            SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOREDRAW);
+    if (curComboHeight != newComboHeight)
+    {
+        lphc->wState |= CBF_NORESIZE;
+        SetWindowPos(lphc->self, 0, 0, 0, curComboWidth, newComboHeight,
+                SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOREDRAW);
+        lphc->wState &= ~CBF_NORESIZE;
+    }
   }
 
-  CBCalcPlacement(lphc->self,
-                 lphc,
-                 &lphc->textRect,
-                 &lphc->buttonRect,
-                 &lphc->droppedRect);
+  CBCalcPlacement(lphc);
 
-  CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
+  CBResetPos(lphc);
 }
 
 
@@ -1487,10 +1444,8 @@ static void COMBO_Size( LPHEADCOMBO lphc )
  */
 static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
 {
-  /*
-   * Set the font
-   */
   lphc->hFont = hFont;
+  lphc->item_height = combo_get_text_height(lphc);
 
   /*
    * Propagate to owned windows.
@@ -1504,13 +1459,9 @@ static void COMBO_Font( LPHEADCOMBO lphc, HFONT hFont, BOOL bRedraw )
    */
   if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
   {
-    CBCalcPlacement(lphc->self,
-                   lphc,
-                   &lphc->textRect,
-                   &lphc->buttonRect,
-                   &lphc->droppedRect);
+    CBCalcPlacement(lphc);
 
-    CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
+    CBResetPos(lphc);
   }
   else
   {
@@ -1530,20 +1481,16 @@ static LRESULT COMBO_SetItemHeight( LPHEADCOMBO lphc, INT index, INT height )
    {
        if( height < 32768 )
        {
-           lphc->editHeight = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
+           lphc->item_height = height + 2;  /* Is the 2 for 2*EDIT_CONTROL_PADDING? */
 
         /*
          * Redo the layout of the control.
          */
         if ( CB_GETTYPE(lphc) == CBS_SIMPLE)
         {
-          CBCalcPlacement(lphc->self,
-                          lphc,
-                          &lphc->textRect,
-                          &lphc->buttonRect,
-                          &lphc->droppedRect);
+          CBCalcPlacement(lphc);
 
-          CBResetPos( lphc, &lphc->textRect, &lphc->droppedRect, TRUE );
+          CBResetPos(lphc);
         }
         else
         {
@@ -1794,8 +1741,7 @@ static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam
     }
 
     case WM_SIZE:
-        if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE))
-            COMBO_Size( lphc );
+        COMBO_Size( lphc );
         return  TRUE;
 
     case WM_SETFONT:
@@ -2017,7 +1963,7 @@ static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam
     case CB_GETITEMHEIGHT:
         if ((INT)wParam >= 0) /* listbox item */
             return SendMessageW(lphc->hWndLBox, LB_GETITEMHEIGHT, wParam, 0);
-        return  CBGetTextAreaHeight(hwnd, lphc);
+        return CBGetTextAreaHeight(lphc, FALSE);
 
     case CB_RESETCONTENT:
         SendMessageW(lphc->hWndLBox, LB_RESETCONTENT, 0, 0);
@@ -2060,7 +2006,7 @@ static LRESULT CALLBACK COMBO_WindowProc( HWND hwnd, UINT message, WPARAM wParam
             lphc->droppedWidth = 0;
 
         /* recalculate the combobox area */
-        CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
+        CBCalcPlacement(lphc);
 
         /* fall through */
     case CB_GETDROPPEDWIDTH:
index 3c3714a..c3c39be 100644 (file)
@@ -30,7 +30,6 @@
 #include "commctrl.h"
 #include "comctl32.h"
 #include "wine/debug.h"
-#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(comboex);
 
@@ -632,14 +631,14 @@ static INT COMBOEX_InsertItemW (COMBOEX_INFO *infoPtr, COMBOBOXEXITEMW const *ci
     if (item->mask & CBEIF_TEXT) {
        INT len = 0;
 
-        if (is_textW(cit->pszText)) len = strlenW (cit->pszText);
+        if (is_textW(cit->pszText)) len = lstrlenW (cit->pszText);
        if (len > 0) {
             item->pszText = Alloc ((len + 1)*sizeof(WCHAR));
            if (!item->pszText) {
                Free(item);
                return -1;
            }
-           strcpyW (item->pszText, cit->pszText);
+           lstrcpyW (item->pszText, cit->pszText);
        }
        else if (cit->pszText == LPSTR_TEXTCALLBACKW)
            item->pszText = LPSTR_TEXTCALLBACKW;
@@ -765,11 +764,11 @@ static BOOL COMBOEX_SetItemW (COMBOEX_INFO *infoPtr, const COMBOBOXEXITEMW *cit)
        INT len = 0;
 
        COMBOEX_FreeText(item);
-        if (is_textW(cit->pszText)) len = strlenW(cit->pszText);
+        if (is_textW(cit->pszText)) len = lstrlenW(cit->pszText);
        if (len > 0) {
             item->pszText = Alloc ((len + 1)*sizeof(WCHAR));
            if (!item->pszText) return FALSE;
-           strcpyW(item->pszText, cit->pszText);
+           lstrcpyW(item->pszText, cit->pszText);
        } else if (cit->pszText == LPSTR_TEXTCALLBACKW)
            item->pszText = LPSTR_TEXTCALLBACKW;
         item->cchTextMax = cit->cchTextMax;
@@ -1404,7 +1403,7 @@ static LRESULT COMBOEX_DrawItem (COMBOEX_INFO *infoPtr, DRAWITEMSTRUCT const *di
     str = COMBOEX_GetText(infoPtr, item);
     if (!str) str = nil;
 
-    len = strlenW (str);
+    len = lstrlenW (str);
     GetTextExtentPoint32W (dis->hDC, str, len, &txtsize);
 
     if (dis->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)) {
index 40baabb..6f9c951 100644 (file)
@@ -88,6 +88,9 @@ extern HBRUSH  COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN;
 
 #define IDT_CHECK        401
 
+/* Command Link arrow */
+#define IDB_CMDLINK      402
+
 
 /* Cursors */
 #define IDC_MOVEBUTTON                  102
@@ -115,6 +118,9 @@ extern HBRUSH  COMCTL32_hPattern55AABrush DECLSPEC_HIDDEN;
 #define IDS_BUTTON_CANCEL 3004
 #define IDS_BUTTON_CLOSE  3005
 
+#define IDS_TD_EXPANDED   3020
+#define IDS_TD_COLLAPSED  3021
+
 #ifndef __REACTOS__
 #define WM_SYSTIMER     0x0118
 #endif
@@ -153,8 +159,8 @@ typedef struct
    RECT           droppedRect;
    INT            droppedIndex;
    INT            fixedOwnerDrawHeight;
-   INT            droppedWidth;   /* last two are not used unless set */
-   INT            editHeight;     /* explicitly */
+   INT            droppedWidth;   /* not used unless set explicitly */
+   INT            item_height;
    INT            visibleItems;
 } HEADCOMBO, *LPHEADCOMBO;
 
@@ -299,6 +305,11 @@ extern void THEMING_Initialize(void) DECLSPEC_HIDDEN;
 #endif
 extern void THEMING_Uninitialize(void) DECLSPEC_HIDDEN;
 extern LRESULT THEMING_CallOriginalClass(HWND, UINT, WPARAM, LPARAM) DECLSPEC_HIDDEN;
-extern void THEMING_SetSubclassData(HWND, ULONG_PTR) DECLSPEC_HIDDEN;
+
+#ifdef __REACTOS__
+#define IDI_SHIELD  32518
+#define wcsnicmp _wcsnicmp
+#define GetDpiForWindow(PVOID) 96
+#endif
 
 #endif  /* __WINE_COMCTL32_H */
index 639db05..753b0f5 100644 (file)
@@ -26,8 +26,6 @@
  *     COMCTL32.DLL (internally).
  *
  */
-#include "config.h"
-#include "wine/port.h"
 
 #include <stdarg.h>
 #include <string.h>
@@ -47,7 +45,6 @@
 #include "objbase.h"
 #include "winerror.h"
 
-#include "wine/unicode.h"
 #include "comctl32.h"
 
 #include "wine/debug.h"
@@ -311,7 +308,7 @@ static void MRU_SaveChanged ( LPWINEMRULIST mp )
     if (mp->wineFlags & WMRUF_CHANGED) {
        mp->wineFlags &= ~WMRUF_CHANGED;
        err = RegSetValueExW(newkey, strMRUList, 0, REG_SZ, (LPBYTE)mp->realMRU,
-                            (strlenW(mp->realMRU) + 1)*sizeof(WCHAR));
+                            (lstrlenW(mp->realMRU) + 1)*sizeof(WCHAR));
        if (err) {
            ERR("error saving MRUList, err=%d\n", err);
        }
@@ -470,7 +467,7 @@ INT WINAPI AddMRUData (HANDLE hList, LPCVOID lpData, DWORD cbData)
 
     if ((replace = FindMRUData (hList, lpData, cbData, NULL)) >= 0) {
         /* Item exists, just move it to the front */
-        LPWSTR pos = strchrW(mp->realMRU, replace + 'a');
+        LPWSTR pos = wcschr(mp->realMRU, replace + 'a');
         while (pos > mp->realMRU)
         {
             pos[0] = pos[-1];
@@ -555,7 +552,7 @@ INT WINAPI AddMRUStringW(HANDLE hList, LPCWSTR lpszString)
     }
 
     return AddMRUData(hList, lpszString,
-                      (strlenW(lpszString) + 1) * sizeof(WCHAR));
+                      (lstrlenW(lpszString) + 1) * sizeof(WCHAR));
 }
 
 /**************************************************************************
@@ -747,8 +744,8 @@ HANDLE WINAPI CreateMRUListLazyW (const MRUINFOW *infoW, DWORD dwParam2,
 
     mp = Alloc(sizeof(WINEMRULIST));
     memcpy(&mp->extview, infoW, sizeof(MRUINFOW));
-    mp->extview.lpszSubKey = Alloc((strlenW(infoW->lpszSubKey) + 1) * sizeof(WCHAR));
-    strcpyW(mp->extview.lpszSubKey, infoW->lpszSubKey);
+    mp->extview.lpszSubKey = Alloc((lstrlenW(infoW->lpszSubKey) + 1) * sizeof(WCHAR));
+    lstrcpyW(mp->extview.lpszSubKey, infoW->lpszSubKey);
     mp->isUnicode = TRUE;
 
     return create_mru_list(mp);
index a3bbd5f..f852824 100644 (file)
@@ -678,20 +678,24 @@ void WINAPI DrawStatusTextW (HDC hdc, LPCRECT lprc, LPCWSTR text, UINT style)
 {
     RECT r = *lprc;
     UINT border = BDR_SUNKENOUTER;
+    COLORREF oldbkcolor;
 
     if (style & SBT_POPOUT)
         border = BDR_RAISEDOUTER;
     else if (style & SBT_NOBORDERS)
         border = 0;
 
-    DrawEdge (hdc, &r, border, BF_RECT|BF_ADJUST);
+    oldbkcolor = SetBkColor (hdc, comctl32_color.clrBtnFace);
+    DrawEdge (hdc, &r, border, BF_MIDDLE|BF_RECT|BF_ADJUST);
 
     /* now draw text */
     if (text) {
         int oldbkmode = SetBkMode (hdc, TRANSPARENT);
+        COLORREF oldtextcolor;
         UINT align = DT_LEFT;
         int strCnt = 0;
 
+        oldtextcolor = SetTextColor (hdc, comctl32_color.clrBtnText);
         if (style & SBT_RTLREADING)
             FIXME("Unsupported RTL style!\n");
         r.left += 3;
@@ -711,8 +715,11 @@ void WINAPI DrawStatusTextW (HDC hdc, LPCRECT lprc, LPCWSTR text, UINT style)
         } while(*text++);
 
         if (strCnt) DrawTextW (hdc, text - strCnt, -1, &r, align|DT_VCENTER|DT_SINGLELINE|DT_NOPREFIX);
-       SetBkMode(hdc, oldbkmode);
+        SetBkMode (hdc, oldbkmode);
+        SetTextColor (hdc, oldtextcolor);
     }
+
+    SetBkColor (hdc, oldbkcolor);
 }
 
 
@@ -856,7 +863,7 @@ CreateUpDownControl (DWORD style, INT x, INT y, INT cx, INT cy,
  *
  * NOTES
  *     This function is just a dummy - all the controls are registered at
- *     the DLL initialization time. See InitCommonContolsEx for details.
+ *     the DLL initialization time. See InitCommonControlsEx for details.
  */
 
 VOID WINAPI
index ccd9373..6d124ce 100644 (file)
@@ -23,9 +23,7 @@
  *
  * TODO:
  *   - EDITBALLOONTIP structure
- *   - EM_GETCUEBANNER/Edit_GetCueBannerText
  *   - EM_HIDEBALLOONTIP/Edit_HideBalloonTip
- *   - EM_SETCUEBANNER/Edit_SetCueBannerText
  *   - EM_SHOWBALLOONTIP/Edit_ShowBalloonTip
  *   - EM_GETIMESTATUS, EM_SETIMESTATUS
  *   - EN_ALIGN_LTR_EC
@@ -34,8 +32,6 @@
  *
  */
 
-#include "config.h"
-
 #include <stdarg.h>
 #include <string.h>
 #include <stdlib.h>
@@ -53,7 +49,6 @@
 #include "commctrl.h"
 #include "uxtheme.h"
 #include "vsstyle.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "wine/heap.h"
 
@@ -139,6 +134,9 @@ typedef struct
                                           should be sent to the first parent. */
        HWND hwndListBox;               /* handle of ComboBox's listbox or NULL */
        INT wheelDeltaRemainder;        /* scroll wheel delta left over after scrolling whole lines */
+       WCHAR *cue_banner_text;
+       BOOL cue_banner_draw_focused;
+
        /*
         *      only for multi line controls
         */
@@ -183,7 +181,7 @@ static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap);
  */
 static inline BOOL EDIT_EM_CanUndo(const EDITSTATE *es)
 {
-       return (es->undo_insert_count || strlenW(es->undo_text));
+       return (es->undo_insert_count || lstrlenW(es->undo_text));
 }
 
 
@@ -219,7 +217,7 @@ static HBRUSH EDIT_NotifyCtlColor(EDITSTATE *es, HDC hdc)
 static inline UINT get_text_length(EDITSTATE *es)
 {
     if(es->text_length == (UINT)-1)
-        es->text_length = strlenW(es->text);
+        es->text_length = lstrlenW(es->text);
     return es->text_length;
 }
 
@@ -527,7 +525,7 @@ static void EDIT_BuildLineDefs_ML(EDITSTATE *es, INT istart, INT iend, INT delta
                /* Mark type of line termination */
                if (!(*cp)) {
                        current_line->ending = END_0;
-                       current_line->net_length = strlenW(current_position);
+                       current_line->net_length = lstrlenW(current_position);
                } else if ((cp > current_position) && (*(cp - 1) == '\r')) {
                        current_line->ending = END_SOFT;
                        current_line->net_length = cp - current_position - 1;
@@ -1052,15 +1050,11 @@ static LRESULT EDIT_EM_PosFromChar(EDITSTATE *es, INT index, BOOL after_wrap)
                lw = line_def->width;
                w = es->format_rect.right - es->format_rect.left;
                if (line_def->ssa)
-               {
                        ScriptStringCPtoX(line_def->ssa, (index - 1) - li, TRUE, &x);
-                       x -= es->x_offset;
-               }
-               else
 #ifdef __REACTOS__ /* CORE-15780 */
-                       x = (lw > 0 ? es->x_offset : x - es->x_offset);
+               x = (lw > 0 ? es->x_offset : x - es->x_offset);
 #else
-                       x = es->x_offset;
+               x = es->x_offset;
 #endif
 
                if (es->style & ES_RIGHT)
@@ -1836,7 +1830,6 @@ static void EDIT_EM_ScrollCaret(EDITSTATE *es)
                }
        }
 
-    if(es->flags & EF_FOCUSED)
        EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
 }
 
@@ -2248,6 +2241,12 @@ static void EDIT_PaintLine(EDITSTATE *es, HDC dc, INT line, BOOL rev)
                x += EDIT_PaintText(es, dc, x, y, line, e - li, li + ll - e, FALSE);
        } else
                x += EDIT_PaintText(es, dc, x, y, line, 0, ll, FALSE);
+
+       if (es->cue_banner_text && es->text_length == 0 && (!(es->flags & EF_FOCUSED) || es->cue_banner_draw_focused))
+       {
+              SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT));
+              TextOutW(dc, x, y, es->cue_banner_text, lstrlenW(es->cue_banner_text));
+       }
 }
 
 
@@ -2537,7 +2536,7 @@ static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_r
                memcpy(buf, es->text + s, bufl * sizeof(WCHAR));
                buf[bufl] = 0; /* ensure 0 termination */
                /* now delete */
-               strcpyW(es->text + s, es->text + e);
+               lstrcpyW(es->text + s, es->text + e);
                 text_buffer_changed(es);
        }
        if (strl) {
@@ -2565,7 +2564,7 @@ static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_r
                /* if text is too long undo all changes */
                if (honor_limit && !(es->style & ES_AUTOVSCROLL) && (es->line_count > vlc)) {
                        if (strl)
-                               strcpyW(es->text + e, es->text + e + strl);
+                               lstrcpyW(es->text + e, es->text + e + strl);
                        if (e != s)
                                for (i = 0 , p = es->text ; i < e - s ; i++)
                                        p[i + s] = buf[i];
@@ -2585,7 +2584,7 @@ static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_r
                /* remove chars that don't fit */
                if (honor_limit && !(es->style & ES_AUTOHSCROLL) && (es->text_width > fw)) {
                        while ((es->text_width > fw) && s + strl >= s) {
-                               strcpyW(es->text + s + strl - 1, es->text + s + strl);
+                               lstrcpyW(es->text + s + strl - 1, es->text + s + strl);
                                strl--;
                                es->text_length = -1;
                                EDIT_InvalidateUniscribeData(es);
@@ -2598,7 +2597,7 @@ static void EDIT_EM_ReplaceSel(EDITSTATE *es, BOOL can_undo, const WCHAR *lpsz_r
 
        if (e != s) {
                if (can_undo) {
-                       utl = strlenW(es->undo_text);
+                       utl = lstrlenW(es->undo_text);
                        if (!es->undo_insert_count && (*es->undo_text && (s == es->undo_position))) {
                                /* undo-buffer is extended to the right */
                                EDIT_MakeUndoFit(es, utl + e - s);
@@ -2742,6 +2741,36 @@ static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit)
     es->buffer_limit = limit;
 }
 
+static BOOL is_cjk(HDC dc)
+{
+    const DWORD FS_DBCS_MASK = FS_JISJAPAN|FS_CHINESESIMP|FS_WANSUNG|FS_CHINESETRAD|FS_JOHAB;
+    FONTSIGNATURE fs;
+
+    switch (GdiGetCodePage(dc)) {
+    case 932: case 936: case 949: case 950: case 1361:
+        return TRUE;
+    default:
+        return (GetTextCharsetInfo(dc, &fs, 0) != DEFAULT_CHARSET &&
+                (fs.fsCsb[0] & FS_DBCS_MASK));
+    }
+}
+
+static int get_cjk_fontinfo_margin(int width, int side_bearing)
+{
+    int margin;
+    if (side_bearing < 0)
+        margin = min(-side_bearing, width/2);
+    else
+        margin = 0;
+    return margin;
+}
+
+struct char_width_info {
+    INT min_lsb, min_rsb, unknown;
+};
+
+/* Undocumented gdi32 export */
+extern BOOL WINAPI GetCharWidthInfo(HDC, struct char_width_info *);
 
 /*********************************************************************
  *
@@ -2751,26 +2780,10 @@ static void EDIT_EM_SetLimitText(EDITSTATE *es, UINT limit)
  * action wParam despite what the docs say. EC_USEFONTINFO calculates the
  * margin according to the textmetrics of the current font.
  *
- * When EC_USEFONTINFO is used in the non_cjk case the margins only
- * change if the edit control is equal to or larger than a certain
- * size.  Though there is an exception for the empty client rect case
- * with small font sizes.
+ * When EC_USEFONTINFO is used, the margins only change if the edit control is
+ * equal to or larger than a certain size. The empty client rect is treated as
+ * 80 pixels width.
  */
-static BOOL is_cjk(UINT charset)
-{
-    switch(charset)
-    {
-    case SHIFTJIS_CHARSET:
-    case HANGUL_CHARSET:
-    case GB2312_CHARSET:
-    case CHINESEBIG5_CHARSET:
-        return TRUE;
-    }
-    /* HANGUL_CHARSET is strange, though treated as CJK by Win 8, it is
-     * not by other versions including Win 10. */
-    return FALSE;
-}
-
 static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
                               WORD left, WORD right, BOOL repaint)
 {
@@ -2782,26 +2795,30 @@ static void EDIT_EM_SetMargins(EDITSTATE *es, INT action,
         if (es->font && (left == EC_USEFONTINFO || right == EC_USEFONTINFO)) {
             HDC dc = GetDC(es->hwndSelf);
             HFONT old_font = SelectObject(dc, es->font);
-            LONG width = GdiGetCharDimensions(dc, &tm, NULL);
+            LONG width = GdiGetCharDimensions(dc, &tm, NULL), rc_width;
             RECT rc;
 
             /* The default margins are only non zero for TrueType or Vector fonts */
             if (tm.tmPitchAndFamily & ( TMPF_VECTOR | TMPF_TRUETYPE )) {
-                if (!is_cjk(tm.tmCharSet)) {
-                    default_left_margin = width / 2;
-                    default_right_margin = width / 2;
+                struct char_width_info width_info;
 
-                    GetClientRect(es->hwndSelf, &rc);
-                    if (rc.right - rc.left < (width / 2 + width) * 2 &&
-                        (width >= 28 || !IsRectEmpty(&rc)) ) {
-                        default_left_margin = es->left_margin;
-                        default_right_margin = es->right_margin;
-                    }
-                } else {
-                    /* FIXME: figure out the CJK values. They are not affected by the client rect. */
+                if (is_cjk(dc) && GetCharWidthInfo(dc, &width_info))
+                {
+                    default_left_margin = get_cjk_fontinfo_margin(width, width_info.min_lsb);
+                    default_right_margin = get_cjk_fontinfo_margin(width, width_info.min_rsb);
+                }
+                else
+                {
                     default_left_margin = width / 2;
                     default_right_margin = width / 2;
                 }
+
+                GetClientRect(es->hwndSelf, &rc);
+                rc_width = !IsRectEmpty(&rc) ? rc.right - rc.left : 80;
+                if (rc_width < default_left_margin + default_right_margin + width * 2) {
+                    default_left_margin = es->left_margin;
+                    default_right_margin = es->right_margin;
+                }
             }
             SelectObject(dc, old_font);
             ReleaseDC(es->hwndSelf, dc);
@@ -2919,11 +2936,11 @@ static BOOL EDIT_EM_Undo(EDITSTATE *es)
        if( es->style & ES_READONLY )
             return !(es->style & ES_MULTILINE);
 
-       ulength = strlenW(es->undo_text);
+       ulength = lstrlenW(es->undo_text);
 
        utext = heap_alloc((ulength + 1) * sizeof(WCHAR));
 
-       strcpyW(utext, es->undo_text);
+       lstrcpyW(utext, es->undo_text);
 
        TRACE("before UNDO:insertion length = %d, deletion buffer = %s\n",
                     es->undo_insert_count, debugstr_w(utext));
@@ -2974,9 +2991,9 @@ static void EDIT_WM_Paste(EDITSTATE *es)
        OpenClipboard(es->hwndSelf);
        if ((hsrc = GetClipboardData(CF_UNICODETEXT))) {
                src = GlobalLock(hsrc);
-                len = strlenW(src);
+                len = lstrlenW(src);
                /* Protect single-line edit against pasting new line character */
-               if (!(es->style & ES_MULTILINE) && ((ptr = strchrW(src, '\n')))) {
+               if (!(es->style & ES_MULTILINE) && ((ptr = wcschr(src, '\n')))) {
                        len = ptr - src;
                        if (len && src[len - 1] == '\r')
                                --len;
@@ -3246,7 +3263,7 @@ static INT EDIT_WM_GetText(const EDITSTATE *es, INT count, LPWSTR dst)
         return 0;
 
     lstrcpynW(dst, es->text, count);
-    return strlenW(dst);
+    return lstrlenW(dst);
 }
 
 /*********************************************************************
@@ -3381,22 +3398,17 @@ static LRESULT EDIT_WM_KeyDown(EDITSTATE *es, INT key)
                                else
                                        EDIT_WM_Clear(es);
                        } else {
-                               if (shift) {
+                               EDIT_EM_SetSel(es, ~0u, 0, FALSE);
+                               if (shift)
                                        /* delete character left of caret */
-                                       EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
                                        EDIT_MoveBackward(es, TRUE);
-                                       EDIT_WM_Clear(es);
-                               } else if (control) {
+                               else if (control)
                                        /* delete to end of line */
-                                       EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
                                        EDIT_MoveEnd(es, TRUE, FALSE);
-                                       EDIT_WM_Clear(es);
-                               } else {
+                               else
                                        /* delete character right of caret */
-                                       EDIT_EM_SetSel(es, (UINT)-1, 0, FALSE);
                                        EDIT_MoveForward(es, TRUE);
-                                       EDIT_WM_Clear(es);
-                               }
+                               EDIT_WM_Clear(es);
                        }
                }
                break;
@@ -3775,6 +3787,29 @@ static void EDIT_WM_SetFocus(HTHEME theme, EDITSTATE *es)
 }
 
 
+static DWORD get_font_margins(HDC hdc, const TEXTMETRICW *tm)
+{
+       ABC abc[256];
+       SHORT left, right;
+       UINT i;
+
+       if (!(tm->tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE)))
+               return MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO);
+
+       if (!is_cjk(hdc))
+               return MAKELONG(EC_USEFONTINFO, EC_USEFONTINFO);
+
+       if (!GetCharABCWidthsW(hdc, 0, 255, abc))
+               return 0;
+
+       left = right = 0;
+       for (i = 0; i < ARRAY_SIZE(abc); i++) {
+               if (-abc[i].abcA > right) right = -abc[i].abcA;
+               if (-abc[i].abcC > left ) left  = -abc[i].abcC;
+       }
+       return MAKELONG(left, right);
+}
+
 /*********************************************************************
  *
  *     WM_SETFONT
@@ -3790,6 +3825,7 @@ static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw)
        HDC dc;
        HFONT old_font = 0;
        RECT clientRect;
+       DWORD margins;
 
        es->font = font;
        EDIT_InvalidateUniscribeData(es);
@@ -3799,6 +3835,7 @@ static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw)
        GetTextMetricsW(dc, &tm);
        es->line_height = tm.tmHeight;
        es->char_width = tm.tmAveCharWidth;
+       margins = get_font_margins(dc, &tm);
        if (font)
                SelectObject(dc, old_font);
        ReleaseDC(es->hwndSelf, dc);
@@ -3806,8 +3843,9 @@ static void EDIT_WM_SetFont(EDITSTATE *es, HFONT font, BOOL redraw)
        /* Reset the format rect and the margins */
        GetClientRect(es->hwndSelf, &clientRect);
        EDIT_SetRectNP(es, &clientRect);
-       EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
-                          EC_USEFONTINFO, EC_USEFONTINFO, FALSE);
+       if (margins)
+               EDIT_EM_SetMargins(es, EC_LEFTMARGIN | EC_RIGHTMARGIN,
+                                  LOWORD(margins), HIWORD(margins), FALSE);
 
        if (es->style & ES_MULTILINE)
                EDIT_BuildLineDefs_ML(es, 0, get_text_length(es), 0, NULL);
@@ -3865,7 +3903,7 @@ static void EDIT_WM_SetText(EDITSTATE *es, LPCWSTR text)
     if (text)
     {
        TRACE("%s\n", debugstr_w(text));
-       EDIT_EM_ReplaceSel(es, FALSE, text, strlenW(text), FALSE, FALSE);
+       EDIT_EM_ReplaceSel(es, FALSE, text, lstrlenW(text), FALSE, FALSE);
     }
     else
     {
@@ -4264,6 +4302,55 @@ static LRESULT EDIT_EM_GetThumb(EDITSTATE *es)
                         EDIT_WM_HScroll(es, EM_GETTHUMB, 0));
 }
 
+static inline WCHAR *heap_strdupW(const WCHAR *str)
+{
+    int len = lstrlenW(str) + 1;
+    WCHAR *ret = heap_alloc(len * sizeof(WCHAR));
+    lstrcpyW(ret, str);
+    return ret;
+}
+
+/*********************************************************************
+ *
+ *     EM_SETCUEBANNER
+ *
+ */
+static BOOL EDIT_EM_SetCueBanner(EDITSTATE *es, BOOL draw_focused, const WCHAR *cue_text)
+{
+    if (es->style & ES_MULTILINE || !cue_text)
+        return FALSE;
+
+    heap_free(es->cue_banner_text);
+    es->cue_banner_text = heap_strdupW(cue_text);
+    es->cue_banner_draw_focused = draw_focused;
+
+    return TRUE;
+}
+
+/*********************************************************************
+ *
+ *     EM_GETCUEBANNER
+ *
+ */
+static BOOL EDIT_EM_GetCueBanner(EDITSTATE *es, WCHAR *buf, DWORD size)
+{
+    if (es->style & ES_MULTILINE)
+        return FALSE;
+
+    if (!es->cue_banner_text)
+    {
+        if (buf && size)
+            *buf = 0;
+        return FALSE;
+    }
+    else
+    {
+        if (buf)
+            lstrcpynW(buf, es->cue_banner_text, size);
+        return TRUE;
+    }
+}
+
 
 /********************************************************************
  *
@@ -4557,7 +4644,7 @@ static LRESULT EDIT_WM_Create(EDITSTATE *es, const WCHAR *name)
 
     if (name && *name)
     {
-        EDIT_EM_ReplaceSel(es, FALSE, name, strlenW(name), FALSE, FALSE);
+        EDIT_EM_ReplaceSel(es, FALSE, name, lstrlenW(name), FALSE, FALSE);
         /* if we insert text to the editline, the text scrolls out
          * of the window, as the caret is placed after the insert
          * pos normally; thus we reset es->selection... to 0 and
@@ -4614,6 +4701,7 @@ static LRESULT EDIT_WM_NCDestroy(EDITSTATE *es)
 
     SetWindowLongPtrW( es->hwndSelf, 0, 0 );
     heap_free(es->undo_text);
+    heap_free(es->cue_banner_text);
     heap_free(es);
 
     return 0;
@@ -4726,7 +4814,7 @@ static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
     {
         const WCHAR *textW = (const WCHAR *)lParam;
 
-        EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, strlenW(textW), TRUE, TRUE);
+        EDIT_EM_ReplaceSel(es, (BOOL)wParam, textW, lstrlenW(textW), TRUE, TRUE);
         result = 1;
         break;
     }
@@ -4828,6 +4916,14 @@ static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
         result = EDIT_EM_CharFromPos(es, (short)LOWORD(lParam), (short)HIWORD(lParam));
         break;
 
+    case EM_SETCUEBANNER:
+        result = EDIT_EM_SetCueBanner(es, (BOOL)wParam, (const WCHAR *)lParam);
+        break;
+
+    case EM_GETCUEBANNER:
+        result = EDIT_EM_GetCueBanner(es, (WCHAR *)wParam, (DWORD)lParam);
+        break;
+
     /* End of the EM_ messages which were in numerical order; what order
      * are these in?  vaguely alphabetical?
      */
@@ -5036,7 +5132,7 @@ static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
     case WM_MOUSEWHEEL:
     {
         int wheelDelta;
-        UINT pulScrollLines = 3;
+        INT pulScrollLines = 3;
         SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
 
         if (wParam & (MK_SHIFT | MK_CONTROL))
@@ -5056,9 +5152,9 @@ static LRESULT CALLBACK EDIT_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
         if (es->wheelDeltaRemainder && pulScrollLines)
         {
             int cLineScroll;
-            pulScrollLines = (int) min((UINT) es->line_count, pulScrollLines);
-            cLineScroll = pulScrollLines * (float)es->wheelDeltaRemainder / WHEEL_DELTA;
-            es->wheelDeltaRemainder -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
+            pulScrollLines = min(es->line_count, pulScrollLines);
+            cLineScroll = pulScrollLines * es->wheelDeltaRemainder / WHEEL_DELTA;
+            es->wheelDeltaRemainder -= WHEEL_DELTA * cLineScroll / pulScrollLines;
             result = EDIT_EM_LineScroll(es, 0, -cLineScroll);
         }
         break;
index e72324c..522f6dd 100644 (file)
@@ -33,7 +33,6 @@
 
 #include "windef.h"
 #include "winbase.h"
-#include "wine/unicode.h"
 #include "wingdi.h"
 #include "winuser.h"
 #include "winnls.h"
@@ -389,7 +388,7 @@ HEADER_DrawItem (HEADER_INFO *infoPtr, HDC hdc, INT iItem, BOOL bHotTrack, LRESU
         state = (phdi->bDown) ? HIS_PRESSED : (bHotTrack ? HIS_HOT : HIS_NORMAL);
 
     /* Set the colors before sending NM_CUSTOMDRAW so that it can change them */
-    SetTextColor(hdc, (bHotTrack && !theme) ? COLOR_HIGHLIGHT : COLOR_BTNTEXT);
+    SetTextColor(hdc, (bHotTrack && !theme) ? comctl32_color.clrHighlight : comctl32_color.clrBtnText);
     SetBkColor(hdc, comctl32_color.clr3dFace);
 
     if (lCDFlags & CDRF_NOTIFYITEMDRAW && !(phdi->fmt & HDF_OWNERDRAW))
index 7c3bff7..58717a2 100644 (file)
@@ -221,7 +221,7 @@ HOTKEY_SetRules(HOTKEY_INFO *infoPtr, WORD invComb, WORD invMod)
 {
     infoPtr->InvComb = invComb;
     infoPtr->InvMod = invMod;
-    TRACE("(infoPtr=%p) Invalid Modifers: 0x%x, If Invalid: 0x%x\n", infoPtr,
+    TRACE("(infoPtr=%p) Invalid Modifiers: 0x%x, If Invalid: 0x%x\n", infoPtr,
           infoPtr->InvComb, infoPtr->InvMod);
 }
 
diff --git a/dll/win32/comctl32/idb_cmdlink.bmp b/dll/win32/comctl32/idb_cmdlink.bmp
new file mode 100644 (file)
index 0000000..4b3f07b
Binary files /dev/null and b/dll/win32/comctl32/idb_cmdlink.bmp differ
index 78407df..8d74aac 100644 (file)
@@ -1037,7 +1037,7 @@ ImageList_InternalDragDraw (HDC hdc, INT x, INT y)
     imldp.cbSize  = sizeof(imldp);
     imldp.himl    = InternalDrag.himl;
     imldp.i       = 0;
-    imldp.hdcDst  = hdc,
+    imldp.hdcDst  = hdc;
     imldp.x       = x;
     imldp.y       = y;
     imldp.rgbBk   = CLR_DEFAULT;
@@ -1271,7 +1271,7 @@ ImageList_DrawEx (HIMAGELIST himl, INT i, HDC hdc, INT x, INT y,
     imldp.cbSize  = sizeof(imldp);
     imldp.himl    = himl;
     imldp.i       = i;
-    imldp.hdcDst  = hdc,
+    imldp.hdcDst  = hdc;
     imldp.x       = x;
     imldp.y       = y;
     imldp.cx      = dx;
@@ -2483,7 +2483,7 @@ HIMAGELIST WINAPI ImageList_Read(IStream *pstm)
     TRACE("cx %u, cy %u, flags 0x%04x, cCurImage %u, cMaxImage %u\n",
           ilHead.cx, ilHead.cy, ilHead.flags, ilHead.cCurImage, ilHead.cMaxImage);
 
-    himl = ImageList_Create(ilHead.cx, ilHead.cy, ilHead.flags, ilHead.cCurImage, ilHead.cMaxImage);
+    himl = ImageList_Create(ilHead.cx, ilHead.cy, ilHead.flags, ilHead.cMaxImage, ilHead.cGrow);
     if (!himl)
        return NULL;
 
index aa2b6ef..aad2f5d 100644 (file)
@@ -38,7 +38,6 @@
 #include "uxtheme.h"
 #include "vsstyle.h"
 #include "vssym32.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "wine/heap.h"
 
@@ -83,12 +82,12 @@ static void IPADDRESS_UpdateText (const IPADDRESS_INFO *infoPtr)
 
     for (i = 0; i < 4; i++) {
         if (GetWindowTextW (infoPtr->Part[i].EditHwnd, field, 4))
-            strcatW(ip, field);
+            lstrcatW(ip, field);
         else
             /* empty edit treated as zero */
-            strcatW(ip, zero);
+            lstrcatW(ip, zero);
         if (i != 3)
-            strcatW(ip, dot);
+            lstrcatW(ip, dot);
     }
 
     SetWindowTextW(infoPtr->Self, ip);
@@ -245,7 +244,7 @@ static LRESULT IPADDRESS_Create (HWND hwnd, const CREATESTRUCTA *lpCreate)
     hSysFont = GetStockObject(ANSI_VAR_FONT);
     GetObjectW(hSysFont, sizeof(LOGFONTW), &logSysFont);
     SystemParametersInfoW(SPI_GETICONTITLELOGFONT, 0, &logFont, 0);
-    strcpyW(logFont.lfFaceName, logSysFont.lfFaceName);
+    lstrcpyW(logFont.lfFaceName, logSysFont.lfFaceName);
     hFont = CreateFontIndirectW(&logFont);
 
     for (i = 0; i < 4; i++) {
@@ -344,7 +343,7 @@ static int IPADDRESS_GetAddress (const IPADDRESS_INFO *infoPtr, LPDWORD ip_addre
     for (i = 0; i < 4; i++) {
         ip_addr *= 256;
         if (GetWindowTextW (infoPtr->Part[i].EditHwnd, field, 4))
-           ip_addr += atolW(field);
+           ip_addr += wcstol(field, NULL, 10);
        else
            invalid++;
     }
@@ -426,7 +425,7 @@ static BOOL IPADDRESS_ConstrainField (const IPADDRESS_INFO *infoPtr, int current
     part = &infoPtr->Part[currentfield];
     if (!GetWindowTextW (part->EditHwnd, field, 4)) return FALSE;
 
-    curValue = atoiW(field);
+    curValue = wcstol(field, NULL, 10);
     TRACE("  curValue=%d\n", curValue);
 
     newValue = IPADDRESS_IPNotify(infoPtr, currentfield, curValue);
index 897f2dc..804edec 100644 (file)
@@ -18,8 +18,6 @@
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * TODO:
- *    - LBS_NODATA
  */
 
 #include <string.h>
 #include "commctrl.h"
 #include "uxtheme.h"
 #include "vssym32.h"
-#include "wine/unicode.h"
 #include "wine/exception.h"
 #include "wine/debug.h"
+#include "wine/heap.h"
 
 #include "comctl32.h"
 
-WINE_DEFAULT_DEBUG_CHANNEL(listbox2);
+WINE_DEFAULT_DEBUG_CHANNEL(listbox);
 
-/* Items array granularity */
+/* Items array granularity (must be power of 2) */
 #define LB_ARRAY_GRANULARITY 16
 
 /* Scrolling timeout in ms */
@@ -70,8 +68,13 @@ typedef struct
     UINT        style;          /* Window style */
     INT         width;          /* Window width */
     INT         height;         /* Window height */
-    LB_ITEMDATA  *items;        /* Array of items */
+    union
+    {
+        LB_ITEMDATA *items;     /* Array of items */
+        BYTE *nodata_items;     /* For multi-selection LBS_NODATA */
+    } u;
     INT         nb_items;       /* Number of items */
+    UINT        items_size;     /* Total number of allocated items in the array */
     INT         top_item;       /* Top visible item */
     INT         selected_item;  /* Selected item */
     INT         focus_item;     /* Item that has the focus */
@@ -125,6 +128,123 @@ static TIMER_DIRECTION LISTBOX_Timer = LB_TIMER_NONE;
 
 static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect );
 
+/*
+   For listboxes without LBS_NODATA, an array of LB_ITEMDATA is allocated
+   to store the states of each item into descr->u.items.
+
+   For single-selection LBS_NODATA listboxes, no storage is allocated,
+   and thus descr->u.nodata_items will always be NULL.
+
+   For multi-selection LBS_NODATA listboxes, one byte per item is stored
+   for the item's selection state into descr->u.nodata_items.
+*/
+static size_t get_sizeof_item( const LB_DESCR *descr )
+{
+    return (descr->style & LBS_NODATA) ? sizeof(BYTE) : sizeof(LB_ITEMDATA);
+}
+
+static BOOL resize_storage(LB_DESCR *descr, UINT items_size)
+{
+    LB_ITEMDATA *items;
+
+    if (items_size > descr->items_size ||
+        items_size + LB_ARRAY_GRANULARITY * 2 < descr->items_size)
+    {
+        items_size = (items_size + LB_ARRAY_GRANULARITY - 1) & ~(LB_ARRAY_GRANULARITY - 1);
+        if ((descr->style & (LBS_NODATA | LBS_MULTIPLESEL | LBS_EXTENDEDSEL)) != LBS_NODATA)
+        {
+            items = heap_realloc(descr->u.items, items_size * get_sizeof_item(descr));
+            if (!items)
+            {
+                SEND_NOTIFICATION(descr, LBN_ERRSPACE);
+                return FALSE;
+            }
+            descr->u.items = items;
+        }
+        descr->items_size = items_size;
+    }
+
+    if ((descr->style & LBS_NODATA) && descr->u.nodata_items && items_size > descr->nb_items)
+    {
+        memset(descr->u.nodata_items + descr->nb_items, 0,
+               (items_size - descr->nb_items) * get_sizeof_item(descr));
+    }
+    return TRUE;
+}
+
+static ULONG_PTR get_item_data( const LB_DESCR *descr, UINT index )
+{
+    return (descr->style & LBS_NODATA) ? 0 : descr->u.items[index].data;
+}
+
+static void set_item_data( LB_DESCR *descr, UINT index, ULONG_PTR data )
+{
+    if (!(descr->style & LBS_NODATA)) descr->u.items[index].data = data;
+}
+
+static WCHAR *get_item_string( const LB_DESCR *descr, UINT index )
+{
+    return HAS_STRINGS(descr) ? descr->u.items[index].str : NULL;
+}
+
+static void set_item_string( const LB_DESCR *descr, UINT index, WCHAR *string )
+{
+    if (!(descr->style & LBS_NODATA)) descr->u.items[index].str = string;
+}
+
+static UINT get_item_height( const LB_DESCR *descr, UINT index )
+{
+    return (descr->style & LBS_NODATA) ? 0 : descr->u.items[index].height;
+}
+
+static void set_item_height( LB_DESCR *descr, UINT index, UINT height )
+{
+    if (!(descr->style & LBS_NODATA)) descr->u.items[index].height = height;
+}
+
+static BOOL is_item_selected( const LB_DESCR *descr, UINT index )
+{
+    if (!(descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL)))
+        return index == descr->selected_item;
+    if (descr->style & LBS_NODATA)
+        return descr->u.nodata_items[index];
+    else
+        return descr->u.items[index].selected;
+}
+
+static void set_item_selected_state(LB_DESCR *descr, UINT index, BOOL state)
+{
+    if (descr->style & (LBS_MULTIPLESEL | LBS_EXTENDEDSEL))
+    {
+        if (descr->style & LBS_NODATA)
+            descr->u.nodata_items[index] = state;
+        else
+            descr->u.items[index].selected = state;
+    }
+}
+
+static void insert_item_data(LB_DESCR *descr, UINT index)
+{
+    size_t size = get_sizeof_item(descr);
+    BYTE *p = descr->u.nodata_items + index * size;
+
+    if (!descr->u.items) return;
+
+    if (index < descr->nb_items)
+        memmove(p + size, p, (descr->nb_items - index) * size);
+}
+
+static void remove_item_data(LB_DESCR *descr, UINT index)
+{
+    size_t size = get_sizeof_item(descr);
+    BYTE *p = descr->u.nodata_items + index * size;
+
+    if (!descr->u.items) return;
+
+    if (index < descr->nb_items)
+        memmove(p, p + size, (descr->nb_items - index) * size);
+}
+
 /***********************************************************************
  *           LISTBOX_GetCurrentPageSize
  *
@@ -136,7 +256,7 @@ static INT LISTBOX_GetCurrentPageSize( const LB_DESCR *descr )
     if (!(descr->style & LBS_OWNERDRAWVARIABLE)) return descr->page_size;
     for (i = descr->top_item, height = 0; i < descr->nb_items; i++)
     {
-        if ((height += descr->items[i].height) > descr->height) break;
+        if ((height += get_item_height(descr, i)) > descr->height) break;
     }
     if (i == descr->top_item) return 1;
     else return i - descr->top_item;
@@ -156,7 +276,7 @@ static INT LISTBOX_GetMaxTopIndex( const LB_DESCR *descr )
     {
         page = descr->height;
         for (max = descr->nb_items - 1; max >= 0; max--)
-            if ((page -= descr->items[max].height) < 0) break;
+            if ((page -= get_item_height(descr, max)) < 0) break;
         if (max < descr->nb_items - 1) max++;
     }
     else if (descr->style & LBS_MULTICOLUMN)
@@ -275,28 +395,27 @@ static LRESULT LISTBOX_SetTopItem( LB_DESCR *descr, INT index, BOOL scroll )
     if (descr->top_item == index) return LB_OKAY;
     if (scroll)
     {
-        INT diff;
+        INT dx = 0, dy = 0;
         if (descr->style & LBS_MULTICOLUMN)
-            diff = (descr->top_item - index) / descr->page_size * descr->column_width;
+            dx = (descr->top_item - index) / descr->page_size * descr->column_width;
         else if (descr->style & LBS_OWNERDRAWVARIABLE)
         {
             INT i;
-            diff = 0;
             if (index > descr->top_item)
             {
                 for (i = index - 1; i >= descr->top_item; i--)
-                    diff -= descr->items[i].height;
+                    dy -= get_item_height(descr, i);
             }
             else
             {
                 for (i = index; i < descr->top_item; i++)
-                    diff += descr->items[i].height;
+                    dy += get_item_height(descr, i);
             }
         }
         else
-            diff = (descr->top_item - index) * descr->item_height;
+            dy = (descr->top_item - index) * descr->item_height;
 
-        ScrollWindowEx( descr->self, 0, diff, NULL, NULL, 0, NULL,
+        ScrollWindowEx( descr->self, dx, dy, NULL, NULL, 0, NULL,
                         SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
     }
     else
@@ -406,14 +525,14 @@ static LRESULT LISTBOX_GetItemRect( const LB_DESCR *descr, INT index, RECT *rect
             if (index < descr->top_item)
             {
                 for (i = descr->top_item-1; i >= index; i--)
-                    rect->top -= descr->items[i].height;
+                    rect->top -= get_item_height(descr, i);
             }
             else
             {
                 for (i = descr->top_item; i < index; i++)
-                    rect->top += descr->items[i].height;
+                    rect->top += get_item_height(descr, i);
             }
-            rect->bottom = rect->top + descr->items[index].height;
+            rect->bottom = rect->top + get_item_height(descr, index);
 
         }
     }
@@ -448,7 +567,7 @@ static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
         {
             while (index < descr->nb_items)
             {
-                if ((pos += descr->items[index].height) > y) break;
+                if ((pos += get_item_height(descr, index)) > y) break;
                 index++;
             }
         }
@@ -457,7 +576,7 @@ static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
             while (index > 0)
             {
                 index--;
-                if ((pos -= descr->items[index].height) <= y) break;
+                if ((pos -= get_item_height(descr, index)) <= y) break;
             }
         }
     }
@@ -486,8 +605,16 @@ static INT LISTBOX_GetItemFromPoint( const LB_DESCR *descr, INT x, INT y )
 static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
                               INT index, UINT action, BOOL ignoreFocus )
 {
-    LB_ITEMDATA *item = NULL;
-    if (index < descr->nb_items) item = &descr->items[index];
+    BOOL selected = FALSE, focused;
+    WCHAR *item_str = NULL;
+
+    if (index < descr->nb_items)
+    {
+        item_str = get_item_string(descr, index);
+        selected = is_item_selected(descr, index);
+    }
+
+    focused = !ignoreFocus && descr->focus_item == index && descr->caret_on && descr->in_focus;
 
     if (IS_OWNERDRAW(descr))
     {
@@ -495,7 +622,7 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
         RECT r;
         HRGN hrgn;
 
-       if (!item)
+       if (index >= descr->nb_items)
        {
            if (action == ODA_FOCUS)
                DrawFocusRect( hdc, rect );
@@ -518,15 +645,15 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
         dis.hDC          = hdc;
         dis.itemID       = index;
         dis.itemState    = 0;
-        if (item->selected) dis.itemState |= ODS_SELECTED;
-        if (!ignoreFocus && (descr->focus_item == index) &&
-            (descr->caret_on) &&
-            (descr->in_focus)) dis.itemState |= ODS_FOCUS;
+        if (selected)
+            dis.itemState |= ODS_SELECTED;
+        if (focused)
+            dis.itemState |= ODS_FOCUS;
         if (!IsWindowEnabled(descr->self)) dis.itemState |= ODS_DISABLED;
-        dis.itemData     = item->data;
+        dis.itemData     = get_item_data(descr, index);
         dis.rcItem       = *rect;
         TRACE("[%p]: drawitem %d (%s) action=%02x state=%02x rect=%s\n",
-              descr->self, index, debugstr_w(item->str), action,
+              descr->self, index, debugstr_w(item_str), action,
               dis.itemState, wine_dbgstr_rect(rect) );
         SendMessageW(descr->owner, WM_DRAWITEM, dis.CtlID, (LPARAM)&dis);
         SelectClipRgn( hdc, hrgn );
@@ -541,39 +668,38 @@ static void LISTBOX_PaintItem( LB_DESCR *descr, HDC hdc, const RECT *rect,
             DrawFocusRect( hdc, rect );
             return;
         }
-        if (item && item->selected)
+        if (selected)
         {
             oldBk = SetBkColor( hdc, GetSysColor( COLOR_HIGHLIGHT ) );
             oldText = SetTextColor( hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
         }
 
         TRACE("[%p]: painting %d (%s) action=%02x rect=%s\n",
-              descr->self, index, item ? debugstr_w(item->str) : "", action,
+              descr->self, index, debugstr_w(item_str), action,
               wine_dbgstr_rect(rect) );
-        if (!item)
+        if (!item_str)
             ExtTextOutW( hdc, rect->left + 1, rect->top,
                            ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
         else if (!(descr->style & LBS_USETABSTOPS))
             ExtTextOutW( hdc, rect->left + 1, rect->top,
-                         ETO_OPAQUE | ETO_CLIPPED, rect, item->str,
-                         strlenW(item->str), NULL );
+                         ETO_OPAQUE | ETO_CLIPPED, rect, item_str,
+                         lstrlenW(item_str), NULL );
         else
        {
            /* Output empty string to paint background in the full width. */
             ExtTextOutW( hdc, rect->left + 1, rect->top,
                          ETO_OPAQUE | ETO_CLIPPED, rect, NULL, 0, NULL );
             TabbedTextOutW( hdc, rect->left + 1 , rect->top,
-                            item->str, strlenW(item->str),
+                            item_str, lstrlenW(item_str),
                             descr->nb_tabs, descr->tabs, 0);
        }
-        if (item && item->selected)
+        if (selected)
         {
             SetBkColor( hdc, oldBk );
             SetTextColor( hdc, oldText );
         }
-        if (!ignoreFocus && (descr->focus_item == index) &&
-            (descr->caret_on) &&
-            (descr->in_focus)) DrawFocusRect( hdc, rect );
+        if (focused)
+            DrawFocusRect( hdc, rect );
     }
 }
 
@@ -672,27 +798,11 @@ static void LISTBOX_DrawFocusRect( LB_DESCR *descr, BOOL on )
  */
 static LRESULT LISTBOX_InitStorage( LB_DESCR *descr, INT nb_items )
 {
-    LB_ITEMDATA *item;
+    UINT new_size = descr->nb_items + nb_items;
 
-    nb_items += LB_ARRAY_GRANULARITY - 1;
-    nb_items -= (nb_items % LB_ARRAY_GRANULARITY);
-    if (descr->items) {
-        nb_items += HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
-       item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
-                              nb_items * sizeof(LB_ITEMDATA));
-    }
-    else {
-       item = HeapAlloc( GetProcessHeap(), 0,
-                              nb_items * sizeof(LB_ITEMDATA));
-    }
-
-    if (!item)
-    {
-        SEND_NOTIFICATION( descr, LBN_ERRSPACE );
+    if (new_size > descr->items_size && !resize_storage(descr, new_size))
         return LB_ERRSPACE;
-    }
-    descr->items = item;
-    return LB_OKAY;
+    return descr->items_size;
 }
 
 
@@ -743,15 +853,17 @@ static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL
 
     if (HAS_STRINGS(descr))
     {
+        WCHAR *str = get_item_string(descr, index);
+
         if (!buffer)
-            return strlenW(descr->items[index].str);
+            return lstrlenW(str);
 
-        TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
+        TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(str));
 
         __TRY  /* hide a Delphi bug that passes a read-only buffer */
         {
-            strcpyW( buffer, descr->items[index].str );
-            len = strlenW(buffer);
+            lstrcpyW(buffer, str);
+            len = lstrlenW(buffer);
         }
         __EXCEPT_PAGE_FAULT
         {
@@ -763,8 +875,8 @@ static LRESULT LISTBOX_GetText( LB_DESCR *descr, INT index, LPWSTR buffer, BOOL
     } else
     {
         if (buffer)
-            *((DWORD *)buffer) = *(DWORD *)&descr->items[index].data;
-        len = sizeof(DWORD);
+            *((ULONG_PTR *)buffer) = get_item_data(descr, index);
+        len = sizeof(ULONG_PTR);
     }
     return len;
 }
@@ -791,14 +903,15 @@ static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
 {
     INT index, min, max, res;
 
-    if (!(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
+    if (!descr->nb_items || !(descr->style & LBS_SORT)) return -1;  /* Add it at the end */
+
     min = 0;
-    max = descr->nb_items;
-    while (min != max)
+    max = descr->nb_items - 1;
+    while (min <= max)
     {
         index = (min + max) / 2;
         if (HAS_STRINGS(descr))
-            res = LISTBOX_lstrcmpiW( descr->locale, str, descr->items[index].str);
+            res = LISTBOX_lstrcmpiW( descr->locale, get_item_string(descr, index), str );
         else
         {
             COMPAREITEMSTRUCT cis;
@@ -809,18 +922,18 @@ static INT LISTBOX_FindStringPos( LB_DESCR *descr, LPCWSTR str, BOOL exact )
             cis.hwndItem   = descr->self;
             /* note that some application (MetaStock) expects the second item
              * to be in the listbox */
-            cis.itemID1    = -1;
-            cis.itemData1  = (ULONG_PTR)str;
-            cis.itemID2    = index;
-            cis.itemData2  = descr->items[index].data;
+            cis.itemID1    = index;
+            cis.itemData1  = get_item_data(descr, index);
+            cis.itemID2    = -1;
+            cis.itemData2  = (ULONG_PTR)str;
             cis.dwLocaleId = descr->locale;
             res = SendMessageW( descr->owner, WM_COMPAREITEM, id, (LPARAM)&cis );
         }
         if (!res) return index;
-        if (res < 0) max = index;
+        if (res > 0) max = index - 1;
         else min = index + 1;
     }
-    return exact ? -1 : max;
+    return exact ? -1 : min;
 }
 
 
@@ -841,7 +954,7 @@ static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
     while (min != max)
     {
         INT index = (min + max) / 2;
-        LPCWSTR p = descr->items[index].str;
+        LPCWSTR p = get_item_string(descr, index);
         if (*p == '[')  /* drive or directory */
         {
             if (*str != '[') res = -1;
@@ -876,44 +989,42 @@ static INT LISTBOX_FindFileStrPos( LB_DESCR *descr, LPCWSTR str )
  */
 static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
 {
-    INT i;
-    LB_ITEMDATA *item;
+    INT i, index;
+
+    if (descr->style & LBS_NODATA) return LB_ERR;
 
-    if (start >= descr->nb_items) start = -1;
-    item = descr->items + start + 1;
+    start++;
+    if (start >= descr->nb_items) start = 0;
     if (HAS_STRINGS(descr))
     {
         if (!str || ! str[0] ) return LB_ERR;
         if (exact)
         {
-            for (i = start + 1; i < descr->nb_items; i++, item++)
-                if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
-            for (i = 0, item = descr->items; i <= start; i++, item++)
-                if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
+            for (i = 0, index = start; i < descr->nb_items; i++, index++)
+            {
+                if (index == descr->nb_items) index = 0;
+                if (!LISTBOX_lstrcmpiW(descr->locale, str, get_item_string(descr, index)))
+                    return index;
+            }
         }
         else
         {
- /* Special case for drives and directories: ignore prefix */
-#define CHECK_DRIVE(item) \
-    if ((item)->str[0] == '[') \
-    { \
-        if (!strncmpiW( str, (item)->str+1, len )) return i; \
-        if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
-        return i; \
-    }
+            /* Special case for drives and directories: ignore prefix */
+            INT len = lstrlenW(str);
+            WCHAR *item_str;
 
-            INT len = strlenW(str);
-            for (i = start + 1; i < descr->nb_items; i++, item++)
-            {
-               if (!strncmpiW( str, item->str, len )) return i;
-               CHECK_DRIVE(item);
-            }
-            for (i = 0, item = descr->items; i <= start; i++, item++)
+            for (i = 0, index = start; i < descr->nb_items; i++, index++)
             {
-               if (!strncmpiW( str, item->str, len )) return i;
-               CHECK_DRIVE(item);
+                if (index == descr->nb_items) index = 0;
+                item_str = get_item_string(descr, index);
+
+                if (!wcsnicmp(str, item_str, len)) return index;
+                if (item_str[0] == '[')
+                {
+                    if (!wcsnicmp(str, item_str + 1, len)) return index;
+                    if (item_str[1] == '-' && !wcsnicmp(str, item_str + 2, len)) return index;
+                }
             }
-#undef CHECK_DRIVE
         }
     }
     else
@@ -923,10 +1034,11 @@ static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exa
             return LISTBOX_FindStringPos( descr, str, TRUE );
 
         /* Otherwise use a linear search */
-        for (i = start + 1; i < descr->nb_items; i++, item++)
-            if (item->data == (ULONG_PTR)str) return i;
-        for (i = 0, item = descr->items; i <= start; i++, item++)
-            if (item->data == (ULONG_PTR)str) return i;
+        for (i = 0, index = start; i < descr->nb_items; i++, index++)
+        {
+            if (index == descr->nb_items) index = 0;
+            if (get_item_data(descr, index) == (ULONG_PTR)str) return index;
+        }
     }
     return LB_ERR;
 }
@@ -938,13 +1050,12 @@ static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exa
 static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
 {
     INT i, count;
-    const LB_ITEMDATA *item = descr->items;
 
     if (!(descr->style & LBS_MULTIPLESEL) ||
         (descr->style & LBS_NOSEL))
       return LB_ERR;
-    for (i = count = 0; i < descr->nb_items; i++, item++)
-        if (item->selected) count++;
+    for (i = count = 0; i < descr->nb_items; i++)
+        if (is_item_selected(descr, i)) count++;
     return count;
 }
 
@@ -955,11 +1066,10 @@ static LRESULT LISTBOX_GetSelCount( const LB_DESCR *descr )
 static LRESULT LISTBOX_GetSelItems( const LB_DESCR *descr, INT max, LPINT array )
 {
     INT i, count;
-    const LB_ITEMDATA *item = descr->items;
 
     if (!(descr->style & LBS_MULTIPLESEL)) return LB_ERR;
-    for (i = count = 0; (i < descr->nb_items) && (count < max); i++, item++)
-        if (item->selected) array[count++] = i;
+    for (i = count = 0; (i < descr->nb_items) && (count < max); i++)
+        if (is_item_selected(descr, i)) array[count++] = i;
     return count;
 }
 
@@ -1011,7 +1121,7 @@ static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
         if (!(descr->style & LBS_OWNERDRAWVARIABLE))
             rect.bottom = rect.top + descr->item_height;
         else
-            rect.bottom = rect.top + descr->items[i].height;
+            rect.bottom = rect.top + get_item_height(descr, i);
 
         /* keep the focus rect, to paint the focus item after */
         if (i == descr->focus_item)
@@ -1038,6 +1148,7 @@ static LRESULT LISTBOX_Paint( LB_DESCR *descr, HDC hdc )
             rect.right += descr->column_width;
             rect.top = 0;
             col_pos = descr->page_size - 1;
+            if (rect.left >= descr->width) break;
         }
         else
         {
@@ -1087,7 +1198,7 @@ static void LISTBOX_NCPaint( LB_DESCR *descr, HRGN region )
     if (!theme || !(exstyle & WS_EX_CLIENTEDGE))
         return;
 
-    cxEdge = GetSystemMetrics(SM_CXEDGE),
+    cxEdge = GetSystemMetrics(SM_CXEDGE);
     cyEdge = GetSystemMetrics(SM_CYEDGE);
 
     GetWindowRect(descr->self, &r);
@@ -1168,7 +1279,7 @@ static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
             SetLastError(ERROR_INVALID_INDEX);
             return LB_ERR;
         }
-        return descr->items[index].height;
+        return get_item_height(descr, index);
     }
     else return descr->item_height;
 }
@@ -1179,7 +1290,7 @@ static LRESULT LISTBOX_GetItemHeight( const LB_DESCR *descr, INT index )
  */
 static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BOOL repaint )
 {
-    if (height > MAXBYTE)
+    if (height > MAXWORD)
         return -1;
 
     if (!height) height = 1;
@@ -1192,7 +1303,7 @@ static LRESULT LISTBOX_SetItemHeight( LB_DESCR *descr, INT index, INT height, BO
             return LB_ERR;
         }
         TRACE("[%p]: item %d height = %d\n", descr->self, index, height );
-        descr->items[index].height = height;
+        set_item_height(descr, index, height);
         LISTBOX_UpdateScroll( descr );
        if (repaint)
            LISTBOX_InvalidateItems( descr, index );
@@ -1267,12 +1378,19 @@ static LRESULT LISTBOX_SetHorizontalExtent( LB_DESCR *descr, INT extent )
 /***********************************************************************
  *           LISTBOX_SetColumnWidth
  */
-static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT width)
+static LRESULT LISTBOX_SetColumnWidth( LB_DESCR *descr, INT column_width)
 {
-    if (width == descr->column_width) return LB_OKAY;
-    TRACE("[%p]: new column width = %d\n", descr->self, width );
-    descr->column_width = width;
-    LISTBOX_UpdatePage( descr );
+    RECT rect;
+
+    TRACE("[%p]: new column width = %d\n", descr->self, column_width);
+
+    GetClientRect(descr->self, &rect);
+    descr->width = rect.right - rect.left;
+    descr->height = rect.bottom - rect.top;
+    descr->column_width = column_width;
+
+    LISTBOX_UpdatePage(descr);
+    LISTBOX_UpdateScroll(descr);
     return LB_OKAY;
 }
 
@@ -1331,9 +1449,9 @@ static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
     }
     else if (descr->style & LBS_OWNERDRAWVARIABLE)
     {
-        INT height = fully ? descr->items[index].height : 1;
+        INT height = fully ? get_item_height(descr, index) : 1;
         for (top = index; top > descr->top_item; top--)
-            if ((height += descr->items[top-1].height) > descr->height) break;
+            if ((height += get_item_height(descr, top - 1)) > descr->height) break;
     }
     else
     {
@@ -1354,19 +1472,23 @@ static void LISTBOX_MakeItemVisible( LB_DESCR *descr, INT index, BOOL fully )
  */
 static LRESULT LISTBOX_SetCaretIndex( LB_DESCR *descr, INT index, BOOL fully_visible )
 {
-    INT oldfocus = descr->focus_item;
+    BOOL focus_changed = descr->focus_item != index;
 
-    TRACE("old focus %d, index %d\n", oldfocus, index);
+    TRACE("old focus %d, index %d\n", descr->focus_item, index);
 
     if (descr->style & LBS_NOSEL) return LB_ERR;
     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
-    if (index == oldfocus) return LB_OKAY;
 
-    LISTBOX_DrawFocusRect( descr, FALSE );
-    descr->focus_item = index;
+    if (focus_changed)
+    {
+        LISTBOX_DrawFocusRect( descr, FALSE );
+        descr->focus_item = index;
+    }
 
     LISTBOX_MakeItemVisible( descr, index, fully_visible );
-    LISTBOX_DrawFocusRect( descr, TRUE );
+
+    if (focus_changed)
+        LISTBOX_DrawFocusRect( descr, TRUE );
 
     return LB_OKAY;
 }
@@ -1397,8 +1519,8 @@ static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
     {
         for (i = first; i <= last; i++)
         {
-            if (descr->items[i].selected) continue;
-            descr->items[i].selected = TRUE;
+            if (is_item_selected(descr, i)) continue;
+            set_item_selected_state(descr, i, TRUE);
             LISTBOX_InvalidateItemRect(descr, i);
         }
     }
@@ -1406,8 +1528,8 @@ static LRESULT LISTBOX_SelectItemRange( LB_DESCR *descr, INT first,
     {
         for (i = first; i <= last; i++)
         {
-            if (!descr->items[i].selected) continue;
-            descr->items[i].selected = FALSE;
+            if (!is_item_selected(descr, i)) continue;
+            set_item_selected_state(descr, i, FALSE);
             LISTBOX_InvalidateItemRect(descr, i);
         }
     }
@@ -1440,10 +1562,10 @@ static LRESULT LISTBOX_SetSelection( LB_DESCR *descr, INT index,
     {
         INT oldsel = descr->selected_item;
         if (index == oldsel) return LB_OKAY;
-        if (oldsel != -1) descr->items[oldsel].selected = FALSE;
-        if (index != -1) descr->items[index].selected = TRUE;
-        if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
+        if (oldsel != -1) set_item_selected_state(descr, oldsel, FALSE);
+        if (index != -1) set_item_selected_state(descr, index, TRUE);
         descr->selected_item = index;
+        if (oldsel != -1) LISTBOX_RepaintItem( descr, oldsel, ODA_SELECT );
         if (index != -1) LISTBOX_RepaintItem( descr, index, ODA_SELECT );
         if (send_notify && descr->nb_items) SEND_NOTIFICATION( descr,
                                (index != -1) ? LBN_SELCHANGE : LBN_SELCANCEL );
@@ -1510,43 +1632,18 @@ static void LISTBOX_MoveCaret( LB_DESCR *descr, INT index, BOOL fully_visible )
 static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
                                    LPWSTR str, ULONG_PTR data )
 {
-    LB_ITEMDATA *item;
-    INT max_items;
     INT oldfocus = descr->focus_item;
 
     if (index == -1) index = descr->nb_items;
     else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
-    if (!descr->items) max_items = 0;
-    else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
-    if (descr->nb_items == max_items)
-    {
-        /* We need to grow the array */
-        max_items += LB_ARRAY_GRANULARITY;
-       if (descr->items)
-           item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
-                                  max_items * sizeof(LB_ITEMDATA) );
-       else
-           item = HeapAlloc( GetProcessHeap(), 0,
-                                  max_items * sizeof(LB_ITEMDATA) );
-        if (!item)
-        {
-            SEND_NOTIFICATION( descr, LBN_ERRSPACE );
-            return LB_ERRSPACE;
-        }
-        descr->items = item;
-    }
-
-    /* Insert the item structure */
+    if (!resize_storage(descr, descr->nb_items + 1)) return LB_ERR;
 
-    item = &descr->items[index];
-    if (index < descr->nb_items)
-        RtlMoveMemory( item + 1, item,
-                       (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
-    item->str      = str;
-    item->data     = HAS_STRINGS(descr) ? 0 : data;
-    item->height   = 0;
-    item->selected = FALSE;
+    insert_item_data(descr, index);
     descr->nb_items++;
+    set_item_string(descr, index, str);
+    set_item_data(descr, index, HAS_STRINGS(descr) ? 0 : data);
+    set_item_height(descr, index, 0);
+    set_item_selected_state(descr, index, FALSE);
 
     /* Get item height */
 
@@ -1561,9 +1658,9 @@ static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
         mis.itemData   = data;
         mis.itemHeight = descr->item_height;
         SendMessageW( descr->owner, WM_MEASUREITEM, id, (LPARAM)&mis );
-        item->height = mis.itemHeight ? mis.itemHeight : 1;
+        set_item_height(descr, index, mis.itemHeight ? mis.itemHeight : 1);
         TRACE("[%p]: measure item %d (%s) = %d\n",
-              descr->self, index, str ? debugstr_w(str) : "", item->height );
+              descr->self, index, str ? debugstr_w(str) : "", get_item_height(descr, index));
     }
 
     /* Repaint the items */
@@ -1605,12 +1702,12 @@ static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
     {
         static const WCHAR empty_stringW[] = { 0 };
         if (!str) str = empty_stringW;
-        if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (strlenW(str) + 1) * sizeof(WCHAR) )))
+        if (!(new_str = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(str) + 1) * sizeof(WCHAR) )))
         {
             SEND_NOTIFICATION( descr, LBN_ERRSPACE );
             return LB_ERRSPACE;
         }
-        strcpyW(new_str, str);
+        lstrcpyW(new_str, str);
     }
 
     if (index == -1) index = descr->nb_items;
@@ -1633,19 +1730,12 @@ static LRESULT LISTBOX_InsertString( LB_DESCR *descr, INT index, LPCWSTR str )
  */
 static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
 {
-    /* save the item data before it gets freed by LB_RESETCONTENT */
-    ULONG_PTR item_data = descr->items[index].data;
-    LPWSTR item_str = descr->items[index].str;
-
-    if (!descr->nb_items)
-        SendMessageW( descr->self, LB_RESETCONTENT, 0, 0 );
-
     /* Note: Win 3.1 only sends DELETEITEM on owner-draw items,
      *       while Win95 sends it for all items with user data.
      *       It's probably better to send it too often than not
      *       often enough, so this is what we do here.
      */
-    if (IS_OWNERDRAW(descr) || item_data)
+    if (IS_OWNERDRAW(descr) || get_item_data(descr, index))
     {
         DELETEITEMSTRUCT dis;
         UINT id = (UINT)GetWindowLongPtrW( descr->self, GWLP_ID );
@@ -1654,11 +1744,10 @@ static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
         dis.CtlID    = id;
         dis.itemID   = index;
         dis.hwndItem = descr->self;
-        dis.itemData = item_data;
+        dis.itemData = get_item_data(descr, index);
         SendMessageW( descr->owner, WM_DELETEITEM, id, (LPARAM)&dis );
     }
-    if (HAS_STRINGS(descr))
-        HeapFree( GetProcessHeap(), 0, item_str );
+    HeapFree( GetProcessHeap(), 0, get_item_string(descr, index) );
 }
 
 
@@ -1669,37 +1758,23 @@ static void LISTBOX_DeleteItem( LB_DESCR *descr, INT index )
  */
 static LRESULT LISTBOX_RemoveItem( LB_DESCR *descr, INT index )
 {
-    LB_ITEMDATA *item;
-    INT max_items;
-
     if ((index < 0) || (index >= descr->nb_items)) return LB_ERR;
 
     /* We need to invalidate the original rect instead of the updated one. */
     LISTBOX_InvalidateItems( descr, index );
 
+    if (descr->nb_items == 1)
+    {
+        SendMessageW(descr->self, LB_RESETCONTENT, 0, 0);
+        return LB_OKAY;
+    }
     descr->nb_items--;
     LISTBOX_DeleteItem( descr, index );
+    remove_item_data(descr, index);
 
-    if (!descr->nb_items) return LB_OKAY;
-
-    /* Remove the item */
-
-    item = &descr->items[index];
-    if (index < descr->nb_items)
-        RtlMoveMemory( item, item + 1,
-                       (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
     if (descr->anchor_item == descr->nb_items) descr->anchor_item--;
+    resize_storage(descr, descr->nb_items);
 
-    /* Shrink the item array if possible */
-
-    max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(LB_ITEMDATA);
-    if (descr->nb_items < max_items - 2*LB_ARRAY_GRANULARITY)
-    {
-        max_items -= LB_ARRAY_GRANULARITY;
-        item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
-                            max_items * sizeof(LB_ITEMDATA) );
-        if (item) descr->items = item;
-    }
     /* Repaint the items */
 
     LISTBOX_UpdateScroll( descr );
@@ -1737,43 +1812,52 @@ static void LISTBOX_ResetContent( LB_DESCR *descr )
 {
     INT i;
 
-    for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
-    HeapFree( GetProcessHeap(), 0, descr->items );
+    if (!(descr->style & LBS_NODATA))
+        for (i = descr->nb_items - 1; i >= 0; i--) LISTBOX_DeleteItem(descr, i);
+    HeapFree( GetProcessHeap(), 0, descr->u.items );
     descr->nb_items      = 0;
     descr->top_item      = 0;
     descr->selected_item = -1;
     descr->focus_item    = 0;
     descr->anchor_item   = -1;
-    descr->items         = NULL;
+    descr->items_size    = 0;
+    descr->u.items       = NULL;
 }
 
 
 /***********************************************************************
  *           LISTBOX_SetCount
  */
-static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
+static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count )
 {
-    LRESULT ret;
+    UINT orig_num = descr->nb_items;
 
-    if (HAS_STRINGS(descr))
-    {
-        SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
-        return LB_ERR;
-    }
+    if (!(descr->style & LBS_NODATA)) return LB_ERR;
 
-    /* FIXME: this is far from optimal... */
-    if (count > descr->nb_items)
-    {
-        while (count > descr->nb_items)
-            if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
-                return ret;
-    }
-    else if (count < descr->nb_items)
+    if (!resize_storage(descr, count))
+        return LB_ERRSPACE;
+    descr->nb_items = count;
+
+    if (count)
     {
-        while (count < descr->nb_items)
-            if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
-                return ret;
+        LISTBOX_UpdateScroll(descr);
+        if (count < orig_num)
+        {
+            descr->anchor_item = min(descr->anchor_item, count - 1);
+            if (descr->selected_item >= count)
+                descr->selected_item = -1;
+
+            /* If we removed the scrollbar, reset the top of the list */
+            if (count <= descr->page_size && orig_num > descr->page_size)
+                LISTBOX_SetTopItem(descr, 0, TRUE);
+
+            descr->focus_item = min(descr->focus_item, count - 1);
+        }
+
+        /* If it was empty before growing, set focus to the first item */
+        else if (orig_num == 0) LISTBOX_SetCaretIndex(descr, 0, FALSE);
     }
+    else SendMessageW(descr->self, LB_RESETCONTENT, 0, 0);
 
     InvalidateRect( descr->self, NULL, TRUE );
     return LB_OKAY;
@@ -1810,13 +1894,13 @@ static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
                     static const WCHAR bracketW[]  = { ']',0 };
                     static const WCHAR dotW[] = { '.',0 };
                     if (!(attrib & DDL_DIRECTORY) ||
-                        !strcmpW( entry.cFileName, dotW )) continue;
+                        !lstrcmpW( entry.cFileName, dotW )) continue;
                     buffer[0] = '[';
                     if (!long_names && entry.cAlternateFileName[0])
-                        strcpyW( buffer + 1, entry.cAlternateFileName );
+                        lstrcpyW( buffer + 1, entry.cAlternateFileName );
                     else
-                        strcpyW( buffer + 1, entry.cFileName );
-                    strcatW(buffer, bracketW);
+                        lstrcpyW( buffer + 1, entry.cFileName );
+                    lstrcatW(buffer, bracketW);
                 }
                 else  /* not a directory */
                 {
@@ -1828,9 +1912,9 @@ static LRESULT LISTBOX_Directory( LB_DESCR *descr, UINT attrib,
                         continue;
 #undef ATTRIBS
                     if (!long_names && entry.cAlternateFileName[0])
-                        strcpyW( buffer, entry.cAlternateFileName );
+                        lstrcpyW( buffer, entry.cAlternateFileName );
                     else
-                        strcpyW( buffer, entry.cFileName );
+                        lstrcpyW( buffer, entry.cFileName );
                 }
                 if (!long_names) CharLowerW( buffer );
                 pos = LISTBOX_FindFileStrPos( descr, buffer );
@@ -1998,7 +2082,7 @@ static LRESULT LISTBOX_HandleHScroll( LB_DESCR *descr, WORD scrollReq, WORD pos
 
 static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
 {
-    UINT pulScrollLines = 3;
+    INT pulScrollLines = 3;
 
     SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
 
@@ -2012,9 +2096,20 @@ static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
     if (descr->wheel_remain && pulScrollLines)
     {
         int cLineScroll;
-        pulScrollLines = min((UINT) descr->page_size, pulScrollLines);
-        cLineScroll = pulScrollLines * (float)descr->wheel_remain / WHEEL_DELTA;
-        descr->wheel_remain -= WHEEL_DELTA * cLineScroll / (int)pulScrollLines;
+        if (descr->style & LBS_MULTICOLUMN)
+        {
+            pulScrollLines = min(descr->width / descr->column_width, pulScrollLines);
+            pulScrollLines = max(1, pulScrollLines);
+            cLineScroll = pulScrollLines * descr->wheel_remain / WHEEL_DELTA;
+            descr->wheel_remain -= WHEEL_DELTA * cLineScroll / pulScrollLines;
+            cLineScroll *= descr->page_size;
+        }
+        else
+        {
+            pulScrollLines = min(descr->page_size, pulScrollLines);
+            cLineScroll = pulScrollLines * descr->wheel_remain / WHEEL_DELTA;
+            descr->wheel_remain -= WHEEL_DELTA * cLineScroll / pulScrollLines;
+        }
         LISTBOX_SetTopItem( descr, descr->top_item - cLineScroll, TRUE );
     }
     return 0;
@@ -2063,7 +2158,7 @@ static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, IN
         {
             LISTBOX_SetCaretIndex( descr, index, FALSE );
             LISTBOX_SetSelection( descr, index,
-                                  !descr->items[index].selected,
+                                  !is_item_selected(descr, index),
                                   (descr->style & LBS_NOTIFY) != 0);
         }
         else
@@ -2073,13 +2168,13 @@ static LRESULT LISTBOX_HandleLButtonDown( LB_DESCR *descr, DWORD keys, INT x, IN
             if (descr->style & LBS_EXTENDEDSEL)
             {
                 LISTBOX_SetSelection( descr, index,
-                               descr->items[index].selected,
+                               is_item_selected(descr, index),
                               (descr->style & LBS_NOTIFY) != 0 );
             }
             else
             {
                 LISTBOX_SetSelection( descr, index,
-                               !descr->items[index].selected,
+                               !is_item_selected(descr, index),
                               (descr->style & LBS_NOTIFY) != 0 );
             }
         }
@@ -2359,8 +2454,7 @@ static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
         if (descr->style & LBS_MULTICOLUMN)
         {
             bForceSelection = FALSE;
-            if (descr->focus_item + descr->page_size < descr->nb_items)
-                caret = descr->focus_item + descr->page_size;
+            caret = min(descr->focus_item + descr->page_size, descr->nb_items - 1);
             break;
         }
         /* fall through */
@@ -2400,7 +2494,7 @@ static LRESULT LISTBOX_HandleKeyDown( LB_DESCR *descr, DWORD key )
         else if (descr->style & LBS_MULTIPLESEL)
         {
             LISTBOX_SetSelection( descr, descr->focus_item,
-                                  !descr->items[descr->focus_item].selected,
+                                  !is_item_selected(descr, descr->focus_item),
                                   (descr->style & LBS_NOTIFY) != 0 );
         }
         break;
@@ -2485,7 +2579,8 @@ static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
     descr->style         = GetWindowLongW( descr->self, GWL_STYLE );
     descr->width         = rect.right - rect.left;
     descr->height        = rect.bottom - rect.top;
-    descr->items         = NULL;
+    descr->u.items       = NULL;
+    descr->items_size    = 0;
     descr->nb_items      = 0;
     descr->top_item      = 0;
     descr->selected_item = -1;
@@ -2520,10 +2615,14 @@ static BOOL LISTBOX_Create( HWND hwnd, LPHEADCOMBO lphc )
     if (descr->style & LBS_EXTENDEDSEL) descr->style |= LBS_MULTIPLESEL;
     if (descr->style & LBS_MULTICOLUMN) descr->style &= ~LBS_OWNERDRAWVARIABLE;
     if (descr->style & LBS_OWNERDRAWVARIABLE) descr->style |= LBS_NOINTEGRALHEIGHT;
+    if ((descr->style & (LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | LBS_SORT)) != LBS_OWNERDRAWFIXED)
+        descr->style &= ~LBS_NODATA;
     descr->item_height = LISTBOX_SetFont( descr, 0 );
 
     if (descr->style & LBS_OWNERDRAWFIXED)
     {
+        descr->style &= ~LBS_OWNERDRAWVARIABLE;
+
        if( descr->lphc && (descr->lphc->dwStyle & CBS_DROPDOWN))
        {
            /* WinWord gets VERY unhappy if we send WM_MEASUREITEM from here */
@@ -2633,7 +2732,7 @@ static LRESULT CALLBACK LISTBOX_WindowProc( HWND hwnd, UINT msg, WPARAM wParam,
             SetLastError(ERROR_INVALID_INDEX);
             return LB_ERR;
         }
-        return descr->items[wParam].data;
+        return get_item_data(descr, wParam);
 
     case LB_SETITEMDATA:
         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
@@ -2641,7 +2740,7 @@ static LRESULT CALLBACK LISTBOX_WindowProc( HWND hwnd, UINT msg, WPARAM wParam,
             SetLastError(ERROR_INVALID_INDEX);
             return LB_ERR;
         }
-        descr->items[wParam].data = lParam;
+        set_item_data(descr, wParam, lParam);
         /* undocumented: returns TRUE, not LB_OKAY (0) */
         return TRUE;
 
@@ -2657,8 +2756,8 @@ static LRESULT CALLBACK LISTBOX_WindowProc( HWND hwnd, UINT msg, WPARAM wParam,
             SetLastError(ERROR_INVALID_INDEX);
             return LB_ERR;
         }
-        if (!HAS_STRINGS(descr)) return sizeof(DWORD);
-        return strlenW( descr->items[wParam].str );
+        if (!HAS_STRINGS(descr)) return sizeof(ULONG_PTR);
+        return lstrlenW(get_item_string(descr, wParam));
 
     case LB_GETCURSEL:
         if (descr->nb_items == 0)
@@ -2764,10 +2863,17 @@ static LRESULT CALLBACK LISTBOX_WindowProc( HWND hwnd, UINT msg, WPARAM wParam,
     case LB_GETSEL:
         if (((INT)wParam < 0) || ((INT)wParam >= descr->nb_items))
             return LB_ERR;
-        return descr->items[wParam].selected;
+        return is_item_selected(descr, wParam);
 
     case LB_SETSEL:
-        return LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
+        ret = LISTBOX_SetSelection( descr, lParam, wParam, FALSE );
+        if (ret != LB_ERR && wParam)
+        {
+            descr->anchor_item = lParam;
+            if (lParam != -1)
+                LISTBOX_SetCaretIndex( descr, lParam, TRUE );
+        }
+        return ret;
 
     case LB_SETCURSEL:
         if (IS_MULTISELECT(descr)) return LB_ERR;
index 53e8b54..5279c7b 100644 (file)
@@ -542,12 +542,6 @@ static inline int textcmpWT(LPCWSTR aw, LPCWSTR bt, BOOL isW)
            
     return 1;
 }
-    
-static inline int lstrncmpiW(LPCWSTR s1, LPCWSTR s2, int n)
-{
-    n = min(min(n, lstrlenW(s1)), lstrlenW(s2));
-    return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, s1, n, s2, n) - CSTR_EQUAL;
-}
 
 /******** Debugging functions *****************************************/
 
@@ -1972,7 +1966,7 @@ static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, L
                 item.cchTextMax = MAX_PATH;
                 if (!LISTVIEW_GetItemW(infoPtr, &item)) return 0;
 
-                if (!lstrncmpiW(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
+                if (!wcsnicmp(item.pszText, infoPtr->szSearchParam, infoPtr->nSearchParamLength))
                 {
                     nItem = i;
                     break;
@@ -1980,7 +1974,7 @@ static INT LISTVIEW_ProcessLetterKeys(LISTVIEW_INFO *infoPtr, WPARAM charCode, L
                 /* this is used to find first char match when search string is not available yet,
                    otherwise every WM_CHAR will search to next item by first char, ignoring that we're
                    already waiting for user to complete a string */
-                else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !lstrncmpiW(item.pszText, infoPtr->szSearchParam, 1))
+                else if (nItem == -1 && infoPtr->nSearchParamLength == 1 && !wcsnicmp(item.pszText, infoPtr->szSearchParam, 1))
                 {
                     /* this would work but we must keep looking for a longer match */
                     nItem = i;
@@ -6797,11 +6791,11 @@ static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImage
 static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
 {
     ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
+    BOOL is_subitem_invalid = FALSE;
     NMLVDISPINFOW dispInfo;
     ITEM_INFO *lpItem;
     ITEMHDR* pItemHdr;
     HDPA hdpaSubItems;
-    INT isubitem;
 
     TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
 
@@ -6811,10 +6805,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     if (lpLVItem->mask == 0) return TRUE;
     TRACE("mask=%x\n", lpLVItem->mask);
 
-    /* make a local copy */
-    isubitem = lpLVItem->iSubItem;
-
-    if (isubitem && (lpLVItem->mask & LVIF_STATE))
+    if (lpLVItem->iSubItem && (lpLVItem->mask & LVIF_STATE))
         lpLVItem->state = 0;
 
     /* a quick optimization if all we're asked is the focus state
@@ -6824,7 +6815,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
         !(infoPtr->uCallbackMask & LVIS_FOCUSED) )
     {
         lpLVItem->state = 0;
-        if (infoPtr->nFocusedItem == lpLVItem->iItem && isubitem == 0)
+        if (infoPtr->nFocusedItem == lpLVItem->iItem && !lpLVItem->iSubItem)
             lpLVItem->state |= LVIS_FOCUSED;
         return TRUE;
     }
@@ -6846,7 +6837,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
             *       depend on the uninitialized fields being 0 */
            dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
            dispInfo.item.iItem = lpLVItem->iItem;
-           dispInfo.item.iSubItem = isubitem;
+           dispInfo.item.iSubItem = lpLVItem->iSubItem;
            if (lpLVItem->mask & LVIF_TEXT)
            {
                if (lpLVItem->mask & LVIF_NORECOMPUTE)
@@ -6893,7 +6884,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
            lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
 
        /* we store only a little state, so if we're not asked, we're done */
-       if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
+       if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
 
        /* if focus is handled by us, report it */
        if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED ) 
@@ -6919,21 +6910,22 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     lpItem = DPA_GetPtr(hdpaSubItems, 0);
     assert (lpItem);
 
-    if (isubitem)
+    if (lpLVItem->iSubItem)
     {
-        SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
-        pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
-        if (!lpSubItem)
+        SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
+        if (lpSubItem)
+            pItemHdr = &lpSubItem->hdr;
+        else
         {
-            WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
-            isubitem = 0;
+            pItemHdr = &callbackHdr;
+            is_subitem_invalid = TRUE;
         }
     }
     else
        pItemHdr = &lpItem->hdr;
 
     /* Do we need to query the state from the app? */
-    if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
+    if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && (!lpLVItem->iSubItem || is_subitem_invalid))
     {
        dispInfo.item.mask |= LVIF_STATE;
        dispInfo.item.stateMask = infoPtr->uCallbackMask;
@@ -6941,15 +6933,14 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
   
     /* Do we need to enquire about the image? */
     if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
-        (isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
+        (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
     {
        dispInfo.item.mask |= LVIF_IMAGE;
         dispInfo.item.iImage = I_IMAGECALLBACK;
     }
 
     /* Only items support indentation */
-    if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
-        (isubitem == 0))
+    if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && !lpLVItem->iSubItem)
     {
         dispInfo.item.mask |= LVIF_INDENT;
         dispInfo.item.iIndent = I_INDENTCALLBACK;
@@ -6970,14 +6961,14 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     if (dispInfo.item.mask)
     {
        dispInfo.item.iItem = lpLVItem->iItem;
-       dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
+       dispInfo.item.iSubItem = lpLVItem->iSubItem;
        dispInfo.item.lParam = lpItem->lParam;
        notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
        TRACE("   getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
     }
 
     /* we should not store values for subitems */
-    if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
+    if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
 
     /* Now, handle the iImage field */
     if (dispInfo.item.mask & LVIF_IMAGE)
@@ -6988,7 +6979,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
     }
     else if (lpLVItem->mask & LVIF_IMAGE)
     {
-        if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
+        if (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
             lpLVItem->iImage = pItemHdr->iImage;
         else
             lpLVItem->iImage = 0;
@@ -7020,7 +7011,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
        lpLVItem->lParam = lpItem->lParam;
 
     /* if this is a subitem, we're done */
-    if (isubitem) return TRUE;
+    if (lpLVItem->iSubItem) return TRUE;
 
     /* ... the state field (this one is different due to uCallbackmask) */
     if (lpLVItem->mask & LVIF_STATE)
index 744b9d3..00e0f79 100644 (file)
@@ -45,7 +45,6 @@
 #include "comctl32.h"
 #include "uxtheme.h"
 #include "vssym32.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "wine/heap.h"
 
@@ -216,7 +215,7 @@ static inline int MONTHCAL_MonthDiff(const SYSTEMTIME *left, const SYSTEMTIME *r
 /* January is 1, December is 12 */
 int MONTHCAL_MonthLength(int month, int year)
 {
-  const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+  static const int mdays[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
   /* Wrap around, this eases handling. Getting length only we shouldn't care
      about year change here cause January and December have
      the same day quantity */
@@ -888,18 +887,18 @@ static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRU
 
   /* draw formatted date string */
   GetDateFormatW(LOCALE_USER_DEFAULT, DATE_YEARMONTH, st, NULL, strW, ARRAY_SIZE(strW));
-  DrawTextW(hdc, strW, strlenW(strW), title, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+  DrawTextW(hdc, strW, lstrlenW(strW), title, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
 
   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SYEARMONTH, fmtW, ARRAY_SIZE(fmtW));
   wsprintfW(yearW, fmtyearW, st->wYear);
 
   /* month is trickier as it's possible to have different format pictures, we'll
      test for M, MM, MMM, and MMMM */
-  if (strstrW(fmtW, mmmmW))
+  if (wcsstr(fmtW, mmmmW))
     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SMONTHNAME1+st->wMonth-1, monthW, ARRAY_SIZE(monthW));
-  else if (strstrW(fmtW, mmmW))
+  else if (wcsstr(fmtW, mmmW))
     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1+st->wMonth-1, monthW, ARRAY_SIZE(monthW));
-  else if (strstrW(fmtW, mmW))
+  else if (wcsstr(fmtW, mmW))
     wsprintfW(monthW, fmtmmW, st->wMonth);
   else
     wsprintfW(monthW, fmtmW, st->wMonth);
@@ -908,7 +907,7 @@ static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRU
   yearoffset = 0;
   while (strW[yearoffset])
   {
-    if (!strncmpW(&strW[yearoffset], yearW, strlenW(yearW)))
+    if (!wcsncmp(&strW[yearoffset], yearW, lstrlenW(yearW)))
         break;
     yearoffset++;
   }
@@ -916,7 +915,7 @@ static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRU
   monthoffset = 0;
   while (strW[monthoffset])
   {
-    if (!strncmpW(&strW[monthoffset], monthW, strlenW(monthW)))
+    if (!wcsncmp(&strW[monthoffset], monthW, lstrlenW(monthW)))
         break;
     monthoffset++;
   }
@@ -933,15 +932,15 @@ static void MONTHCAL_PaintTitle(MONTHCAL_INFO *infoPtr, HDC hdc, const PAINTSTRU
   infoPtr->calendars[calIdx].titlemonth.left = sz.cx;
 
   /* for right limits use actual string parts lengths */
-  GetTextExtentPoint32W(hdc, &strW[yearoffset], strlenW(yearW), &sz);
+  GetTextExtentPoint32W(hdc, &strW[yearoffset], lstrlenW(yearW), &sz);
   infoPtr->calendars[calIdx].titleyear.right = infoPtr->calendars[calIdx].titleyear.left + sz.cx;
 
-  GetTextExtentPoint32W(hdc, monthW, strlenW(monthW), &sz);
+  GetTextExtentPoint32W(hdc, monthW, lstrlenW(monthW), &sz);
   infoPtr->calendars[calIdx].titlemonth.right = infoPtr->calendars[calIdx].titlemonth.left + sz.cx;
 
   /* Finally translate rectangles to match center aligned string,
      hit rectangles are relative to title rectangle before translation. */
-  GetTextExtentPoint32W(hdc, strW, strlenW(strW), &sz);
+  GetTextExtentPoint32W(hdc, strW, lstrlenW(strW), &sz);
   shiftX = (title->right - title->left - sz.cx) / 2 + title->left;
   OffsetRect(&infoPtr->calendars[calIdx].titleyear, shiftX, 0);
   OffsetRect(&infoPtr->calendars[calIdx].titlemonth, shiftX, 0);
@@ -977,7 +976,7 @@ static void MONTHCAL_PaintWeeknumbers(const MONTHCAL_INFO *infoPtr, HDC hdc, con
      The first week of the year must contain only days of the new year
   */
   GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTWEEKOFYEAR, buf, ARRAY_SIZE(buf));
-  weeknum = atoiW(buf);
+  weeknum = wcstol(buf, NULL, 10);
   switch (weeknum)
   {
     case 1: mindays = 6;
@@ -1208,7 +1207,7 @@ static void MONTHCAL_PaintCalendar(const MONTHCAL_INFO *infoPtr, HDC hdc, const
   i = infoPtr->firstDay;
   for(j = 0; j < 7; j++) {
     get_localized_dayname(infoPtr, (i + j + 6) % 7, buf, ARRAY_SIZE(buf));
-    DrawTextW(hdc, buf, strlenW(buf), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+    DrawTextW(hdc, buf, lstrlenW(buf), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
     OffsetRect(&r, infoPtr->width_increment, 0);
   }
 
@@ -1414,9 +1413,9 @@ MONTHCAL_SetFirstDayOfWeek(MONTHCAL_INFO *infoPtr, INT day)
     WCHAR buf[80];
 
     GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK, buf, ARRAY_SIZE(buf));
-    TRACE("%s %d\n", debugstr_w(buf), strlenW(buf));
+    TRACE("%s %d\n", debugstr_w(buf), lstrlenW(buf));
 
-    new_day = atoiW(buf);
+    new_day = wcstol(buf, NULL, 10);
 
     infoPtr->firstDaySet = FALSE;
   }
index fe68515..4763ed5 100644 (file)
@@ -71,6 +71,7 @@ typedef struct
     HWND   hwndSelf;   /* handle of the control wnd */
     HWND   hwndChild;  /* handle of the contained wnd */
     HWND   hwndNotify; /* handle of the parent wnd */
+    BOOL   bUnicode;   /* send notifications in Unicode */
     DWORD  dwStyle;    /* styles for this control */
     COLORREF clrBk;    /* background color */
     INT    nBorder;    /* border size for the control */
@@ -83,6 +84,8 @@ typedef struct
     INT    TLbtnState; /* state of top or left btn */
     INT    BRbtnState; /* state of bottom or right btn */
     INT    direction;  /* direction of the scroll, (e.g. PGF_SCROLLUP) */
+    WCHAR  *pwszBuffer;/* text buffer for converted notifications */
+    INT    nBufferSize;/* size of the above buffer */
 } PAGER_INFO;
 
 #define TIMERID1         1
@@ -90,6 +93,21 @@ typedef struct
 #define INITIAL_DELAY    500
 #define REPEAT_DELAY     50
 
+/* Text field conversion behavior flags for PAGER_SendConvertedNotify() */
+enum conversion_flags
+{
+    /* Convert Unicode text to ANSI for parent before sending. If not set, do nothing */
+    CONVERT_SEND = 0x01,
+    /* Convert ANSI text from parent back to Unicode for children */
+    CONVERT_RECEIVE = 0x02,
+    /* Send empty text to parent if text is NULL. Original text pointer still remains NULL */
+    SEND_EMPTY_IF_NULL = 0x04,
+    /* Set text to null after parent received the notification if the required mask is not set before sending notification */
+    SET_NULL_IF_NO_MASK = 0x08,
+    /* Zero out the text buffer before sending it to parent */
+    ZERO_SEND = 0x10
+};
+
 static void
 PAGER_GetButtonRects(const PAGER_INFO* infoPtr, RECT* prcTopLeft, RECT* prcBottomRight, BOOL bClientCoords)
 {
@@ -555,6 +573,7 @@ static LRESULT
 PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
 {
     PAGER_INFO *infoPtr;
+    INT ret;
 
     /* allocate memory for info structure */
     infoPtr = heap_alloc_zero (sizeof(*infoPtr));
@@ -581,6 +600,9 @@ PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
     if (infoPtr->dwStyle & PGS_DRAGNDROP)
         FIXME("[%p] Drag and Drop style is not implemented yet.\n", infoPtr->hwndSelf);
 
+    ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
+    infoPtr->bUnicode = (ret == NFR_UNICODE);
+
     return 0;
 }
 
@@ -589,6 +611,7 @@ static LRESULT
 PAGER_Destroy (PAGER_INFO *infoPtr)
 {
     SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
+    heap_free (infoPtr->pwszBuffer);
     heap_free (infoPtr);
     return 0;
 }
@@ -998,6 +1021,438 @@ PAGER_StyleChanged(PAGER_INFO *infoPtr, WPARAM wStyleType, const STYLESTRUCT *lp
     return 0;
 }
 
+static LRESULT PAGER_NotifyFormat(PAGER_INFO *infoPtr, INT command)
+{
+    INT ret;
+    switch (command)
+    {
+    case NF_REQUERY:
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFYFORMAT, (WPARAM)infoPtr->hwndSelf, NF_QUERY);
+        infoPtr->bUnicode = (ret == NFR_UNICODE);
+        return ret;
+    case NF_QUERY:
+        /* Pager always wants Unicode notifications from children */
+        return NFR_UNICODE;
+    default:
+        return 0;
+    }
+}
+
+static UINT PAGER_GetAnsiNtfCode(UINT code)
+{
+    switch (code)
+    {
+    /* ComboxBoxEx */
+    case CBEN_DRAGBEGINW: return CBEN_DRAGBEGINA;
+    case CBEN_ENDEDITW: return CBEN_ENDEDITA;
+    case CBEN_GETDISPINFOW: return CBEN_GETDISPINFOA;
+    /* Date and Time Picker */
+    case DTN_FORMATW: return DTN_FORMATA;
+    case DTN_FORMATQUERYW: return DTN_FORMATQUERYA;
+    case DTN_USERSTRINGW: return DTN_USERSTRINGA;
+    case DTN_WMKEYDOWNW: return DTN_WMKEYDOWNA;
+    /* Header */
+    case HDN_BEGINTRACKW: return HDN_BEGINTRACKA;
+    case HDN_DIVIDERDBLCLICKW: return HDN_DIVIDERDBLCLICKA;
+    case HDN_ENDTRACKW: return HDN_ENDTRACKA;
+    case HDN_GETDISPINFOW: return HDN_GETDISPINFOA;
+    case HDN_ITEMCHANGEDW: return HDN_ITEMCHANGEDA;
+    case HDN_ITEMCHANGINGW: return HDN_ITEMCHANGINGA;
+    case HDN_ITEMCLICKW: return HDN_ITEMCLICKA;
+    case HDN_ITEMDBLCLICKW: return HDN_ITEMDBLCLICKA;
+    case HDN_TRACKW: return HDN_TRACKA;
+    /* List View */
+    case LVN_BEGINLABELEDITW: return LVN_BEGINLABELEDITA;
+    case LVN_ENDLABELEDITW: return LVN_ENDLABELEDITA;
+    case LVN_GETDISPINFOW: return LVN_GETDISPINFOA;
+    case LVN_GETINFOTIPW: return LVN_GETINFOTIPA;
+    case LVN_INCREMENTALSEARCHW: return LVN_INCREMENTALSEARCHA;
+    case LVN_ODFINDITEMW: return LVN_ODFINDITEMA;
+    case LVN_SETDISPINFOW: return LVN_SETDISPINFOA;
+    /* Toolbar */
+    case TBN_GETBUTTONINFOW: return TBN_GETBUTTONINFOA;
+    case TBN_GETINFOTIPW: return TBN_GETINFOTIPA;
+    /* Tooltip */
+    case TTN_GETDISPINFOW: return TTN_GETDISPINFOA;
+    /* Tree View */
+    case TVN_BEGINDRAGW: return TVN_BEGINDRAGA;
+    case TVN_BEGINLABELEDITW: return TVN_BEGINLABELEDITA;
+    case TVN_BEGINRDRAGW: return TVN_BEGINRDRAGA;
+    case TVN_DELETEITEMW: return TVN_DELETEITEMA;
+    case TVN_ENDLABELEDITW: return TVN_ENDLABELEDITA;
+    case TVN_GETDISPINFOW: return TVN_GETDISPINFOA;
+    case TVN_GETINFOTIPW: return TVN_GETINFOTIPA;
+    case TVN_ITEMEXPANDEDW: return TVN_ITEMEXPANDEDA;
+    case TVN_ITEMEXPANDINGW: return TVN_ITEMEXPANDINGA;
+    case TVN_SELCHANGEDW: return TVN_SELCHANGEDA;
+    case TVN_SELCHANGINGW: return TVN_SELCHANGINGA;
+    case TVN_SETDISPINFOW: return TVN_SETDISPINFOA;
+    }
+    return code;
+}
+
+static BOOL PAGER_AdjustBuffer(PAGER_INFO *infoPtr, INT size)
+{
+    if (!infoPtr->pwszBuffer)
+        infoPtr->pwszBuffer = heap_alloc(size);
+    else if (infoPtr->nBufferSize < size)
+        infoPtr->pwszBuffer = heap_realloc(infoPtr->pwszBuffer, size);
+
+    if (!infoPtr->pwszBuffer) return FALSE;
+    if (infoPtr->nBufferSize < size) infoPtr->nBufferSize = size;
+
+    return TRUE;
+}
+
+/* Convert text to Unicode and return the original text address */
+static WCHAR *PAGER_ConvertText(WCHAR **text)
+{
+    WCHAR *oldText = *text;
+    *text = NULL;
+    Str_SetPtrWtoA((CHAR **)text, oldText);
+    return oldText;
+}
+
+static void PAGER_RestoreText(WCHAR **text, WCHAR *oldText)
+{
+    if (!oldText) return;
+
+    Free(*text);
+    *text = oldText;
+}
+
+static LRESULT PAGER_SendConvertedNotify(PAGER_INFO *infoPtr, NMHDR *hdr, UINT *mask, UINT requiredMask, WCHAR **text,
+                                         INT *textMax, DWORD flags)
+{
+    CHAR *sendBuffer = NULL;
+    CHAR *receiveBuffer;
+    INT bufferSize;
+    WCHAR *oldText;
+    INT oldTextMax;
+    LRESULT ret = NO_ERROR;
+
+    oldText = *text;
+    oldTextMax = textMax ? *textMax : 0;
+
+    hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
+
+    if (mask && !(*mask & requiredMask))
+    {
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+        if (flags & SET_NULL_IF_NO_MASK) oldText = NULL;
+        goto done;
+    }
+
+    if (oldTextMax < 0) goto done;
+
+    if ((*text && flags & (CONVERT_SEND | ZERO_SEND)) || (!*text && flags & SEND_EMPTY_IF_NULL))
+    {
+        bufferSize = textMax ? *textMax : lstrlenW(*text) + 1;
+        sendBuffer = heap_alloc_zero(bufferSize);
+        if (!sendBuffer) goto done;
+        if (!(flags & ZERO_SEND)) WideCharToMultiByte(CP_ACP, 0, *text, -1, sendBuffer, bufferSize, NULL, FALSE);
+        *text = (WCHAR *)sendBuffer;
+    }
+
+    ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+
+    if (*text && oldText && (flags & CONVERT_RECEIVE))
+    {
+        /* MultiByteToWideChar requires that source and destination are not the same buffer */
+        if (*text == oldText)
+        {
+            bufferSize = lstrlenA((CHAR *)*text)  + 1;
+            receiveBuffer = heap_alloc(bufferSize);
+            if (!receiveBuffer) goto done;
+            memcpy(receiveBuffer, *text, bufferSize);
+            MultiByteToWideChar(CP_ACP, 0, receiveBuffer, bufferSize, oldText, oldTextMax);
+            heap_free(receiveBuffer);
+        }
+        else
+            MultiByteToWideChar(CP_ACP, 0, (CHAR *)*text, -1, oldText, oldTextMax);
+    }
+
+done:
+    heap_free(sendBuffer);
+    *text = oldText;
+    return ret;
+}
+
+static LRESULT PAGER_Notify(PAGER_INFO *infoPtr, NMHDR *hdr)
+{
+    LRESULT ret;
+
+    if (infoPtr->bUnicode) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+
+    switch (hdr->code)
+    {
+    /* ComboBoxEx */
+    case CBEN_GETDISPINFOW:
+    {
+        NMCOMBOBOXEXW *nmcbe = (NMCOMBOBOXEXW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmcbe->ceItem.mask, CBEIF_TEXT, &nmcbe->ceItem.pszText,
+                                         &nmcbe->ceItem.cchTextMax, ZERO_SEND | SET_NULL_IF_NO_MASK | CONVERT_RECEIVE);
+    }
+    case CBEN_DRAGBEGINW:
+    {
+        NMCBEDRAGBEGINW *nmdbW = (NMCBEDRAGBEGINW *)hdr;
+        NMCBEDRAGBEGINA nmdbA = {{0}};
+        nmdbA.hdr.code = PAGER_GetAnsiNtfCode(nmdbW->hdr.code);
+        nmdbA.hdr.hwndFrom = nmdbW->hdr.hwndFrom;
+        nmdbA.hdr.idFrom = nmdbW->hdr.idFrom;
+        nmdbA.iItemid = nmdbW->iItemid;
+        WideCharToMultiByte(CP_ACP, 0, nmdbW->szText, ARRAY_SIZE(nmdbW->szText), nmdbA.szText, ARRAY_SIZE(nmdbA.szText),
+                            NULL, FALSE);
+        return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmdbA);
+    }
+    case CBEN_ENDEDITW:
+    {
+        NMCBEENDEDITW *nmedW = (NMCBEENDEDITW *)hdr;
+        NMCBEENDEDITA nmedA = {{0}};
+        nmedA.hdr.code = PAGER_GetAnsiNtfCode(nmedW->hdr.code);
+        nmedA.hdr.hwndFrom = nmedW->hdr.hwndFrom;
+        nmedA.hdr.idFrom = nmedW->hdr.idFrom;
+        nmedA.fChanged = nmedW->fChanged;
+        nmedA.iNewSelection = nmedW->iNewSelection;
+        nmedA.iWhy = nmedW->iWhy;
+        WideCharToMultiByte(CP_ACP, 0, nmedW->szText, ARRAY_SIZE(nmedW->szText), nmedA.szText, ARRAY_SIZE(nmedA.szText),
+                            NULL, FALSE);
+        return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmedA);
+    }
+    /* Date and Time Picker */
+    case DTN_FORMATW:
+    {
+        NMDATETIMEFORMATW *nmdtf = (NMDATETIMEFORMATW *)hdr;
+        WCHAR *oldFormat;
+        INT textLength;
+
+        hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
+        oldFormat = PAGER_ConvertText((WCHAR **)&nmdtf->pszFormat);
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)nmdtf);
+        PAGER_RestoreText((WCHAR **)&nmdtf->pszFormat, oldFormat);
+
+        if (nmdtf->pszDisplay)
+        {
+            textLength = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmdtf->pszDisplay, -1, 0, 0);
+            if (!PAGER_AdjustBuffer(infoPtr, textLength * sizeof(WCHAR))) return ret;
+            MultiByteToWideChar(CP_ACP, 0, (LPCSTR)nmdtf->pszDisplay, -1, infoPtr->pwszBuffer, textLength);
+            if (nmdtf->pszDisplay != nmdtf->szDisplay)
+                nmdtf->pszDisplay = infoPtr->pwszBuffer;
+            else
+            {
+                textLength = min(textLength, ARRAY_SIZE(nmdtf->szDisplay));
+                memcpy(nmdtf->szDisplay, infoPtr->pwszBuffer, textLength * sizeof(WCHAR));
+            }
+        }
+
+        return ret;
+    }
+    case DTN_FORMATQUERYW:
+    {
+        NMDATETIMEFORMATQUERYW *nmdtfq = (NMDATETIMEFORMATQUERYW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdtfq->pszFormat, NULL, CONVERT_SEND);
+    }
+    case DTN_WMKEYDOWNW:
+    {
+        NMDATETIMEWMKEYDOWNW *nmdtkd = (NMDATETIMEWMKEYDOWNW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdtkd->pszFormat, NULL, CONVERT_SEND);
+    }
+    case DTN_USERSTRINGW:
+    {
+        NMDATETIMESTRINGW *nmdts = (NMDATETIMESTRINGW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, (WCHAR **)&nmdts->pszUserString, NULL, CONVERT_SEND);
+    }
+    /* Header */
+    case HDN_BEGINTRACKW:
+    case HDN_DIVIDERDBLCLICKW:
+    case HDN_ENDTRACKW:
+    case HDN_ITEMCHANGEDW:
+    case HDN_ITEMCHANGINGW:
+    case HDN_ITEMCLICKW:
+    case HDN_ITEMDBLCLICKW:
+    case HDN_TRACKW:
+    {
+        NMHEADERW *nmh = (NMHEADERW *)hdr;
+        WCHAR *oldText = NULL, *oldFilterText = NULL;
+        HD_TEXTFILTERW *tf = NULL;
+
+        hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
+
+        if (!nmh->pitem) return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+        if (nmh->pitem->mask & HDI_TEXT) oldText = PAGER_ConvertText(&nmh->pitem->pszText);
+        if ((nmh->pitem->mask & HDI_FILTER) && (nmh->pitem->type == HDFT_ISSTRING) && nmh->pitem->pvFilter)
+        {
+            tf = (HD_TEXTFILTERW *)nmh->pitem->pvFilter;
+            oldFilterText = PAGER_ConvertText(&tf->pszText);
+        }
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+        PAGER_RestoreText(&nmh->pitem->pszText, oldText);
+        if (tf) PAGER_RestoreText(&tf->pszText, oldFilterText);
+        return ret;
+    }
+    case HDN_GETDISPINFOW:
+    {
+        NMHDDISPINFOW *nmhddi = (NMHDDISPINFOW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmhddi->mask, HDI_TEXT, &nmhddi->pszText, &nmhddi->cchTextMax,
+                                         SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE);
+    }
+    /* List View */
+    case LVN_BEGINLABELEDITW:
+    case LVN_ENDLABELEDITW:
+    case LVN_SETDISPINFOW:
+    {
+        NMLVDISPINFOW *nmlvdi = (NMLVDISPINFOW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvdi->item.mask, LVIF_TEXT, &nmlvdi->item.pszText,
+                                         &nmlvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE);
+    }
+    case LVN_GETDISPINFOW:
+    {
+        NMLVDISPINFOW *nmlvdi = (NMLVDISPINFOW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvdi->item.mask, LVIF_TEXT, &nmlvdi->item.pszText,
+                                         &nmlvdi->item.cchTextMax, CONVERT_RECEIVE);
+    }
+    case LVN_GETINFOTIPW:
+    {
+        NMLVGETINFOTIPW *nmlvgit = (NMLVGETINFOTIPW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmlvgit->pszText, &nmlvgit->cchTextMax,
+                                         CONVERT_SEND | CONVERT_RECEIVE);
+    }
+    case LVN_INCREMENTALSEARCHW:
+    case LVN_ODFINDITEMW:
+    {
+        NMLVFINDITEMW *nmlvfi = (NMLVFINDITEMW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmlvfi->lvfi.flags, LVFI_STRING | LVFI_SUBSTRING,
+                                         (WCHAR **)&nmlvfi->lvfi.psz, NULL, CONVERT_SEND);
+    }
+    /* Toolbar */
+    case TBN_GETBUTTONINFOW:
+    {
+        NMTOOLBARW *nmtb = (NMTOOLBARW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtb->pszText, &nmtb->cchText,
+                                         SEND_EMPTY_IF_NULL | CONVERT_SEND | CONVERT_RECEIVE);
+    }
+    case TBN_GETINFOTIPW:
+    {
+        NMTBGETINFOTIPW *nmtbgit = (NMTBGETINFOTIPW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtbgit->pszText, &nmtbgit->cchTextMax, CONVERT_RECEIVE);
+    }
+    /* Tooltip */
+    case TTN_GETDISPINFOW:
+    {
+        NMTTDISPINFOW *nmttdiW = (NMTTDISPINFOW *)hdr;
+        NMTTDISPINFOA nmttdiA = {{0}};
+        INT size;
+
+        nmttdiA.hdr.code = PAGER_GetAnsiNtfCode(nmttdiW->hdr.code);
+        nmttdiA.hdr.hwndFrom = nmttdiW->hdr.hwndFrom;
+        nmttdiA.hdr.idFrom = nmttdiW->hdr.idFrom;
+        nmttdiA.hinst = nmttdiW->hinst;
+        nmttdiA.uFlags = nmttdiW->uFlags;
+        nmttdiA.lParam = nmttdiW->lParam;
+        nmttdiA.lpszText = nmttdiA.szText;
+        WideCharToMultiByte(CP_ACP, 0, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText), nmttdiA.szText,
+                            ARRAY_SIZE(nmttdiA.szText), NULL, FALSE);
+
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)&nmttdiA);
+
+        nmttdiW->hinst = nmttdiA.hinst;
+        nmttdiW->uFlags = nmttdiA.uFlags;
+        nmttdiW->lParam = nmttdiA.lParam;
+
+        MultiByteToWideChar(CP_ACP, 0, nmttdiA.szText, ARRAY_SIZE(nmttdiA.szText), nmttdiW->szText,
+                            ARRAY_SIZE(nmttdiW->szText));
+        if (!nmttdiA.lpszText)
+            nmttdiW->lpszText = nmttdiW->szText;
+        else if (!IS_INTRESOURCE(nmttdiA.lpszText))
+        {
+            size = MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, 0, 0);
+            if (size > ARRAY_SIZE(nmttdiW->szText))
+            {
+                if (!PAGER_AdjustBuffer(infoPtr, size * sizeof(WCHAR))) return ret;
+                MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, infoPtr->pwszBuffer, size);
+                nmttdiW->lpszText = infoPtr->pwszBuffer;
+                /* Override content in szText */
+                memcpy(nmttdiW->szText, nmttdiW->lpszText, min(sizeof(nmttdiW->szText), size * sizeof(WCHAR)));
+            }
+            else
+            {
+                MultiByteToWideChar(CP_ACP, 0, nmttdiA.lpszText, -1, nmttdiW->szText, ARRAY_SIZE(nmttdiW->szText));
+                nmttdiW->lpszText = nmttdiW->szText;
+            }
+        }
+        else
+        {
+            nmttdiW->szText[0] = 0;
+            nmttdiW->lpszText = (WCHAR *)nmttdiA.lpszText;
+        }
+
+        return ret;
+    }
+    /* Tree View */
+    case TVN_BEGINDRAGW:
+    case TVN_BEGINRDRAGW:
+    case TVN_ITEMEXPANDEDW:
+    case TVN_ITEMEXPANDINGW:
+    {
+        NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtv->itemNew.mask, TVIF_TEXT, &nmtv->itemNew.pszText, NULL,
+                                         CONVERT_SEND);
+    }
+    case TVN_DELETEITEMW:
+    {
+        NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtv->itemOld.mask, TVIF_TEXT, &nmtv->itemOld.pszText, NULL,
+                                         CONVERT_SEND);
+    }
+    case TVN_BEGINLABELEDITW:
+    case TVN_ENDLABELEDITW:
+    {
+        NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText,
+                                         &nmtvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE);
+    }
+    case TVN_SELCHANGINGW:
+    case TVN_SELCHANGEDW:
+    {
+        NMTREEVIEWW *nmtv = (NMTREEVIEWW *)hdr;
+        WCHAR *oldItemOldText = NULL;
+        WCHAR *oldItemNewText = NULL;
+
+        hdr->code = PAGER_GetAnsiNtfCode(hdr->code);
+
+        if (!((nmtv->itemNew.mask | nmtv->itemOld.mask) & TVIF_TEXT))
+            return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+
+        if (nmtv->itemOld.mask & TVIF_TEXT) oldItemOldText = PAGER_ConvertText(&nmtv->itemOld.pszText);
+        if (nmtv->itemNew.mask & TVIF_TEXT) oldItemNewText = PAGER_ConvertText(&nmtv->itemNew.pszText);
+
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+        PAGER_RestoreText(&nmtv->itemOld.pszText, oldItemOldText);
+        PAGER_RestoreText(&nmtv->itemNew.pszText, oldItemNewText);
+        return ret;
+    }
+    case TVN_GETDISPINFOW:
+    {
+        NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText,
+                                         &nmtvdi->item.cchTextMax, ZERO_SEND | CONVERT_RECEIVE);
+    }
+    case TVN_SETDISPINFOW:
+    {
+        NMTVDISPINFOW *nmtvdi = (NMTVDISPINFOW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, &nmtvdi->item.mask, TVIF_TEXT, &nmtvdi->item.pszText,
+                                         &nmtvdi->item.cchTextMax, SET_NULL_IF_NO_MASK | CONVERT_SEND | CONVERT_RECEIVE);
+    }
+    case TVN_GETINFOTIPW:
+    {
+        NMTVGETINFOTIPW *nmtvgit = (NMTVGETINFOTIPW *)hdr;
+        return PAGER_SendConvertedNotify(infoPtr, hdr, NULL, 0, &nmtvgit->pszText, &nmtvgit->cchTextMax, CONVERT_RECEIVE);
+    }
+    }
+    /* Other notifications, no need to convert */
+    return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
+}
+
 static LRESULT WINAPI
 PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
@@ -1089,7 +1544,12 @@ PAGER_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
         case WM_TIMER:
             return PAGER_Timer (infoPtr, (INT)wParam);
 
+        case WM_NOTIFYFORMAT:
+            return PAGER_NotifyFormat (infoPtr, lParam);
+
         case WM_NOTIFY:
+            return PAGER_Notify (infoPtr, (NMHDR *)lParam);
+
         case WM_COMMAND:
             return SendMessageW (infoPtr->hwndNotify, uMsg, wParam, lParam);
 
index fa348c2..9600e81 100644 (file)
@@ -65,7 +65,6 @@
 #include "uxtheme.h"
 
 #include "wine/debug.h"
-#include "wine/unicode.h"
 
 /******************************************************************************
  * Data structures
@@ -177,9 +176,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(propsheet);
 
 static WCHAR *heap_strdupW(const WCHAR *str)
 {
-    int len = strlenW(str) + 1;
+    int len = lstrlenW(str) + 1;
     WCHAR *ret = Alloc(len * sizeof(WCHAR));
-    strcpyW(ret, str);
+    lstrcpyW(ret, str);
     return ret;
 }
 
@@ -2021,6 +2020,13 @@ static BOOL PROPSHEET_SetCurSel(HWND hwndDlg,
     if (!psInfo->proppage[index].hwndPage) {
       if(!PROPSHEET_CreatePage(hwndDlg, index, psInfo, ppshpage)) {
         PROPSHEET_RemovePage(hwndDlg, index, NULL);
+
+        if (!psInfo->isModeless)
+        {
+            DestroyWindow(hwndDlg);
+            return FALSE;
+        }
+
         if(index >= psInfo->nPages)
           index--;
         if(index < 0)
@@ -2150,8 +2156,8 @@ static void PROPSHEET_SetTitleW(HWND hwndDlg, DWORD dwStyle, LPCWSTR lpszText)
   if (dwStyle & PSH_PROPTITLE)
   {
     WCHAR* dest;
-    int lentitle = strlenW(lpszText);
-    int lenprop  = strlenW(psInfo->strPropertiesFor);
+    int lentitle = lstrlenW(lpszText);
+    int lenprop  = lstrlenW(psInfo->strPropertiesFor);
 
     dest = Alloc( (lentitle + lenprop + 1)*sizeof (WCHAR));
     wsprintfW(dest, psInfo->strPropertiesFor, lpszText);
@@ -2793,8 +2799,8 @@ static BOOL PROPSHEET_IsDialogMessage(HWND hwnd, LPMSG lpMsg);
 
 static INT do_loop(const PropSheetInfo *psInfo)
 {
-    MSG msg;
-    INT ret = -1;
+    MSG msg = { 0 };
+    INT ret = 0;
     HWND hwnd = psInfo->hwnd;
     HWND parent = psInfo->ppshheader.hwndParent;
 
@@ -2814,11 +2820,8 @@ static INT do_loop(const PropSheetInfo *psInfo)
         }
     }
 
-    if(ret == 0)
-    {
+    if(ret == 0 && msg.message)
         PostQuitMessage(msg.wParam);
-        ret = -1;
-    }
 
     if(ret != -1)
         ret = psInfo->result;
@@ -2974,7 +2977,7 @@ static LPWSTR load_string( HINSTANCE instance, LPCWSTR str )
     }
     else
     {
-        int len = (strlenW(str) + 1) * sizeof(WCHAR);
+        int len = (lstrlenW(str) + 1) * sizeof(WCHAR);
         ret = Alloc( len );
         if (ret) memcpy( ret, str, len );
     }
index 19019d7..818ae99 100644 (file)
@@ -84,7 +84,6 @@
 #include "windef.h"
 #include "winbase.h"
 #include "wingdi.h"
-#include "wine/unicode.h"
 #include "winuser.h"
 #include "winnls.h"
 #include "commctrl.h"
@@ -280,6 +279,9 @@ static const char * const band_stylename[] = {
     "RBBS_VARIABLEHEIGHT",     /* 0040 */
     "RBBS_GRIPPERALWAYS",      /* 0080 */
     "RBBS_NOGRIPPER",          /* 0100 */
+    "RBBS_USECHEVRON",         /* 0200 */
+    "RBBS_HIDETITLE",          /* 0400 */
+    "RBBS_TOPALIGN",           /* 0800 */
     NULL };
 
 static const char * const band_maskname[] = {
@@ -295,50 +297,52 @@ static const char * const band_maskname[] = {
     "RBBIM_IDEALSIZE",     /*    0x00000200 */
     "RBBIM_LPARAM",        /*    0x00000400 */
     "RBBIM_HEADERSIZE",    /*    0x00000800 */
+    "RBBIM_CHEVRONLOCATION", /*  0x00001000 */
+    "RBBIM_CHEVRONSTATE",  /*    0x00002000 */
     NULL };
 
 
-static CHAR line[200];
-
 static const WCHAR themeClass[] = { 'R','e','b','a','r',0 };
 
 static CHAR *
-REBAR_FmtStyle( UINT style)
+REBAR_FmtStyle(char *buffer, UINT style)
 {
     INT i = 0;
 
-    *line = 0;
+    *buffer = 0;
     while (band_stylename[i]) {
        if (style & (1<<i)) {
-           if (*line != 0) strcat(line, " | ");
-           strcat(line, band_stylename[i]);
+           if (*buffer) strcat(buffer, " | ");
+           strcat(buffer, band_stylename[i]);
        }
        i++;
     }
-    return line;
+    return buffer;
 }
 
 
 static CHAR *
-REBAR_FmtMask( UINT mask)
+REBAR_FmtMask(char *buffer, UINT mask)
 {
     INT i = 0;
 
-    *line = 0;
+    *buffer = 0;
     while (band_maskname[i]) {
        if (mask & (1<<i)) {
-           if (*line != 0) strcat(line, " | ");
-           strcat(line, band_maskname[i]);
+           if (*buffer) strcat(buffer, " | ");
+           strcat(buffer, band_maskname[i]);
        }
        i++;
     }
-    return line;
+    return buffer;
 }
 
 
 static VOID
 REBAR_DumpBandInfo(const REBARBANDINFOW *pB)
 {
+    char buff[300];
+
     if( !TRACE_ON(rebar) ) return;
     TRACE("band info: ");
     if (pB->fMask & RBBIM_ID)
@@ -348,9 +352,9 @@ REBAR_DumpBandInfo(const REBARBANDINFOW *pB)
         TRACE(", clrF=0x%06x, clrB=0x%06x", pB->clrFore, pB->clrBack);
     TRACE("\n");
 
-    TRACE("band info: mask=0x%08x (%s)\n", pB->fMask, REBAR_FmtMask(pB->fMask));
+    TRACE("band info: mask=0x%08x (%s)\n", pB->fMask, REBAR_FmtMask(buff, pB->fMask));
     if (pB->fMask & RBBIM_STYLE)
-       TRACE("band info: style=0x%08x (%s)\n", pB->fStyle, REBAR_FmtStyle(pB->fStyle));
+       TRACE("band info: style=0x%08x (%s)\n", pB->fStyle, REBAR_FmtStyle(buff, pB->fStyle));
     if (pB->fMask & (RBBIM_SIZE | RBBIM_IDEALSIZE | RBBIM_HEADERSIZE | RBBIM_LPARAM )) {
        TRACE("band info:");
        if (pB->fMask & RBBIM_SIZE)
@@ -372,6 +376,7 @@ REBAR_DumpBandInfo(const REBARBANDINFOW *pB)
 static VOID
 REBAR_DumpBand (const REBAR_INFO *iP)
 {
+    char buff[300];
     REBAR_BAND *pB;
     UINT i;
 
@@ -397,10 +402,9 @@ REBAR_DumpBand (const REBAR_INFO *iP)
        if (pB->fMask & RBBIM_COLORS)
             TRACE(" clrF=0x%06x clrB=0x%06x", pB->clrFore, pB->clrBack);
        TRACE("\n");
-       TRACE("band # %u: mask=0x%08x (%s)\n", i, pB->fMask, REBAR_FmtMask(pB->fMask));
+       TRACE("band # %u: mask=0x%08x (%s)\n", i, pB->fMask, REBAR_FmtMask(buff, pB->fMask));
        if (pB->fMask & RBBIM_STYLE)
-           TRACE("band # %u: style=0x%08x (%s)\n",
-                 i, pB->fStyle, REBAR_FmtStyle(pB->fStyle));
+           TRACE("band # %u: style=0x%08x (%s)\n", i, pB->fStyle, REBAR_FmtStyle(buff, pB->fStyle));
        TRACE("band # %u: xHeader=%u",
              i, pB->cxHeader);
        if (pB->fMask & (RBBIM_SIZE | RBBIM_IDEALSIZE | RBBIM_LPARAM )) {
@@ -1306,8 +1310,8 @@ static int REBAR_SetBandsHeight(const REBAR_INFO *infoPtr, INT iBeginBand, INT i
     REBAR_BAND *lpBand;
     int yMaxHeight = 0;
     int yPos = yStart;
-    int row = REBAR_GetBand(infoPtr, iBeginBand)->iRow;
-    int i;
+    int row, i;
+
     for (i = iBeginBand; i < iEndBand; i = next_visible(infoPtr, i))
     {
         lpBand = REBAR_GetBand(infoPtr, i);
@@ -1316,6 +1320,8 @@ static int REBAR_SetBandsHeight(const REBAR_INFO *infoPtr, INT iBeginBand, INT i
     }
     TRACE("Bands [%d; %d) height: %d\n", iBeginBand, iEndBand, yMaxHeight);
 
+    row = iBeginBand < iEndBand ? REBAR_GetBand(infoPtr, iBeginBand)->iRow : 0;
+
     for (i = iBeginBand; i < iEndBand; i = next_visible(infoPtr, i))
     {
         lpBand = REBAR_GetBand(infoPtr, i);
@@ -2154,7 +2160,7 @@ REBAR_HandleUDDrag (REBAR_INFO *infoPtr, const POINT *ptsmove)
     }
     else
     {
-        /* Place the band in the prexisting row the mouse is hovering over */
+        /* Place the band in the preexisting row the mouse is hovering over */
         iRowBegin = first_visible(infoPtr);
         while(iRowBegin < infoPtr->uNumBands)
         {
index 1d2e39f..c81db5d 100644 (file)
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
  * 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.
- *     Because it has not yet been decided how to implement the two
- *     different modes in Wine, only the Windows XP mode is implemented.
  *   - 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.
@@ -730,7 +725,6 @@ static void STATIC_PaintBitmapfn(HWND hwnd, HDC hdc, DWORD style )
     HBITMAP hBitmap, oldbitmap;
     HBRUSH hbrush;
 
-    /* message is still sent, even if the returned brush is not used */
     hbrush = STATIC_SendWmCtlColorStatic(hwnd, hdc);
 
     if ((hBitmap = (HBITMAP)GetWindowLongPtrW( hwnd, HICON_GWL_OFFSET ))
index 34a299e..879b281 100644 (file)
@@ -36,7 +36,6 @@
 
 #include "windef.h"
 #include "winbase.h"
-#include "wine/unicode.h"
 #include "wingdi.h"
 #include "winuser.h"
 #include "winnls.h"
@@ -504,10 +503,10 @@ STATUSBAR_GetTextW (STATUS_INFO *infoPtr, INT nPart, LPWSTR buf)
     if (part->style & SBT_OWNERDRAW)
        result = (LRESULT)part->text;
     else {
-       result = part->text ? strlenW (part->text) : 0;
+       result = part->text ? lstrlenW (part->text) : 0;
        result |= (part->style << 16);
        if (part->text && buf)
-           strcpyW (buf, part->text);
+           lstrcpyW (buf, part->text);
     }
     return result;
 }
@@ -530,7 +529,7 @@ STATUSBAR_GetTextLength (STATUS_INFO *infoPtr, INT nPart)
        part = &infoPtr->parts[nPart];
 
     if ((~part->style & SBT_OWNERDRAW) && part->text)
-       result = strlenW(part->text);
+       result = lstrlenW(part->text);
     else
        result = 0;
 
@@ -754,16 +753,16 @@ STATUSBAR_SetTextT (STATUS_INFO *infoPtr, INT nPart, WORD style,
            if (!ntext) return FALSE;
             MultiByteToWideChar( CP_ACP, 0, atxt, -1, ntext, len );
        } else if (text) {
-           ntext = Alloc( (strlenW(text) + 1)*sizeof(WCHAR) );
+           ntext = Alloc( (lstrlenW(text) + 1)*sizeof(WCHAR) );
            if (!ntext) return FALSE;
-           strcpyW (ntext, text);
+           lstrcpyW (ntext, text);
        } else ntext = 0;
 
        /* replace nonprintable characters with spaces */
        if (ntext) {
            idx = ntext;
            while (*idx) {
-               if(!isprintW(*idx))
+               if(!iswprint(*idx))
                    *idx = ' ';
                idx++;
            }
@@ -944,11 +943,11 @@ STATUSBAR_WMCreate (HWND hwnd, const CREATESTRUCTA *lpCreate)
     
     OpenThemeData (hwnd, themeClass);
 
-    if (lpCreate->lpszName && (len = strlenW ((LPCWSTR)lpCreate->lpszName)))
+    if (lpCreate->lpszName && (len = lstrlenW ((LPCWSTR)lpCreate->lpszName)))
     {
         infoPtr->parts[0].text = Alloc ((len + 1)*sizeof(WCHAR));
         if (!infoPtr->parts[0].text) goto create_fail;
-        strcpyW (infoPtr->parts[0].text, (LPCWSTR)lpCreate->lpszName);
+        lstrcpyW (infoPtr->parts[0].text, (LPCWSTR)lpCreate->lpszName);
     }
 
     dwStyle = GetWindowLongW (hwnd, GWL_STYLE);
@@ -997,12 +996,12 @@ STATUSBAR_WMGetText (const STATUS_INFO *infoPtr, INT size, LPWSTR buf)
     if (!(infoPtr->parts[0].text))
         return 0;
 
-    len = strlenW (infoPtr->parts[0].text);
+    len = lstrlenW (infoPtr->parts[0].text);
 
     if (!size)
         return len;
     else if (size > len) {
-        strcpyW (buf, infoPtr->parts[0].text);
+        lstrcpyW (buf, infoPtr->parts[0].text);
        return len;
     }
     else {
@@ -1083,10 +1082,10 @@ STATUSBAR_WMSetText (const STATUS_INFO *infoPtr, LPCSTR text)
     Free (part->text);
     part->text = 0;
 
-    if (text && (len = strlenW((LPCWSTR)text))) {
+    if (text && (len = lstrlenW((LPCWSTR)text))) {
         part->text = Alloc ((len+1)*sizeof(WCHAR));
         if (!part->text) return FALSE;
-        strcpyW (part->text, (LPCWSTR)text);
+        lstrcpyW (part->text, (LPCWSTR)text);
     }
 
     InvalidateRect(infoPtr->Self, &part->bound, FALSE);
index 112d9d3..d5bc6d3 100644 (file)
@@ -22,9 +22,6 @@
  *
  */
 
-#include "config.h"
-#include "wine/port.h"
-
 #include <stdarg.h>
 #include <string.h>
 #include <stdlib.h> /* atoi */
@@ -36,7 +33,6 @@
 
 #include "comctl32.h"
 
-#include "wine/unicode.h"
 
 #include "wine/debug.h"
 
@@ -208,7 +204,7 @@ INT WINAPI Str_GetPtrW (LPCWSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
     TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
 
     if (!lpDest && lpSrc)
-        return strlenW (lpSrc);
+        return lstrlenW (lpSrc);
 
     if (nMaxLen == 0)
         return 0;
@@ -218,7 +214,7 @@ INT WINAPI Str_GetPtrW (LPCWSTR lpSrc, LPWSTR lpDest, INT nMaxLen)
         return 0;
     }
 
-    len = strlenW (lpSrc);
+    len = lstrlenW (lpSrc);
     if (len >= nMaxLen)
         len = nMaxLen - 1;
 
@@ -238,11 +234,11 @@ BOOL WINAPI Str_SetPtrW (LPWSTR *lppDest, LPCWSTR lpSrc)
     TRACE("(%p %s)\n", lppDest, debugstr_w(lpSrc));
 
     if (lpSrc) {
-        INT len = strlenW (lpSrc) + 1;
+        INT len = lstrlenW (lpSrc) + 1;
         LPWSTR ptr = ReAlloc (*lppDest, len * sizeof(WCHAR));
         if (!ptr)
             return FALSE;
-        strcpyW (ptr, lpSrc);
+        lstrcpyW (ptr, lpSrc);
         *lppDest = ptr;
     }
     else {
@@ -391,8 +387,8 @@ LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
   if (!lpszStr || !lpszSearch || !*lpszSearch)
     return NULL;
 
-  iLen = strlenW(lpszSearch);
-  end = lpszStr + strlenW(lpszStr);
+  iLen = lstrlenW(lpszSearch);
+  end = lpszStr + lstrlenW(lpszStr);
 
   while (lpszStr + iLen <= end)
   {
@@ -410,7 +406,7 @@ LPWSTR WINAPI StrStrIW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
  */
 INT WINAPI StrToIntW (LPCWSTR lpString)
 {
-    return atoiW(lpString);
+    return wcstol(lpString, NULL, 10);
 }
 
 /*************************************************************************
@@ -472,7 +468,7 @@ LPWSTR WINAPI StrChrW(LPCWSTR lpszStr, WCHAR ch)
   TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
 
   if (lpszStr)
-    lpszRet = strchrW(lpszStr, ch);
+    lpszRet = wcschr(lpszStr, ch);
   return lpszRet;
 }
 
@@ -558,7 +554,7 @@ LPWSTR WINAPI StrRChrW(LPCWSTR str, LPCWSTR end, WORD ch)
     WCHAR *ret = NULL;
 
     if (!str) return NULL;
-    if (!end) end = str + strlenW(str);
+    if (!end) end = str + lstrlenW(str);
     while (str < end)
     {
         if (*str == ch) ret = (WCHAR *)str;
@@ -594,7 +590,7 @@ LPSTR WINAPI StrStrA(LPCSTR lpszStr, LPCSTR lpszSearch)
 LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
 {
     if (!lpszStr || !lpszSearch) return NULL;
-    return strstrW( lpszStr, lpszSearch );
+    return wcsstr( lpszStr, lpszSearch );
 }
 
 /*************************************************************************
@@ -638,10 +634,10 @@ LPWSTR WINAPI StrChrIW(LPCWSTR lpszStr, WCHAR ch)
 
   if (lpszStr)
   {
-    ch = toupperW(ch);
+    ch = towupper(ch);
     while (*lpszStr)
     {
-      if (toupperW(*lpszStr) == ch)
+      if (towupper(*lpszStr) == ch)
         return (LPWSTR)lpszStr;
       lpszStr++;
     }
@@ -713,10 +709,10 @@ LPWSTR WINAPI StrRStrIW(LPCWSTR lpszStr, LPCWSTR lpszEnd, LPCWSTR lpszSearch)
   if (!lpszStr || !lpszSearch || !*lpszSearch)
     return NULL;
 
-  iLen = strlenW(lpszSearch);
+  iLen = lstrlenW(lpszSearch);
 
   if (!lpszEnd)
-    lpszEnd = lpszStr + strlenW(lpszStr);
+    lpszEnd = lpszStr + lstrlenW(lpszStr);
   else /* reproduce the broken behaviour on Windows */
     lpszEnd += min(iLen - 1, lstrlenW(lpszEnd));
 
@@ -826,7 +822,7 @@ LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
     WCHAR *ret = NULL;
 
     if (!str) return NULL;
-    if (!end) end = str + strlenW(str);
+    if (!end) end = str + lstrlenW(str);
     while (str < end)
     {
         if (!COMCTL32_ChrCmpIW(*str, ch)) ret = (WCHAR *)str;
@@ -843,7 +839,7 @@ LPWSTR WINAPI StrRChrIW(LPCWSTR str, LPCWSTR end, WORD ch)
 int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
 {
     if (!lpszStr || !lpszMatch) return 0;
-    return strcspnW( lpszStr, lpszMatch );
+    return wcscspn( lpszStr, lpszMatch );
 }
 
 /*************************************************************************
index 91796a3..a00ef9e 100644 (file)
@@ -27,7 +27,6 @@
 #include "winnls.h"
 #include "commctrl.h"
 #include "comctl32.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "wine/list.h"
 
@@ -124,7 +123,7 @@ static PDOC_ITEM SYSLINK_AppendDocItem (SYSLINK_INFO *infoPtr, LPCWSTR Text, UIN
 {
     PDOC_ITEM Item;
 
-    textlen = min(textlen, strlenW(Text));
+    textlen = min(textlen, lstrlenW(Text));
     Item = Alloc(FIELD_OFFSET(DOC_ITEM, Text[textlen + 1]));
     if(Item == NULL)
     {
@@ -183,7 +182,7 @@ static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
     {
         if(*current == '<')
         {
-            if(!strncmpiW(current, SL_LINKOPEN, ARRAY_SIZE(SL_LINKOPEN)) && (CurrentType == slText))
+            if(!wcsnicmp(current, SL_LINKOPEN, ARRAY_SIZE(SL_LINKOPEN)) && (CurrentType == slText))
             {
                 BOOL ValidParam = FALSE, ValidLink = FALSE;
 
@@ -211,14 +210,14 @@ static UINT SYSLINK_ParseText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
                     
 CheckParameter:
                     /* compare the current position with all known parameters */
-                    if(!strncmpiW(tmp, SL_HREF, ARRAY_SIZE(SL_HREF)))
+                    if(!wcsnicmp(tmp, SL_HREF, ARRAY_SIZE(SL_HREF)))
                     {
                         taglen += 6;
                         ValidParam = TRUE;
                         CurrentParameter = &lpUrl;
                         CurrentParameterLen = &lenUrl;
                     }
-                    else if(!strncmpiW(tmp, SL_ID, ARRAY_SIZE(SL_ID)))
+                    else if(!wcsnicmp(tmp, SL_ID, ARRAY_SIZE(SL_ID)))
                     {
                         taglen += 4;
                         ValidParam = TRUE;
@@ -292,7 +291,7 @@ CheckParameter:
                     }
                 }
             }
-            else if(!strncmpiW(current, SL_LINKCLOSE, ARRAY_SIZE(SL_LINKCLOSE)) && (CurrentType == slLink) && firsttag)
+            else if(!wcsnicmp(current, SL_LINKCLOSE, ARRAY_SIZE(SL_LINKCLOSE)) && (CurrentType == slLink) && firsttag)
             {
                 /* there's a <a...> tag opened, first add the previous text, if present */
                 if(textstart != NULL && textlen > 0 && firsttag > textstart)
@@ -330,7 +329,7 @@ CheckParameter:
                         /* Copy the tag parameters */
                         if(lpID != NULL)
                         {
-                            nc = min(lenId, strlenW(lpID));
+                            nc = min(lenId, lstrlenW(lpID));
                             nc = min(nc, MAX_LINKID_TEXT - 1);
                             Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
                             if(Last->u.Link.szID != NULL)
@@ -342,7 +341,7 @@ CheckParameter:
                             Last->u.Link.szID = NULL;
                         if(lpUrl != NULL)
                         {
-                            nc = min(lenUrl, strlenW(lpUrl));
+                            nc = min(lenUrl, lstrlenW(lpUrl));
                             nc = min(nc, L_MAX_URL_LENGTH - 1);
                             Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
                             if(Last->u.Link.szUrl != NULL)
@@ -408,7 +407,7 @@ CheckParameter:
             /* Copy the tag parameters */
             if(lpID != NULL)
             {
-                nc = min(lenId, strlenW(lpID));
+                nc = min(lenId, lstrlenW(lpID));
                 nc = min(nc, MAX_LINKID_TEXT - 1);
                 Last->u.Link.szID = Alloc((nc + 1) * sizeof(WCHAR));
                 if(Last->u.Link.szID != NULL)
@@ -420,7 +419,7 @@ CheckParameter:
                 Last->u.Link.szID = NULL;
             if(lpUrl != NULL)
             {
-                nc = min(lenUrl, strlenW(lpUrl));
+                nc = min(lenUrl, lstrlenW(lpUrl));
                 nc = min(nc, L_MAX_URL_LENGTH - 1);
                 Last->u.Link.szUrl = Alloc((nc + 1) * sizeof(WCHAR));
                 if(Last->u.Link.szUrl != NULL)
@@ -566,12 +565,12 @@ static BOOL SYSLINK_WrapLine (LPWSTR Text, WCHAR BreakChar, int x, int *LineLen,
 {
     int i;
 
-    for (i = 0; i < nFit; i++) if (Text[i] == '\n') break;
+    for (i = 0; i < nFit; i++) if (Text[i] == '\r' || Text[i] == '\n') break;
 
     if (i == *LineLen) return FALSE;
 
     /* check if we're in the middle of a word */
-    if (Text[i] != '\n' && Text[i] != BreakChar)
+    if (Text[i] != '\r' && Text[i] != '\n' && Text[i] != BreakChar)
     {
         /* search for the beginning of the word */
         while (i && Text[i - 1] != BreakChar) i--;
@@ -654,6 +653,12 @@ static VOID SYSLINK_Render (const SYSLINK_INFO *infoPtr, HDC hdc, PRECT pRect)
             /* skip break characters unless they're the first of the doc item */
             if(tx != Current->Text || x == SL_LEFTMARGIN)
             {
+                if (n && *tx == '\r')
+                {
+                    tx++;
+                    SkipChars++;
+                    n--;
+                }
                 if (n && *tx == '\n')
                 {
                     tx++;
@@ -954,7 +959,7 @@ static LRESULT SYSLINK_SetText (SYSLINK_INFO *infoPtr, LPCWSTR Text)
 
 /***********************************************************************
  *           SYSLINK_SetFocusLink
- * Updates the focus status bits and focusses the specified link.
+ * Updates the focus status bits and focuses the specified link.
  * If no document item is specified, the focus bit will be removed from all links.
  * Returns the previous focused item.
  */
index 1ab9e13..3d98c64 100644 (file)
@@ -2,6 +2,7 @@
  * Task dialog control
  *
  * Copyright 2017 Fabian Maurer
+ * Copyright 2018 Zhiyi Zhang
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include "comctl32.h"
 
 #include "wine/debug.h"
-#include "wine/list.h"
-#include "wine/unicode.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
 
-#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
-#define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
-#define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align)
-#define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align)
-
 static const UINT DIALOG_MIN_WIDTH = 240;
 static const UINT DIALOG_SPACING = 5;
 static const UINT DIALOG_BUTTON_WIDTH = 50;
 static const UINT DIALOG_BUTTON_HEIGHT = 14;
+static const UINT DIALOG_EXPANDO_ICON_WIDTH = 10;
+static const UINT DIALOG_EXPANDO_ICON_HEIGHT = 10;
+static const UINT DIALOG_TIMER_MS = 200;
 
-static const UINT ID_MAIN_INSTRUCTION = 0xf000;
-static const UINT ID_CONTENT          = 0xf001;
+static const UINT ID_TIMER = 1;
 
-struct taskdialog_control
+struct taskdialog_info
 {
-    struct list entry;
-    DLGITEMTEMPLATE *template;
-    unsigned int template_size;
+    HWND hwnd;
+    const TASKDIALOGCONFIG *taskconfig;
+    DWORD last_timer_tick;
+    HFONT font;
+    HFONT main_instruction_font;
+    /* Control handles */
+    HWND main_icon;
+    HWND main_instruction;
+    HWND content;
+    HWND progress_bar;
+    HWND *radio_buttons;
+    INT radio_button_count;
+    HWND *command_links;
+    INT command_link_count;
+    HWND expanded_info;
+    HWND expando_button;
+    HWND verification_box;
+    HWND footer_icon;
+    HWND footer_text;
+    HWND *buttons;
+    INT button_count;
+    HWND default_button;
+    /* Dialog metrics */
+    struct
+    {
+        LONG x_baseunit;
+        LONG y_baseunit;
+        LONG h_spacing;
+        LONG v_spacing;
+    } m;
+    INT selected_radio_id;
+    BOOL verification_checked;
+    BOOL expanded;
+    BOOL has_cancel;
+    WCHAR *expanded_text;
+    WCHAR *collapsed_text;
 };
 
-struct taskdialog_button_desc
+struct button_layout_info
 {
-    int id;
-    const WCHAR *text;
-    unsigned int width;
-    unsigned int line;
-    HINSTANCE hinst;
+    LONG width;
+    LONG line;
 };
 
-struct taskdialog_template_desc
+static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam);
+static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id);
+static void taskdialog_layout(struct taskdialog_info *dialog_info);
+
+static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
 {
-    const TASKDIALOGCONFIG *taskconfig;
-    unsigned int dialog_height;
-    unsigned int dialog_width;
-    struct list controls;
-    WORD control_count;
-    LONG x_baseunit;
-    LONG y_baseunit;
-    HFONT font;
-    struct taskdialog_button_desc *default_button;
-};
+    if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4);
+    if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8);
+}
 
-struct taskdialog_info
+static void template_write_data(char **ptr, const void *src, unsigned int size)
 {
-    HWND hwnd;
-    PFTASKDIALOGCALLBACK callback;
-    LONG_PTR callback_data;
-};
+    memcpy(*ptr, src, size);
+    *ptr += size;
+}
 
-static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
+static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret)
 {
-    if (width)
-        *width = MulDiv(*width, 4, desc->x_baseunit);
-    if (height)
-        *height = MulDiv(*height, 8, desc->y_baseunit);
+    HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(),
+                                         MONITOR_DEFAULTTOPRIMARY);
+    MONITORINFO info;
+
+    info.cbSize = sizeof(info);
+    GetMonitorInfoW(monitor, &info);
+
+    if ((taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) && taskconfig->hwndParent)
+        GetWindowRect(taskconfig->hwndParent, ret);
+    else
+        *ret = info.rcWork;
+
+    return info.rcWork.right - info.rcWork.left;
 }
 
-static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
+static WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length)
 {
-    if (width)
-        *width = MulDiv(*width, desc->x_baseunit, 4);
-    if (height)
-        *height = MulDiv(*height, desc->y_baseunit, 8);
+    DWORD len = GetModuleFileNameW(NULL, name, length);
+    if (len && len < length)
+    {
+        WCHAR *p;
+        if ((p = wcsrchr(name, '/'))) name = p + 1;
+        if ((p = wcsrchr(name, '\\'))) name = p + 1;
+        return name;
+    }
+    else
+        return NULL;
 }
 
-static void template_write_data(char **ptr, const void *src, unsigned int size)
+static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
 {
-    memcpy(*ptr, src, size);
-    *ptr += size;
+    unsigned int size, title_size;
+    static const WORD fontsize = 0x7fff;
+    static const WCHAR emptyW[] = { 0 };
+    const WCHAR *titleW = NULL;
+    DLGTEMPLATE *template;
+    WCHAR pathW[MAX_PATH];
+    char *ptr;
+
+    /* Window title */
+    if (!taskconfig->pszWindowTitle)
+        titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
+    else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
+    {
+        if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
+            titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
+    }
+    else
+        titleW = taskconfig->pszWindowTitle;
+    if (!titleW)
+        titleW = emptyW;
+    title_size = (lstrlenW(titleW) + 1) * sizeof(WCHAR);
+
+    size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
+    size += title_size;
+    size += 2; /* font size */
+
+    template = Alloc(size);
+    if (!template) return NULL;
+
+    template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
+    if (taskconfig->dwFlags & TDF_CAN_BE_MINIMIZED) template->style |= WS_MINIMIZEBOX;
+    if (!(taskconfig->dwFlags & TDF_NO_SET_FOREGROUND)) template->style |= DS_SETFOREGROUND;
+    if (taskconfig->dwFlags & TDF_RTL_LAYOUT) template->dwExtendedStyle = WS_EX_LAYOUTRTL | WS_EX_RIGHT | WS_EX_RTLREADING;
+
+    ptr = (char *)(template + 1);
+    ptr += 2; /* menu */
+    ptr += 2; /* class */
+    template_write_data(&ptr, titleW, title_size);
+    template_write_data(&ptr, &fontsize, sizeof(fontsize));
+
+    return template;
 }
 
-/* used to calculate size for the controls */
-static void taskdialog_get_text_extent(const struct taskdialog_template_desc *desc, const WCHAR *text,
-        BOOL user_resource, SIZE *sz)
+static HWND taskdialog_find_button(HWND *buttons, INT count, INT id)
+{
+    INT button_id;
+    INT i;
+
+    for (i = 0; i < count; i++)
+    {
+        button_id = GetWindowLongW(buttons[i], GWLP_ID);
+        if (button_id == id) return buttons[i];
+    }
+
+    return NULL;
+}
+
+static void taskdialog_enable_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
+{
+    HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
+    if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
+    if (hwnd) EnableWindow(hwnd, enable);
+}
+
+static void taskdialog_click_button(struct taskdialog_info *dialog_info, INT id)
+{
+    if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, id, 0) == S_OK) EndDialog(dialog_info->hwnd, id);
+}
+
+static void taskdialog_button_set_shield(const struct taskdialog_info *dialog_info, INT id, BOOL elevate)
+{
+    HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
+    if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
+    if (hwnd) SendMessageW(hwnd, BCM_SETSHIELD, 0, elevate);
+}
+
+static void taskdialog_enable_radio_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
+{
+    HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
+    if (hwnd) EnableWindow(hwnd, enable);
+}
+
+static void taskdialog_click_radio_button(const struct taskdialog_info *dialog_info, INT id)
+{
+    HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
+    if (hwnd) SendMessageW(hwnd, BM_CLICK, 0, 0);
+}
+
+static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    return taskconfig->pfCallback
+               ? taskconfig->pfCallback(dialog_info->hwnd, notification, wparam, lparam, taskconfig->lpCallbackData)
+               : S_OK;
+}
+
+static void taskdialog_move_controls_vertically(HWND parent, HWND *controls, INT count, INT offset)
+{
+    RECT rect;
+    POINT pt;
+    INT i;
+
+    for (i = 0; i < count; i++)
+    {
+        if (!controls[i]) continue;
+
+        GetWindowRect(controls[i], &rect);
+        pt.x = rect.left;
+        pt.y = rect.top;
+        MapWindowPoints(HWND_DESKTOP, parent, &pt, 1);
+        SetWindowPos(controls[i], 0, pt.x, pt.y + offset, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+    }
+}
+
+static void taskdialog_toggle_expando_control(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    const WCHAR *text;
+    RECT info_rect, rect;
+    INT height, offset;
+
+    dialog_info->expanded = !dialog_info->expanded;
+    text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+    SendMessageW(dialog_info->expando_button, WM_SETTEXT, 0, (LPARAM)text);
+    ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
+
+    GetWindowRect(dialog_info->expanded_info, &info_rect);
+    /* If expanded information starts up not expanded, call taskdialog_layout()
+     * to to set size for expanded information control at least once */
+    if (IsRectEmpty(&info_rect))
+    {
+        taskdialog_layout(dialog_info);
+        return;
+    }
+    height = info_rect.bottom - info_rect.top + dialog_info->m.v_spacing;
+    offset = dialog_info->expanded ? height : -height;
+
+    /* Update vertical layout, move all controls after expanded information */
+    /* Move dialog */
+    GetWindowRect(dialog_info->hwnd, &rect);
+    SetWindowPos(dialog_info->hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top + offset,
+                 SWP_NOMOVE | SWP_NOZORDER);
+    /* Move controls */
+    if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA))
+    {
+        taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->progress_bar, 1, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->expando_button, 1, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->verification_box, 1, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_icon, 1, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_text, 1, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->buttons, dialog_info->button_count, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->radio_buttons,
+                                            dialog_info->radio_button_count, offset);
+        taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->command_links,
+                                            dialog_info->command_link_count, offset);
+    }
+}
+
+static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id)
+{
+    INT command_id;
+    HWND button, radio_button;
+
+    /* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
+    command_id = hwnd ? GetWindowLongW(hwnd, GWLP_ID) : id;
+
+    if (hwnd && hwnd == dialog_info->expando_button)
+    {
+        taskdialog_toggle_expando_control(dialog_info);
+        taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
+        return;
+    }
+
+    if (hwnd && hwnd == dialog_info->verification_box)
+    {
+        dialog_info->verification_checked = !dialog_info->verification_checked;
+        taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
+        return;
+    }
+
+    radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
+    if (radio_button)
+    {
+        dialog_info->selected_radio_id = command_id;
+        taskdialog_notify(dialog_info, TDN_RADIO_BUTTON_CLICKED, command_id, 0);
+        return;
+    }
+
+    button = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, command_id);
+    if (!button) button = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, command_id);
+    if (!button && command_id == IDOK)
+    {
+        button = dialog_info->command_link_count > 0 ? dialog_info->command_links[0] : dialog_info->buttons[0];
+        command_id = GetWindowLongW(button, GWLP_ID);
+    }
+
+    if (button && taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
+        EndDialog(dialog_info->hwnd, command_id);
+}
+
+static WCHAR *taskdialog_gettext(struct taskdialog_info *dialog_info, BOOL user_resource, const WCHAR *text)
 {
-    RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */
     const WCHAR *textW = NULL;
-    static const WCHAR nulW;
-    unsigned int length;
-    HFONT oldfont;
-    HDC hdc;
+    INT length;
+    WCHAR *ret;
 
     if (IS_INTRESOURCE(text))
     {
-        if (!(length = LoadStringW(user_resource ? desc->taskconfig->hInstance : COMCTL32_hModule,
-                (UINT_PTR)text, (WCHAR *)&textW, 0)))
-        {
-            WARN("Failed to load text\n");
-            textW = &nulW;
-            length = 0;
-        }
+        if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule,
+                                   (UINT_PTR)text, (WCHAR *)&textW, 0)))
+            return NULL;
     }
     else
     {
         textW = text;
-        length = strlenW(textW);
+        length = lstrlenW(textW);
     }
 
-    hdc = GetDC(0);
-    oldfont = SelectObject(hdc, desc->font);
+    ret = Alloc((length + 1) * sizeof(WCHAR));
+    if (ret) memcpy(ret, textW, length * sizeof(WCHAR));
 
-    dialogunits_to_pixels(desc, &rect.right, NULL);
-    DrawTextW(hdc, textW, length, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK);
-    pixels_to_dialogunits(desc, &rect.right, &rect.bottom);
+    return ret;
+}
 
-    SelectObject(hdc, oldfont);
-    ReleaseDC(0, hdc);
+static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info)
+{
+    return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS;
+}
 
-    sz->cx = rect.right - rect.left;
-    sz->cy = rect.bottom - rect.top;
+static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info)
+{
+    return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON);
 }
 
-static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc, WORD id, const WCHAR *class,
-        HINSTANCE hInstance, const WCHAR *text, DWORD style, short x, short y, short cx, short cy)
+static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size,
+                                      BOOL syslink)
 {
-    struct taskdialog_control *control = Alloc(sizeof(*control));
-    unsigned int size, class_size, text_size;
-    DLGITEMTEMPLATE *template;
-    static const WCHAR nulW;
-    const WCHAR *textW;
-    char *ptr;
+    DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
+    HFONT hfont, old_hfont;
+    HDC hdc;
+    RECT rect = {0};
+    WCHAR *text;
+    INT text_length;
 
-    class_size = (strlenW(class) + 1) * sizeof(WCHAR);
+    if (syslink)
+    {
+        SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size);
+        return;
+    }
 
-    if (IS_INTRESOURCE(text))
-        text_size = LoadStringW(hInstance, (UINT_PTR)text, (WCHAR *)&textW, 0) * sizeof(WCHAR);
+    if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
+        style |= DT_RIGHT | DT_RTLREADING;
     else
+        style |= DT_LEFT;
+
+    hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
+    text_length = GetWindowTextLengthW(hwnd);
+    text = Alloc((text_length + 1) * sizeof(WCHAR));
+    if (!text)
     {
-        textW = text;
-        text_size = strlenW(textW) * sizeof(WCHAR);
+        size->cx = 0;
+        size->cy = 0;
+        return;
     }
+    GetWindowTextW(hwnd, text, text_length + 1);
+    hdc = GetDC(hwnd);
+    old_hfont = SelectObject(hdc, hfont);
+    rect.right = max_width;
+    size->cy = DrawTextW(hdc, text, text_length, &rect, style);
+    size->cx = min(max_width, rect.right - rect.left);
+    if (old_hfont) SelectObject(hdc, old_hfont);
+    ReleaseDC(hwnd, hdc);
+    Free(text);
+}
 
-    size = sizeof(DLGITEMTEMPLATE);
-    size += class_size;
-    size += text_size + sizeof(WCHAR);
-    size += sizeof(WORD); /* creation data */
+static void taskdialog_get_button_size(HWND hwnd, LONG max_width, SIZE *size)
+{
+    size->cx = max_width;
+    size->cy = 0;
+    SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)size);
+}
 
-    control->template = template = Alloc(size);
-    control->template_size = size;
+static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
+{
+    DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
+    HFONT hfont, old_hfont;
+    HDC hdc;
+    RECT rect = {0};
+    LONG icon_width, icon_height, text_offset;
+    LONG max_width, max_text_height;
 
-    template->style = WS_VISIBLE | style;
-    template->dwExtendedStyle = 0;
-    template->x = x;
-    template->y = y;
-    template->cx = cx;
-    template->cy = cy;
-    template->id = id;
-    ptr = (char *)(template + 1);
-    template_write_data(&ptr, class, class_size);
-    template_write_data(&ptr, textW, text_size);
-    template_write_data(&ptr, &nulW, sizeof(nulW));
+    hdc = GetDC(hwnd);
+    hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
+    old_hfont = SelectObject(hdc, hfont);
+
+    icon_width = DIALOG_EXPANDO_ICON_WIDTH;
+    icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
+    taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
+
+    GetCharWidthW(hdc, '0', '0', &text_offset);
+    text_offset /= 2;
+
+    if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
+        style |= DT_RIGHT | DT_RTLREADING;
+    else
+        style |= DT_LEFT;
+
+    max_width = DIALOG_MIN_WIDTH / 2;
+    taskdialog_du_to_px(dialog_info, &max_width, NULL);
 
-    list_add_tail(&desc->controls, &control->entry);
-    desc->control_count++;
-    return ALIGNED_LENGTH(size, 3);
+    rect.right = max_width - icon_width - text_offset;
+    max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
+    size->cy = max(max_text_height, icon_height);
+    size->cx = rect.right - rect.left;
+
+    rect.right = max_width - icon_width - text_offset;
+    max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
+    size->cy = max(size->cy, max_text_height);
+    size->cx = max(size->cx, rect.right - rect.left);
+    size->cx = min(size->cx, max_width);
+
+    if (old_hfont) SelectObject(hdc, old_hfont);
+    ReleaseDC(hwnd, hdc);
 }
 
-static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc *desc, WORD id, const WCHAR *str)
+static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
 {
-    unsigned int size;
-    SIZE sz;
+    if (icon == TD_WARNING_ICON)
+        return IDI_WARNING;
+    else if (icon == TD_ERROR_ICON)
+        return IDI_ERROR;
+    else if (icon == TD_INFORMATION_ICON)
+        return IDI_INFORMATION;
+    else if (icon == TD_SHIELD_ICON)
+        return IDI_SHIELD;
+    else
+        return (ULONG_PTR)icon;
+}
 
-    if (!str)
-        return 0;
+static void taskdialog_set_icon(struct taskdialog_info *dialog_info, INT element, HICON icon)
+{
+    DWORD flags = dialog_info->taskconfig->dwFlags;
+    INT cx = 0, cy = 0;
+    HICON hicon;
 
-    taskdialog_get_text_extent(desc, str, TRUE, &sz);
+    if (!icon) return;
 
-    desc->dialog_height += DIALOG_SPACING;
-    size = taskdialog_add_control(desc, id, WC_STATICW, desc->taskconfig->hInstance, str, 0, DIALOG_SPACING,
-            desc->dialog_height, sz.cx, sz.cy);
-    desc->dialog_height += sz.cy + DIALOG_SPACING;
-    return size;
+    if (((flags & TDF_USE_HICON_MAIN) && element == TDIE_ICON_MAIN)
+        || ((flags & TDF_USE_HICON_FOOTER) && element == TDIE_ICON_FOOTER))
+        hicon = icon;
+    else
+    {
+        if (element == TDIE_ICON_FOOTER)
+        {
+            cx = GetSystemMetrics(SM_CXSMICON);
+            cy = GetSystemMetrics(SM_CYSMICON);
+        }
+        hicon = LoadImageW(dialog_info->taskconfig->hInstance, (LPCWSTR)icon, IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTSIZE);
+        if (!hicon)
+            hicon = LoadImageW(NULL, (LPCWSTR)taskdialog_get_standard_icon((LPCWSTR)icon), IMAGE_ICON, cx, cy,
+                               LR_SHARED | LR_DEFAULTSIZE);
+    }
+
+    if (!hicon) return;
+
+    if (element == TDIE_ICON_MAIN)
+    {
+        SendMessageW(dialog_info->hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hicon);
+        SendMessageW(dialog_info->main_icon, STM_SETICON, (WPARAM)hicon, 0);
+    }
+    else if (element == TDIE_ICON_FOOTER)
+        SendMessageW(dialog_info->footer_icon, STM_SETICON, (WPARAM)hicon, 0);
+}
+
+static void taskdialog_set_element_text(struct taskdialog_info *dialog_info, TASKDIALOG_ELEMENTS element,
+                                        const WCHAR *text)
+{
+    HWND hwnd = NULL;
+    WCHAR *textW;
+
+    if (element == TDE_CONTENT)
+        hwnd = dialog_info->content;
+    else if (element == TDE_EXPANDED_INFORMATION)
+        hwnd = dialog_info->expanded_info;
+    else if (element == TDE_FOOTER)
+        hwnd = dialog_info->footer_text;
+    else if (element == TDE_MAIN_INSTRUCTION)
+        hwnd = dialog_info->main_instruction;
+
+    if (!hwnd) return;
+
+    textW = taskdialog_gettext(dialog_info, TRUE, text);
+    SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
+    Free(textW);
 }
 
-static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc)
+static void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info)
 {
-    return taskdialog_add_static_label(desc, ID_MAIN_INSTRUCTION, desc->taskconfig->pszMainInstruction);
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    HWND default_button;
+
+    if (!dialog_info->radio_button_count) return;
+
+    default_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count,
+                                            taskconfig->nDefaultRadioButton);
+
+    if (!default_button && !(taskconfig->dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON))
+        default_button = dialog_info->radio_buttons[0];
+
+    if (default_button)
+    {
+        SendMessageW(default_button, BM_SETCHECK, BST_CHECKED, 0);
+        taskdialog_on_button_click(dialog_info, default_button, 0);
+    }
 }
 
-static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc)
+static void taskdialog_add_main_icon(struct taskdialog_info *dialog_info)
 {
-    return taskdialog_add_static_label(desc, ID_CONTENT, desc->taskconfig->pszContent);
+    if (!dialog_info->taskconfig->u.hMainIcon) return;
+
+    dialog_info->main_icon =
+        CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
+    taskdialog_set_icon(dialog_info, TDIE_ICON_MAIN, dialog_info->taskconfig->u.hMainIcon);
 }
 
-static void taskdialog_init_button(struct taskdialog_button_desc *button, struct taskdialog_template_desc *desc,
-        int id, const WCHAR *text, BOOL custom_button)
+static HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink)
 {
-    SIZE sz;
+    WCHAR *textW;
+    HWND hwnd;
+    const WCHAR *class;
+    DWORD style = WS_CHILD | WS_VISIBLE;
 
-    taskdialog_get_text_extent(desc, text, custom_button, &sz);
+    if (!text) return NULL;
 
-    button->id = id;
-    button->text = text;
-    button->width = max(DIALOG_BUTTON_WIDTH, sz.cx + DIALOG_SPACING * 2);
-    button->line = 0;
-    button->hinst = custom_button ? desc->taskconfig->hInstance : COMCTL32_hModule;
+    class = syslink ? WC_LINK : WC_STATICW;
+    if (syslink) style |= WS_TABSTOP;
+    textW = taskdialog_gettext(dialog_info, TRUE, text);
+    hwnd = CreateWindowW(class, textW, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
+    Free(textW);
 
-    if (id == desc->taskconfig->nDefaultButton)
-        desc->default_button = button;
+    SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0);
+    return hwnd;
 }
 
-static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons,
-    unsigned int *button_count)
+static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info)
 {
-    DWORD flags = desc->taskconfig->dwCommonButtons;
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    NONCLIENTMETRICSW ncm;
 
-#define TASKDIALOG_INIT_COMMON_BUTTON(id) \
-    do { \
-        taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \
-    } while(0)
+    if (!taskconfig->pszMainInstruction) return;
 
-    if (flags & TDCBF_OK_BUTTON)
-        TASKDIALOG_INIT_COMMON_BUTTON(OK);
-    if (flags & TDCBF_YES_BUTTON)
-        TASKDIALOG_INIT_COMMON_BUTTON(YES);
-    if (flags & TDCBF_NO_BUTTON)
-        TASKDIALOG_INIT_COMMON_BUTTON(NO);
-    if (flags & TDCBF_RETRY_BUTTON)
-        TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
-    if (flags & TDCBF_CANCEL_BUTTON)
-        TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
-    if (flags & TDCBF_CLOSE_BUTTON)
-        TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
+    ncm.cbSize = sizeof(ncm);
+    SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
+    /* 1.25 times the height */
+    ncm.lfMessageFont.lfHeight = ncm.lfMessageFont.lfHeight * 5 / 4;
+    ncm.lfMessageFont.lfWeight = FW_BOLD;
+    dialog_info->main_instruction_font = CreateFontIndirectW(&ncm.lfMessageFont);
 
-#undef TASKDIALOG_INIT_COMMON_BUTTON
+    dialog_info->main_instruction =
+        taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE);
 }
 
-static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc)
+static void taskdialog_add_content(struct taskdialog_info *dialog_info)
 {
-    unsigned int count = 0, buttons_size, i, line_count, size = 0;
-    unsigned int location_x, *line_widths, alignment = ~0u;
-    const TASKDIALOGCONFIG *taskconfig = desc->taskconfig;
-    struct taskdialog_button_desc *buttons;
+    dialog_info->content = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszContent, dialog_info->font,
+                                                   taskdialog_hyperlink_enabled(dialog_info));
+}
+
+static void taskdialog_add_progress_bar(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    DWORD style = PBS_SMOOTH | PBS_SMOOTHREVERSE | WS_CHILD | WS_VISIBLE;
+
+    if (!(taskconfig->dwFlags & (TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR))) return;
+    if (taskconfig->dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) style |= PBS_MARQUEE;
+    dialog_info->progress_bar =
+        CreateWindowW(PROGRESS_CLASSW, NULL, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
+}
+
+static void taskdialog_add_radio_buttons(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    static const DWORD style = BS_AUTORADIOBUTTON | BS_MULTILINE | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
+    WCHAR *textW;
+    INT i;
+
+    if (!taskconfig->cRadioButtons || !taskconfig->pRadioButtons) return;
+
+    dialog_info->radio_buttons = Alloc(taskconfig->cRadioButtons * sizeof(*dialog_info->radio_buttons));
+    if (!dialog_info->radio_buttons) return;
+
+    dialog_info->radio_button_count = taskconfig->cRadioButtons;
+    for (i = 0; i < dialog_info->radio_button_count; i++)
+    {
+        textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pRadioButtons[i].pszButtonText);
+        dialog_info->radio_buttons[i] =
+            CreateWindowW(WC_BUTTONW, textW, i == 0 ? style | WS_GROUP : style, 0, 0, 0, 0, dialog_info->hwnd,
+                          LongToHandle(taskconfig->pRadioButtons[i].nButtonID), 0, NULL);
+        SendMessageW(dialog_info->radio_buttons[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
+        Free(textW);
+    }
+}
+
+static void taskdialog_add_command_links(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    DWORD default_style = BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP, style;
+    BOOL is_default;
+    WCHAR *textW;
+    INT i;
+
+    if (!taskconfig->cButtons || !taskconfig->pButtons || !taskdialog_use_command_link(dialog_info)) return;
+
+    dialog_info->command_links = Alloc(taskconfig->cButtons * sizeof(*dialog_info->command_links));
+    if (!dialog_info->command_links) return;
+
+    dialog_info->command_link_count = taskconfig->cButtons;
+    for (i = 0; i < dialog_info->command_link_count; i++)
+    {
+        is_default = taskconfig->pButtons[i].nButtonID == taskconfig->nDefaultButton;
+        style = is_default ? default_style | BS_DEFCOMMANDLINK : default_style | BS_COMMANDLINK;
+        textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pButtons[i].pszButtonText);
+        dialog_info->command_links[i] = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd,
+                                                      LongToHandle(taskconfig->pButtons[i].nButtonID), 0, NULL);
+        SendMessageW(dialog_info->command_links[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
+        Free(textW);
+
+        if (is_default && !dialog_info->default_button) dialog_info->default_button = dialog_info->command_links[i];
+    }
+}
+
+static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+
+    if (!taskconfig->pszExpandedInformation) return;
+
+    dialog_info->expanded = taskconfig->dwFlags & TDF_EXPANDED_BY_DEFAULT;
+    dialog_info->expanded_info = taskdialog_create_label(dialog_info, taskconfig->pszExpandedInformation,
+                                                         dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
+    ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
+}
+
+static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    const WCHAR *textW;
+
+    if (!taskconfig->pszExpandedInformation) return;
+
+    if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
+    {
+        dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
+        dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
+    }
+    else
+    {
+        textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
+                                                   : taskconfig->pszCollapsedControlText;
+        dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
+        textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
+                                                    : taskconfig->pszExpandedControlText;
+        dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
+    }
+
+    textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+
+    dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
+                                                0, 0, 0, dialog_info->hwnd, 0, 0, 0);
+    SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+}
+
+static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
+    WCHAR *textW;
+
+    /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
+    if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
+
+    if (!taskconfig->pszVerificationText) return;
+
+    textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
+    dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
+    SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+    Free(textW);
+
+    if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
+        SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
+}
+
+static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
+                                  BOOL custom_button)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    WCHAR *textW;
+
+    textW = taskdialog_gettext(dialog_info, custom_button, text);
+    *button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, dialog_info->hwnd,
+                            (HMENU)id, 0, NULL);
+    Free(textW);
+    SendMessageW(*button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+
+    if (id == taskconfig->nDefaultButton && !dialog_info->default_button) dialog_info->default_button = *button;
+}
+
+static void taskdialog_add_buttons(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    BOOL use_command_links = taskdialog_use_command_link(dialog_info);
+    DWORD flags = taskconfig->dwCommonButtons;
+    INT count, max_count;
 
     /* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
-    buttons_size = 6;
-    if (taskconfig->cButtons && taskconfig->pButtons)
-        buttons_size += taskconfig->cButtons;
+    max_count = 6;
+    if (!use_command_links && taskconfig->cButtons && taskconfig->pButtons) max_count += taskconfig->cButtons;
+
+    dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons));
+    if (!dialog_info->buttons) return;
+
+    for (count = 0; !use_command_links && count < taskconfig->cButtons; count++)
+        taskdialog_add_button(dialog_info, &dialog_info->buttons[count], taskconfig->pButtons[count].nButtonID,
+                              taskconfig->pButtons[count].pszButtonText, TRUE);
+
+#define TASKDIALOG_INIT_COMMON_BUTTON(id)                                                                             \
+    do                                                                                                                \
+    {                                                                                                                 \
+        taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
+                              FALSE);                                                                                 \
+    } while (0)
+
+    if (flags & TDCBF_OK_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(OK);
+    if (flags & TDCBF_YES_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(YES);
+    if (flags & TDCBF_NO_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(NO);
+    if (flags & TDCBF_RETRY_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
+    if (flags & TDCBF_CANCEL_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
+    if (flags & TDCBF_CLOSE_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
+
+    if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK);
+#undef TASKDIALOG_INIT_COMMON_BUTTON
 
-    if (!(buttons = Alloc(buttons_size * sizeof(*buttons))))
-        return 0;
+    dialog_info->button_count = count;
+}
 
-    /* Custom buttons */
-    if (taskconfig->cButtons && taskconfig->pButtons)
-        for (i = 0; i < taskconfig->cButtons; i++)
-            taskdialog_init_button(&buttons[count++], desc, taskconfig->pButtons[i].nButtonID,
-                    taskconfig->pButtons[i].pszButtonText, TRUE);
+static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info)
+{
+    if (!dialog_info->taskconfig->u2.hFooterIcon) return;
 
-    /* Common buttons */
-    taskdialog_init_common_buttons(desc, buttons, &count);
+    dialog_info->footer_icon =
+        CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, 0);
+    taskdialog_set_icon(dialog_info, TDIE_ICON_FOOTER, dialog_info->taskconfig->u2.hFooterIcon);
+}
 
-    /* There must be at least one button */
-    if (count == 0)
-        taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE);
+static void taskdialog_add_footer_text(struct taskdialog_info *dialog_info)
+{
+    dialog_info->footer_text = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszFooter,
+                                                       dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
+}
 
-    if (!desc->default_button)
-        desc->default_button = &buttons[0];
+static LONG taskdialog_get_dialog_width(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
+    LONG max_width, main_icon_width, screen_width;
+    RECT rect;
+    SIZE size;
+
+    screen_width = taskdialog_get_reference_rect(taskconfig, &rect);
+    if ((taskconfig->dwFlags & TDF_SIZE_TO_CONTENT) && !taskconfig->cxWidth)
+    {
+        max_width = DIALOG_MIN_WIDTH;
+        taskdialog_du_to_px(dialog_info, &max_width, NULL);
+        main_icon_width = dialog_info->m.h_spacing;
+        if (dialog_info->main_icon) main_icon_width += GetSystemMetrics(SM_CXICON);
+        if (dialog_info->content)
+        {
+            taskdialog_get_label_size(dialog_info, dialog_info->content, 0, &size, syslink);
+            max_width = max(max_width, size.cx + main_icon_width + dialog_info->m.h_spacing * 2);
+        }
+    }
+    else
+    {
+        max_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
+        taskdialog_du_to_px(dialog_info, &max_width, NULL);
+    }
+    max_width = min(max_width, screen_width);
+    return max_width;
+}
+
+static void taskdialog_label_layout(struct taskdialog_info *dialog_info, HWND hwnd, INT start_x, LONG dialog_width,
+                                    LONG *dialog_height, BOOL syslink)
+{
+    LONG x, y, max_width;
+    SIZE size;
+
+    if (!hwnd) return;
+
+    x = start_x + dialog_info->m.h_spacing;
+    y = *dialog_height + dialog_info->m.v_spacing;
+    max_width = dialog_width - x - dialog_info->m.h_spacing;
+    taskdialog_get_label_size(dialog_info, hwnd, max_width, &size, syslink);
+    SetWindowPos(hwnd, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+    *dialog_height = y + size.cy;
+}
+
+static void taskdialog_layout(struct taskdialog_info *dialog_info)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
+    static BOOL first_time = TRUE;
+    RECT ref_rect;
+    LONG dialog_width, dialog_height = 0;
+    LONG h_spacing, v_spacing;
+    LONG main_icon_right, main_icon_bottom;
+    LONG expando_right, expando_bottom;
+    struct button_layout_info *button_layout_infos;
+    LONG button_min_width, button_height;
+    LONG *line_widths, line_count, align;
+    LONG footer_icon_right, footer_icon_bottom;
+    LONG x, y;
+    SIZE size;
+    INT i;
+
+    taskdialog_get_reference_rect(dialog_info->taskconfig, &ref_rect);
+    dialog_width = taskdialog_get_dialog_width(dialog_info);
+
+    h_spacing = dialog_info->m.h_spacing;
+    v_spacing = dialog_info->m.v_spacing;
+
+    /* Main icon */
+    main_icon_right = 0;
+    main_icon_bottom = 0;
+    if (dialog_info->main_icon)
+    {
+        x = h_spacing;
+        y = dialog_height + v_spacing;
+        size.cx = GetSystemMetrics(SM_CXICON);
+        size.cy = GetSystemMetrics(SM_CYICON);
+        SetWindowPos(dialog_info->main_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        main_icon_right = x + size.cx;
+        main_icon_bottom = y + size.cy;
+    }
+
+    /* Main instruction */
+    taskdialog_label_layout(dialog_info, dialog_info->main_instruction, main_icon_right, dialog_width, &dialog_height,
+                            FALSE);
+
+    /* Content */
+    taskdialog_label_layout(dialog_info, dialog_info->content, main_icon_right, dialog_width, &dialog_height, syslink);
+
+    /* Expanded information */
+    if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
+        taskdialog_label_layout(dialog_info, dialog_info->expanded_info, main_icon_right, dialog_width, &dialog_height,
+                                syslink);
+
+    /* Progress bar */
+    if (dialog_info->progress_bar)
+    {
+        x = main_icon_right + h_spacing;
+        y = dialog_height + v_spacing;
+        size.cx = dialog_width - x - h_spacing;
+        size.cy = GetSystemMetrics(SM_CYVSCROLL);
+        SetWindowPos(dialog_info->progress_bar, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        dialog_height = y + size.cy;
+    }
+
+    /* Radio buttons */
+    for (i = 0; i < dialog_info->radio_button_count; i++)
+    {
+        x = main_icon_right + h_spacing;
+        y = dialog_height + v_spacing;
+        taskdialog_get_button_size(dialog_info->radio_buttons[i], dialog_width - x - h_spacing, &size);
+        size.cx = dialog_width - x - h_spacing;
+        SetWindowPos(dialog_info->radio_buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        dialog_height = y + size.cy;
+    }
+
+    /* Command links */
+    for (i = 0; i < dialog_info->command_link_count; i++)
+    {
+        x = main_icon_right + h_spacing;
+        y = dialog_height;
+        /* Only add spacing for the first command links. There is no vertical spacing between command links */
+        if (!i)
+            y += v_spacing;
+        taskdialog_get_button_size(dialog_info->command_links[i], dialog_width - x - h_spacing, &size);
+        size.cx = dialog_width - x - h_spacing;
+        /* Add spacing */
+        size.cy += 4;
+        SetWindowPos(dialog_info->command_links[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        dialog_height = y + size.cy;
+    }
 
-    /* For easy handling just allocate as many lines as buttons, the worst case. */
-    line_widths = Alloc(count * sizeof(*line_widths));
+    dialog_height = max(dialog_height, main_icon_bottom);
+
+    expando_right = 0;
+    expando_bottom = dialog_height;
+    /* Expando control */
+    if (dialog_info->expando_button)
+    {
+        x = h_spacing;
+        y = dialog_height + v_spacing;
+        taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
+        SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        expando_right = x + size.cx;
+        expando_bottom = y + size.cy;
+    }
+
+    /* Verification box */
+    if (dialog_info->verification_box)
+    {
+        x = h_spacing;
+        y = expando_bottom + v_spacing;
+        size.cx = DIALOG_MIN_WIDTH / 2;
+        taskdialog_du_to_px(dialog_info, &size.cx, NULL);
+        taskdialog_get_button_size(dialog_info->verification_box, size.cx, &size);
+        SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        expando_right = max(expando_right, x + size.cx);
+        expando_bottom = y + size.cy;
+    }
+
+    /* Common and custom buttons */
+    button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
+    line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
+
+    button_min_width = DIALOG_BUTTON_WIDTH;
+    button_height = DIALOG_BUTTON_HEIGHT;
+    taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
+    for (i = 0; i < dialog_info->button_count; i++)
+    {
+        taskdialog_get_button_size(dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2, &size);
+        button_layout_infos[i].width = max(size.cx, button_min_width);
+    }
 
     /* Separate buttons into lines */
-    location_x = DIALOG_SPACING;
-    for (i = 0, line_count = 0; i < count; i++)
+    x = expando_right + h_spacing;
+    for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
     {
-        if (location_x + buttons[i].width + DIALOG_SPACING > desc->dialog_width)
+        button_layout_infos[i].line = line_count;
+        x += button_layout_infos[i].width + h_spacing;
+        line_widths[line_count] += button_layout_infos[i].width + h_spacing;
+
+        if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
         {
-            location_x = DIALOG_SPACING;
+            x = expando_right + h_spacing;
             line_count++;
         }
-
-        buttons[i].line = line_count;
-
-        location_x += buttons[i].width + DIALOG_SPACING;
-        line_widths[line_count] += buttons[i].width + DIALOG_SPACING;
     }
     line_count++;
 
@@ -324,215 +1001,213 @@ static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc
         unsigned int j, last_button = 0;
         int diff_changed;
 
-        for (j = 0; j < count; j++)
-            if (buttons[j].line == i - 1)
-                last_button = j;
+        for (j = 0; j < dialog_info->button_count; j++)
+            if (button_layout_infos[j].line == i - 1) last_button = j;
 
         /* Difference in length of both lines if we wrapped the last button from the last line into this one */
-        diff_changed = abs(2 * buttons[last_button].width + line_widths[i] - line_widths[i - 1]);
+        diff_changed = abs(2 * button_layout_infos[last_button].width + line_widths[i] - line_widths[i - 1]);
 
         if (diff_changed < diff_now)
         {
-            buttons[last_button].line = i;
-            line_widths[i] += buttons[last_button].width;
-            line_widths[i - 1] -= buttons[last_button].width;
+            button_layout_infos[last_button].line = i;
+            line_widths[i] += button_layout_infos[last_button].width;
+            line_widths[i - 1] -= button_layout_infos[last_button].width;
         }
     }
 
     /* Calculate left alignment so all lines are as far right as possible. */
+    align = dialog_width - h_spacing;
     for (i = 0; i < line_count; i++)
     {
-        int new_alignment = desc->dialog_width - line_widths[i];
-        if (new_alignment < alignment)
-            alignment = new_alignment;
+        int new_alignment = dialog_width - line_widths[i];
+        if (new_alignment < align) align = new_alignment;
     }
 
-    /* Now that we got them all positioned, create all buttons */
-    location_x = alignment;
-    for (i = 0; i < count; i++)
+    /* Now that we got them all positioned, move all buttons */
+    x = align;
+    size.cy = button_height;
+    for (i = 0; i < dialog_info->button_count; i++)
     {
-        DWORD style = &buttons[i] == desc->default_button ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;
-
-        if (i > 0 && buttons[i].line != buttons[i - 1].line) /* New line */
+        /* New line */
+        if (i > 0 && button_layout_infos[i].line != button_layout_infos[i - 1].line)
         {
-            location_x = alignment;
-            desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
+            x = align;
+            dialog_height += size.cy + v_spacing;
         }
 
-        size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, buttons[i].hinst, buttons[i].text, style,
-                location_x, desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
-
-        location_x += buttons[i].width + DIALOG_SPACING;
+        y = dialog_height + v_spacing;
+        size.cx = button_layout_infos[i].width;
+        SetWindowPos(dialog_info->buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        x += button_layout_infos[i].width + h_spacing;
     }
 
-    /* Add height for last row and spacing */
-    desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
+    /* Add height for last row button and spacing */
+    dialog_height += size.cy + v_spacing;
+    dialog_height = max(dialog_height, expando_bottom);
 
+    Free(button_layout_infos);
     Free(line_widths);
-    Free(buttons);
-
-    return size;
-}
-
-static void taskdialog_clear_controls(struct list *controls)
-{
-    struct taskdialog_control *control, *control2;
 
-    LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, struct taskdialog_control, entry)
+    /* Footer icon */
+    footer_icon_right = 0;
+    footer_icon_bottom = dialog_height;
+    if (dialog_info->footer_icon)
     {
-        list_remove(&control->entry);
-        Free(control->template);
-        Free(control);
+        x = h_spacing;
+        y = dialog_height + v_spacing;
+        size.cx = GetSystemMetrics(SM_CXSMICON);
+        size.cy = GetSystemMetrics(SM_CYSMICON);
+        SetWindowPos(dialog_info->footer_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+        footer_icon_right = x + size.cx;
+        footer_icon_bottom = y + size.cy;
     }
-}
-
-static unsigned int taskdialog_get_reference_rect(const struct taskdialog_template_desc *desc, RECT *ret)
-{
-    HMONITOR monitor = MonitorFromWindow(desc->taskconfig->hwndParent ? desc->taskconfig->hwndParent : GetActiveWindow(),
-            MONITOR_DEFAULTTOPRIMARY);
-    MONITORINFO info;
-
-    info.cbSize = sizeof(info);
-    GetMonitorInfoW(monitor, &info);
 
-    if (desc->taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && desc->taskconfig->hwndParent)
-        GetWindowRect(desc->taskconfig->hwndParent, ret);
-    else
-        *ret = info.rcWork;
+    /* Footer text */
+    taskdialog_label_layout(dialog_info, dialog_info->footer_text, footer_icon_right, dialog_width, &dialog_height,
+                            syslink);
+    dialog_height = max(dialog_height, footer_icon_bottom);
 
-    pixels_to_dialogunits(desc, &ret->left, &ret->top);
-    pixels_to_dialogunits(desc, &ret->right, &ret->bottom);
+    /* Expanded information */
+    if ((taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
+        taskdialog_label_layout(dialog_info, dialog_info->expanded_info, 0, dialog_width, &dialog_height, syslink);
 
-    pixels_to_dialogunits(desc, &info.rcWork.left, &info.rcWork.top);
-    pixels_to_dialogunits(desc, &info.rcWork.right, &info.rcWork.bottom);
-    return info.rcWork.right - info.rcWork.left;
-}
+    /* Add height for spacing, title height and frame height */
+    dialog_height += v_spacing;
+    dialog_height += GetSystemMetrics(SM_CYCAPTION);
+    dialog_height += GetSystemMetrics(SM_CXDLGFRAME);
 
-static WCHAR *taskdialog_get_exe_name(const TASKDIALOGCONFIG *taskconfig, WCHAR *name, DWORD length)
-{
-    DWORD len = GetModuleFileNameW(NULL, name, length);
-    if (len && len < length)
+    if (first_time)
     {
-        WCHAR *p;
-        if ((p = strrchrW(name, '/'))) name = p + 1;
-        if ((p = strrchrW(name, '\\'))) name = p + 1;
-        return name;
+        x = (ref_rect.left + ref_rect.right - dialog_width) / 2;
+        y = (ref_rect.top + ref_rect.bottom - dialog_height) / 2;
+        SetWindowPos(dialog_info->hwnd, 0, x, y, dialog_width, dialog_height, SWP_NOZORDER);
+        first_time = FALSE;
     }
     else
-        return NULL;
+        SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
 }
 
-static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
+static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
 {
-    struct taskdialog_control *control, *control2;
-    unsigned int size, title_size, screen_width;
-    struct taskdialog_template_desc desc;
-    static const WORD fontsize = 0x7fff;
-    static const WCHAR emptyW[] = { 0 };
-    const WCHAR *titleW = NULL;
-    DLGTEMPLATE *template;
-    NONCLIENTMETRICSW ncm;
-    WCHAR pathW[MAX_PATH];
-    RECT ref_rect;
-    char *ptr;
+    HWND hwnd;
     HDC hdc;
+    RECT rect = {0};
+    WCHAR *text;
+    LONG icon_width, icon_height, text_offset;
+    UINT style = DFCS_FLAT;
+    BOOL draw_focus;
+
+    hdc = dis->hDC;
+    hwnd = dis->hwndItem;
+
+    SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
+
+    icon_width = DIALOG_EXPANDO_ICON_WIDTH;
+    icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
+    taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
+    rect.right = icon_width;
+    rect.bottom = icon_height;
+    style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
+    DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
+
+    GetCharWidthW(hdc, '0', '0', &text_offset);
+    text_offset /= 2;
+
+    rect = dis->rcItem;
+    rect.left += icon_width + text_offset;
+    text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+    DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
+
+    draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
+    if(draw_focus) DrawFocusRect(hdc, &rect);
+}
 
-    /* Window title */
-    if (!taskconfig->pszWindowTitle)
-        titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
-    else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
-    {
-        if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
-            titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
-    }
-    else
-        titleW = taskconfig->pszWindowTitle;
-    if (!titleW)
-        titleW = emptyW;
-    title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
-
-    size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
-    size += title_size;
-    size += 2; /* font size */
-
-    list_init(&desc.controls);
-    desc.taskconfig = taskconfig;
-    desc.control_count = 0;
+static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
+{
+    const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+    NONCLIENTMETRICSW ncm;
+    HDC hdc;
+    INT id;
 
     ncm.cbSize = sizeof(ncm);
     SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
-    desc.font = CreateFontIndirectW(&ncm.lfMessageFont);
-
-    hdc = GetDC(0);
-    SelectObject(hdc, desc.font);
-    desc.x_baseunit = GdiGetCharDimensions(hdc, NULL, &desc.y_baseunit);
-    ReleaseDC(0, hdc);
 
-    screen_width = taskdialog_get_reference_rect(&desc, &ref_rect);
+    memset(dialog_info, 0, sizeof(*dialog_info));
+    dialog_info->taskconfig = taskconfig;
+    dialog_info->hwnd = hwnd;
+    dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont);
 
-    desc.dialog_height = 0;
-    desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
-    desc.dialog_width = min(desc.dialog_width, screen_width);
-    desc.default_button = NULL;
+    hdc = GetDC(dialog_info->hwnd);
+    SelectObject(hdc, dialog_info->font);
+    dialog_info->m.x_baseunit = GdiGetCharDimensions(hdc, NULL, &dialog_info->m.y_baseunit);
+    ReleaseDC(dialog_info->hwnd, hdc);
 
-    size += taskdialog_add_main_instruction(&desc);
-    size += taskdialog_add_content(&desc);
-    size += taskdialog_add_buttons(&desc);
+    dialog_info->m.h_spacing = DIALOG_SPACING;
+    dialog_info->m.v_spacing = DIALOG_SPACING;
+    taskdialog_du_to_px(dialog_info, &dialog_info->m.h_spacing, &dialog_info->m.v_spacing);
 
-    template = Alloc(size);
-    if (!template)
+    if (taskconfig->dwFlags & TDF_CALLBACK_TIMER)
     {
-        taskdialog_clear_controls(&desc.controls);
-        DeleteObject(desc.font);
-        return NULL;
+        SetTimer(hwnd, ID_TIMER, DIALOG_TIMER_MS, NULL);
+        dialog_info->last_timer_tick = GetTickCount();
     }
 
-    template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
-    template->cdit = desc.control_count;
-    template->x = (ref_rect.left + ref_rect.right + desc.dialog_width) / 2;
-    template->y = (ref_rect.top + ref_rect.bottom + desc.dialog_height) / 2;
-    template->cx = desc.dialog_width;
-    template->cy = desc.dialog_height;
-
-    ptr = (char *)(template + 1);
-    ptr += 2; /* menu */
-    ptr += 2; /* class */
-    template_write_data(&ptr, titleW, title_size);
-    template_write_data(&ptr, &fontsize, sizeof(fontsize));
-
-    /* write control entries */
-    LIST_FOR_EACH_ENTRY_SAFE(control, control2, &desc.controls, struct taskdialog_control, entry)
-    {
-        ALIGN_POINTER(ptr, 3);
-
-        template_write_data(&ptr, control->template, control->template_size);
-
-        /* list item won't be needed later */
-        list_remove(&control->entry);
-        Free(control->template);
-        Free(control);
-    }
-
-    DeleteObject(desc.font);
-    return template;
+    taskdialog_add_main_icon(dialog_info);
+    taskdialog_add_main_instruction(dialog_info);
+    taskdialog_add_content(dialog_info);
+    taskdialog_add_expanded_info(dialog_info);
+    taskdialog_add_progress_bar(dialog_info);
+    taskdialog_add_radio_buttons(dialog_info);
+    taskdialog_add_command_links(dialog_info);
+    taskdialog_add_expando_button(dialog_info);
+    taskdialog_add_verification_box(dialog_info);
+    taskdialog_add_buttons(dialog_info);
+    taskdialog_add_footer_icon(dialog_info);
+    taskdialog_add_footer_text(dialog_info);
+
+    /* Set default button */
+    if (!dialog_info->default_button && dialog_info->command_links)
+        dialog_info->default_button = dialog_info->command_links[0];
+    if (!dialog_info->default_button) dialog_info->default_button = dialog_info->buttons[0];
+    SendMessageW(dialog_info->hwnd, WM_NEXTDLGCTL, (WPARAM)dialog_info->default_button, TRUE);
+    id = GetWindowLongW(dialog_info->default_button, GWLP_ID);
+    SendMessageW(dialog_info->hwnd, DM_SETDEFID, id, 0);
+
+    dialog_info->has_cancel =
+        (taskconfig->dwFlags & TDF_ALLOW_DIALOG_CANCELLATION)
+        || taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, IDCANCEL)
+        || taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, IDCANCEL);
+
+    if (!dialog_info->has_cancel) DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
+
+    taskdialog_layout(dialog_info);
 }
 
-static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
+static BOOL CALLBACK takdialog_destroy_control(HWND hwnd, LPARAM lParam)
 {
-    return dialog_info->callback ? dialog_info->callback(dialog_info->hwnd, notification, wparam, lparam,
-            dialog_info->callback_data) : S_OK;
+    DestroyWindow(hwnd);
+    return TRUE;
 }
 
-static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, WORD command_id)
+static void taskdialog_destroy(struct taskdialog_info *dialog_info)
 {
-    if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
-        EndDialog(dialog_info->hwnd, command_id);
+    EnumChildWindows(dialog_info->hwnd, takdialog_destroy_control, 0);
+
+    if (dialog_info->taskconfig->dwFlags & TDF_CALLBACK_TIMER) KillTimer(dialog_info->hwnd, ID_TIMER);
+    if (dialog_info->font) DeleteObject(dialog_info->font);
+    if (dialog_info->main_instruction_font) DeleteObject(dialog_info->main_instruction_font);
+    Free(dialog_info->buttons);
+    Free(dialog_info->radio_buttons);
+    Free(dialog_info->command_links);
+    Free(dialog_info->expanded_text);
+    Free(dialog_info->collapsed_text);
 }
 
 static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
     static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
     struct taskdialog_info *dialog_info;
+    LRESULT result;
 
     TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
 
@@ -541,32 +1216,155 @@ static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPAR
 
     switch (msg)
     {
+        case TDM_NAVIGATE_PAGE:
+            dialog_info->taskconfig = (const TASKDIALOGCONFIG *)lParam;
+            taskdialog_destroy(dialog_info);
+            taskdialog_init(dialog_info, hwnd);
+            taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
+            /* Default radio button click notification is sent before TDN_NAVIGATED */
+            taskdialog_check_default_radio_buttons(dialog_info);
+            taskdialog_notify(dialog_info, TDN_NAVIGATED, 0, 0);
+            break;
         case TDM_CLICK_BUTTON:
-            taskdialog_on_button_click(dialog_info, LOWORD(wParam));
+            taskdialog_click_button(dialog_info, wParam);
+            break;
+        case TDM_ENABLE_BUTTON:
+            taskdialog_enable_button(dialog_info, wParam, lParam);
+            break;
+        case TDM_SET_MARQUEE_PROGRESS_BAR:
+        {
+            BOOL marquee = wParam;
+            LONG style;
+            if(!dialog_info->progress_bar) break;
+            style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
+            style = marquee ? style | PBS_MARQUEE : style & (~PBS_MARQUEE);
+            SetWindowLongW(dialog_info->progress_bar, GWL_STYLE, style);
+            break;
+        }
+        case TDM_SET_PROGRESS_BAR_STATE:
+            result = SendMessageW(dialog_info->progress_bar, PBM_SETSTATE, wParam, 0);
+            SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
+            break;
+        case TDM_SET_PROGRESS_BAR_RANGE:
+            result = SendMessageW(dialog_info->progress_bar, PBM_SETRANGE, 0, lParam);
+            SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
+            break;
+        case TDM_SET_PROGRESS_BAR_POS:
+            result = 0;
+            if (dialog_info->progress_bar)
+            {
+                LONG style = GetWindowLongW(dialog_info->progress_bar, GWL_STYLE);
+                if (!(style & PBS_MARQUEE)) result = SendMessageW(dialog_info->progress_bar, PBM_SETPOS, wParam, 0);
+            }
+            SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, result);
+            break;
+        case TDM_SET_PROGRESS_BAR_MARQUEE:
+            SendMessageW(dialog_info->progress_bar, PBM_SETMARQUEE, wParam, lParam);
+            break;
+        case TDM_SET_ELEMENT_TEXT:
+            taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
+            taskdialog_layout(dialog_info);
+            break;
+        case TDM_UPDATE_ELEMENT_TEXT:
+            taskdialog_set_element_text(dialog_info, wParam, (const WCHAR *)lParam);
+            break;
+        case TDM_CLICK_RADIO_BUTTON:
+            taskdialog_click_radio_button(dialog_info, wParam);
+            break;
+        case TDM_ENABLE_RADIO_BUTTON:
+            taskdialog_enable_radio_button(dialog_info, wParam, lParam);
+            break;
+        case TDM_CLICK_VERIFICATION:
+        {
+            BOOL checked = (BOOL)wParam;
+            BOOL focused = (BOOL)lParam;
+            dialog_info->verification_checked = checked;
+            if (dialog_info->verification_box)
+            {
+                SendMessageW(dialog_info->verification_box, BM_SETCHECK, checked ? BST_CHECKED : BST_UNCHECKED, 0);
+                taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, checked, 0);
+                if (focused) SetFocus(dialog_info->verification_box);
+            }
+            break;
+        }
+        case TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE:
+            taskdialog_button_set_shield(dialog_info, wParam, lParam);
+            break;
+        case TDM_UPDATE_ICON:
+            taskdialog_set_icon(dialog_info, wParam, (HICON)lParam);
             break;
         case WM_INITDIALOG:
             dialog_info = (struct taskdialog_info *)lParam;
-            dialog_info->hwnd = hwnd;
-            SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
 
+            taskdialog_init(dialog_info, hwnd);
+
+            SetPropW(hwnd, taskdialog_info_propnameW, dialog_info);
             taskdialog_notify(dialog_info, TDN_DIALOG_CONSTRUCTED, 0, 0);
-            break;
-        case WM_SHOWWINDOW:
             taskdialog_notify(dialog_info, TDN_CREATED, 0, 0);
-            break;
+            /* Default radio button click notification sent after TDN_CREATED */
+            taskdialog_check_default_radio_buttons(dialog_info);
+            return FALSE;
         case WM_COMMAND:
             if (HIWORD(wParam) == BN_CLICKED)
             {
-                taskdialog_on_button_click(dialog_info, LOWORD(wParam));
-                return TRUE;
+                taskdialog_on_button_click(dialog_info, (HWND)lParam, LOWORD(wParam));
+                break;
             }
+            return FALSE;
+        case WM_HELP:
+            taskdialog_notify(dialog_info, TDN_HELP, 0, 0);
             break;
+        case WM_TIMER:
+            if (ID_TIMER == wParam)
+            {
+                DWORD elapsed = GetTickCount() - dialog_info->last_timer_tick;
+                if (taskdialog_notify(dialog_info, TDN_TIMER, elapsed, 0) == S_FALSE)
+                    dialog_info->last_timer_tick = GetTickCount();
+            }
+            break;
+        case WM_NOTIFY:
+        {
+            PNMLINK pnmLink = (PNMLINK)lParam;
+            HWND hwndFrom = pnmLink->hdr.hwndFrom;
+            if ((taskdialog_hyperlink_enabled(dialog_info))
+                && (hwndFrom == dialog_info->content || hwndFrom == dialog_info->expanded_info
+                    || hwndFrom == dialog_info->footer_text)
+                && (pnmLink->hdr.code == NM_CLICK || pnmLink->hdr.code == NM_RETURN))
+            {
+                taskdialog_notify(dialog_info, TDN_HYPERLINK_CLICKED, 0, (LPARAM)pnmLink->item.szUrl);
+                break;
+            }
+            return FALSE;
+        }
+        case WM_DRAWITEM:
+        {
+            LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
+            if (dis->hwndItem == dialog_info->expando_button)
+            {
+                taskdialog_draw_expando_control(dialog_info, dis);
+                SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE);
+                break;
+            }
+            return FALSE;
+        }
         case WM_DESTROY:
             taskdialog_notify(dialog_info, TDN_DESTROYED, 0, 0);
             RemovePropW(hwnd, taskdialog_info_propnameW);
+            taskdialog_destroy(dialog_info);
             break;
+        case WM_CLOSE:
+            if (dialog_info->has_cancel)
+            {
+                if(taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, IDCANCEL, 0) == S_OK)
+                    EndDialog(hwnd, IDCANCEL);
+                SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, 0);
+                break;
+            }
+            return FALSE;
+        default:
+            return FALSE;
     }
-    return FALSE;
+    return TRUE;
 }
 
 /***********************************************************************
@@ -584,8 +1382,7 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *butto
     if (!taskconfig || taskconfig->cbSize != sizeof(TASKDIALOGCONFIG))
         return E_INVALIDARG;
 
-    dialog_info.callback = taskconfig->pfCallback;
-    dialog_info.callback_data = taskconfig->lpCallbackData;
+    dialog_info.taskconfig = taskconfig;
 
     template = create_taskdialog_template(taskconfig);
     ret = (short)DialogBoxIndirectParamW(taskconfig->hInstance, template, taskconfig->hwndParent,
@@ -593,8 +1390,8 @@ HRESULT WINAPI TaskDialogIndirect(const TASKDIALOGCONFIG *taskconfig, int *butto
     Free(template);
 
     if (button) *button = ret;
-    if (radio_button) *radio_button = taskconfig->nDefaultButton;
-    if (verification_flag_checked) *verification_flag_checked = TRUE;
+    if (radio_button) *radio_button = dialog_info.selected_radio_id;
+    if (verification_flag_checked) *verification_flag_checked = dialog_info.verification_checked;
 
     return S_OK;
 }
index ec17033..d66c784 100644 (file)
@@ -211,13 +211,3 @@ LRESULT THEMING_CallOriginalClass (HWND wnd, UINT msg, WPARAM wParam, LPARAM lPa
     WNDPROC oldProc = originalProcs[subclass];
     return CallWindowProcW (oldProc, wnd, msg, wParam, lParam);
 }
-
-/***********************************************************************
- * THEMING_SetSubclassData
- *
- * Update the "refData" value of the subclassed window.
- */
-void THEMING_SetSubclassData (HWND wnd, ULONG_PTR refData)
-{
-    SetPropW (wnd, (LPCWSTR)MAKEINTATOM(atRefDataProp), (HANDLE)refData);
-}
index 441b7ff..844f4e4 100644 (file)
@@ -74,7 +74,6 @@
 #include "winreg.h"
 #include "wingdi.h"
 #include "winuser.h"
-#include "wine/unicode.h"
 #include "winnls.h"
 #include "commctrl.h"
 #include "comctl32.h"
@@ -786,7 +785,7 @@ static void TOOLBAR_DrawMasked(HIMAGELIST himl, int index, HDC hdc, INT x, INT y
 
 
 static UINT
-TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr)
+TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr, BOOL captured)
 {
     UINT retstate = 0;
 
@@ -794,7 +793,7 @@ TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr)
     retstate |= (btnPtr->fsState & TBSTATE_PRESSED) ? CDIS_SELECTED : 0;
     retstate |= (btnPtr->fsState & TBSTATE_ENABLED) ? 0 : CDIS_DISABLED;
     retstate |= (btnPtr->fsState & TBSTATE_MARKED ) ? CDIS_MARKED   : 0;
-    retstate |= (btnPtr->bHot                     ) ? CDIS_HOT      : 0;
+    retstate |= (btnPtr->bHot & !captured         ) ? CDIS_HOT      : 0;
     retstate |= ((btnPtr->fsState & (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) == (TBSTATE_ENABLED|TBSTATE_INDETERMINATE)) ? CDIS_INDETERMINATE : 0;
     /* NOTE: we don't set CDIS_GRAYED, CDIS_FOCUS, CDIS_DEFAULT */
     return retstate;
@@ -1104,7 +1103,7 @@ TOOLBAR_DrawButton (const TOOLBAR_INFO *infoPtr, TBUTTON_INFO *btnPtr, HDC hdc,
     tbcd.rcText.top = 0;
     tbcd.rcText.right = rcText.right - rc.left;
     tbcd.rcText.bottom = rcText.bottom - rc.top;
-    tbcd.nmcd.uItemState = TOOLBAR_TranslateState(btnPtr);
+    tbcd.nmcd.uItemState = TOOLBAR_TranslateState(btnPtr, infoPtr->bCaptured);
     tbcd.nmcd.hdc = hdc;
     tbcd.nmcd.rc = btnPtr->rect;
     tbcd.hbrMonoDither = COMCTL32_hPattern55AABrush;
@@ -1382,7 +1381,7 @@ TOOLBAR_MeasureString(const TOOLBAR_INFO *infoPtr, const TBUTTON_INFO *btnPtr,
 
        if(lpText != NULL) {
            /* first get size of all the text */
-           GetTextExtentPoint32W (hdc, lpText, strlenW (lpText), lpSize);
+           GetTextExtentPoint32W (hdc, lpText, lstrlenW (lpText), lpSize);
 
            /* feed above size into the rectangle for DrawText */
             SetRect(&myrect, 0, 0, lpSize->cx, lpSize->cy);
@@ -2629,7 +2628,7 @@ TOOLBAR_CustomizeDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
                     btnInfo->btn = nmtb.tbButton;
                     if (!(nmtb.tbButton.fsStyle & BTNS_SEP))
                     {
-                        if (lstrlenW(nmtb.pszText))
+                        if (*nmtb.pszText)
                             lstrcpyW(btnInfo->text, nmtb.pszText);
                         else if (nmtb.tbButton.iString >= 0 && 
                             nmtb.tbButton.iString < infoPtr->nNumStrings)
@@ -3151,7 +3150,7 @@ TOOLBAR_AddStringW (TOOLBAR_INFO *infoPtr, HINSTANCE hInstance, LPARAM lParam)
         delimiter = *szString;
         p = szString + 1;
 
-        while ((next_delim = strchrW(p, delimiter)) != NULL) {
+        while ((next_delim = wcschr(p, delimiter)) != NULL) {
             *next_delim = 0;
             if (next_delim + 1 >= szString + len)
             {
@@ -3175,7 +3174,7 @@ TOOLBAR_AddStringW (TOOLBAR_INFO *infoPtr, HINSTANCE hInstance, LPARAM lParam)
            return -1;
        TRACE("adding string(s) from array\n");
        while (*p) {
-            len = strlenW (p);
+            len = lstrlenW (p);
 
             TRACE("len=%d %s\n", len, debugstr_w(p));
             infoPtr->strings = ReAlloc(infoPtr->strings, sizeof(LPWSTR)*(infoPtr->nNumStrings+1));
@@ -3638,8 +3637,8 @@ TOOLBAR_GetButtonText (const TOOLBAR_INFO *infoPtr, INT Id, LPWSTR lpStr, BOOL i
     {
         if (lpText)
         {
-            ret = strlenW (lpText);
-            if (lpStr) strcpyW (lpStr, lpText);
+            ret = lstrlenW (lpText);
+            if (lpStr) lstrcpyW (lpStr, lpText);
         }
     }
     else
@@ -4078,7 +4077,7 @@ TOOLBAR_MapAccelerator (const TOOLBAR_INFO *infoPtr, WCHAR wAccel, UINT *pIDButt
         if (!(btnPtr->fsStyle & BTNS_NOPREFIX) &&
             !(btnPtr->fsState & TBSTATE_HIDDEN))
         {
-            int iLen = strlenW(wszAccel);
+            int iLen = lstrlenW(wszAccel);
             LPCWSTR lpszStr = TOOLBAR_GetText(infoPtr, btnPtr);
             
             if (!lpszStr)
@@ -4091,7 +4090,7 @@ TOOLBAR_MapAccelerator (const TOOLBAR_INFO *infoPtr, WCHAR wAccel, UINT *pIDButt
                     lpszStr += 2;
                     continue;
                 }
-                if (!strncmpiW(lpszStr, wszAccel, iLen))
+                if (!wcsnicmp(lpszStr, wszAccel, iLen))
                 {
                     *pIDButton = btnPtr->idCommand;
                     return TRUE;
@@ -5424,7 +5423,7 @@ TOOLBAR_GetStringW (const TOOLBAR_INFO *infoPtr, WPARAM wParam, LPWSTR str)
 
     if (iString < infoPtr->nNumStrings)
     {
-        len = min(len, strlenW(infoPtr->strings[iString]));
+        len = min(len, lstrlenW(infoPtr->strings[iString]));
         ret = (len+1)*sizeof(WCHAR);
         if (str)
         {
@@ -5987,7 +5986,7 @@ TOOLBAR_LButtonUp (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
 
             if (nButton == infoPtr->nButtonDrag)
             {
-                /* if the button is moved sightly left and we have a
+                /* if the button is moved slightly left and we have a
                  * separator there then remove it */
                 if (pt.x < (btnPtr->rect.left + (btnPtr->rect.right - btnPtr->rect.left)/2))
                 {
@@ -6028,7 +6027,10 @@ TOOLBAR_LButtonUp (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
 
         TOOLBAR_SendNotify(&hdr, infoPtr, TBN_TOOLBARCHANGE);
     }
-    else if (infoPtr->nButtonDown >= 0) {
+    else if (infoPtr->nButtonDown >= 0)
+    {
+        BOOL was_clicked = nHit == infoPtr->nButtonDown;
+
        btnPtr = &infoPtr->buttons[infoPtr->nButtonDown];
        btnPtr->fsState &= ~TBSTATE_PRESSED;
 
@@ -6070,7 +6072,7 @@ TOOLBAR_LButtonUp (TOOLBAR_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
        TOOLBAR_SendNotify ((NMHDR *) &nmtb, infoPtr,
                        TBN_ENDDRAG);
 
-       if (btnPtr->fsState & TBSTATE_ENABLED)
+       if (was_clicked && btnPtr->fsState & TBSTATE_ENABLED)
        {
            SendMessageW (infoPtr->hwndNotify, WM_COMMAND,
              MAKEWPARAM(infoPtr->buttons[nHit].idCommand, BN_CLICKED), (LPARAM)infoPtr->hwndSelf);
@@ -6426,6 +6428,9 @@ TOOLBAR_NCPaint (HWND hwnd, WPARAM wParam, LPARAM lParam)
 static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnmtdi)
 {
     int index = TOOLBAR_GetButtonIndex(infoPtr, lpnmtdi->hdr.idFrom, FALSE);
+    NMTTDISPINFOA nmtdi;
+    unsigned int len;
+    LRESULT ret;
 
     TRACE("button index = %d\n", index);
 
@@ -6439,7 +6444,6 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm
     {
         WCHAR wszBuffer[INFOTIPSIZE+1];
         NMTBGETINFOTIPW tbgit;
-        unsigned int len; /* in chars */
 
         wszBuffer[0] = '\0';
         wszBuffer[INFOTIPSIZE] = '\0';
@@ -6453,7 +6457,7 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm
 
         TRACE("TBN_GETINFOTIPW - got string %s\n", debugstr_w(tbgit.pszText));
 
-        len = strlenW(tbgit.pszText);
+        len = tbgit.pszText ? lstrlenW(tbgit.pszText) : 0;
         if (len > ARRAY_SIZE(lpnmtdi->szText) - 1)
         {
             /* need to allocate temporary buffer in infoPtr as there
@@ -6477,7 +6481,6 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm
     {
         CHAR szBuffer[INFOTIPSIZE+1];
         NMTBGETINFOTIPA tbgit;
-        unsigned int len; /* in chars */
 
         szBuffer[0] = '\0';
         szBuffer[INFOTIPSIZE] = '\0';
@@ -6518,7 +6521,7 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm
         !(infoPtr->buttons[index].fsStyle & BTNS_SHOWTEXT))
     {
         LPWSTR pszText = TOOLBAR_GetText(infoPtr, &infoPtr->buttons[index]);
-        unsigned int len = pszText ? strlenW(pszText) : 0;
+        len = pszText ? lstrlenW(pszText) : 0;
 
         TRACE("using button hidden text %s\n", debugstr_w(pszText));
 
@@ -6544,9 +6547,68 @@ static LRESULT TOOLBAR_TTGetDispInfo (TOOLBAR_INFO *infoPtr, NMTTDISPINFOW *lpnm
 
     TRACE("Sending tooltip notification to %p\n", infoPtr->hwndNotify);
 
-    /* last resort: send notification on to app */
-    /* FIXME: find out what is really used here */
-    return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmtdi->hdr.idFrom, (LPARAM)lpnmtdi);
+    /* Last resort, forward TTN_GETDISPINFO to the app:
+
+       - NFR_UNICODE gets TTN_GETDISPINFOW, and TTN_GETDISPINFOA if -W returned no text;
+       - NFR_ANSI gets only TTN_GETDISPINFOA.
+    */
+    if (infoPtr->bUnicode)
+    {
+        ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, lpnmtdi->hdr.idFrom, (LPARAM)lpnmtdi);
+
+        TRACE("TTN_GETDISPINFOW - got string %s\n", debugstr_w(lpnmtdi->lpszText));
+
+        if (IS_INTRESOURCE(lpnmtdi->lpszText))
+            return ret;
+
+        if (lpnmtdi->lpszText && *lpnmtdi->lpszText)
+            return ret;
+    }
+
+    nmtdi.hdr.hwndFrom = lpnmtdi->hdr.hwndFrom;
+    nmtdi.hdr.idFrom = lpnmtdi->hdr.idFrom;
+    nmtdi.hdr.code = TTN_GETDISPINFOA;
+    nmtdi.lpszText = nmtdi.szText;
+    nmtdi.szText[0] = 0;
+    nmtdi.hinst = lpnmtdi->hinst;
+    nmtdi.uFlags = lpnmtdi->uFlags;
+    nmtdi.lParam = lpnmtdi->lParam;
+
+    ret = SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, nmtdi.hdr.idFrom, (LPARAM)&nmtdi);
+
+    TRACE("TTN_GETDISPINFOA - got string %s\n", debugstr_a(nmtdi.lpszText));
+
+    lpnmtdi->hinst = nmtdi.hinst;
+    lpnmtdi->uFlags = nmtdi.uFlags;
+    lpnmtdi->lParam = nmtdi.lParam;
+
+    if (IS_INTRESOURCE(nmtdi.lpszText))
+    {
+        lpnmtdi->lpszText = (WCHAR *)nmtdi.lpszText;
+        return ret;
+    }
+
+    if (!nmtdi.lpszText || !*nmtdi.lpszText)
+        return ret;
+
+    len = MultiByteToWideChar(CP_ACP, 0, nmtdi.lpszText, -1, NULL, 0);
+    if (len > ARRAY_SIZE(lpnmtdi->szText))
+    {
+        infoPtr->pszTooltipText = Alloc(len * sizeof(WCHAR));
+        if (infoPtr->pszTooltipText)
+        {
+            MultiByteToWideChar(CP_ACP, 0, nmtdi.lpszText, -1, infoPtr->pszTooltipText, len);
+            lpnmtdi->lpszText = infoPtr->pszTooltipText;
+            return 0;
+        }
+    }
+    else
+    {
+        MultiByteToWideChar(CP_ACP, 0, nmtdi.lpszText, -1, lpnmtdi->lpszText, ARRAY_SIZE(nmtdi.szText));
+        return 0;
+    }
+
+    return ret;
 }
 
 
index a172938..8720902 100644 (file)
 
 #include <stdarg.h>
 #include <string.h>
+#include <stdlib.h>
 
 #include "windef.h"
 #include "winbase.h"
-#include "wine/unicode.h"
 #include "wingdi.h"
 #include "winuser.h"
 #include "winnls.h"
@@ -184,15 +184,6 @@ typedef struct
 static LRESULT CALLBACK
 TOOLTIPS_SubclassProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uId, DWORD_PTR dwRef);
 
-
-static inline BOOL TOOLTIPS_IsCallbackString(LPCWSTR str, BOOL isW)
-{
-    if (isW)
-      return str == LPSTR_TEXTCALLBACKW;
-    else
-      return (LPCSTR)str == LPSTR_TEXTCALLBACKA;
-}
-
 static inline UINT_PTR
 TOOLTIPS_GetTitleIconIndex(HICON hIcon)
 {
@@ -525,7 +516,7 @@ TOOLTIPS_GetTipText (const TOOLTIPS_INFO *infoPtr, INT nTool, WCHAR *buffer)
 
     if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & TTS_NOPREFIX)) {
         WCHAR *ptrW;
-        if ((ptrW = strchrW(buffer, '\t')))
+        if ((ptrW = wcschr(buffer, '\t')))
             *ptrW = 0;
     }
 
@@ -1036,7 +1027,7 @@ TOOLTIPS_CopyInfoT (const TOOLTIPS_INFO *infoPtr, INT index, TTTOOLINFOW *ti, BO
             toolPtr->lpszText == LPSTR_TEXTCALLBACKW)
             ti->lpszText = toolPtr->lpszText;
         else if (isW)
-            strcpyW (ti->lpszText, toolPtr->lpszText);
+            lstrcpyW (ti->lpszText, toolPtr->lpszText);
         else
             /* ANSI version, the buffer is maximum 80 bytes without null. */
             WideCharToMultiByte(CP_ACP, 0, toolPtr->lpszText, -1,
@@ -1155,7 +1146,7 @@ TOOLTIPS_AddToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW)
             toolPtr->lpszText = ti->lpszText;
         }
         else if (ti->lpszText) {
-            if (TOOLTIPS_IsCallbackString(ti->lpszText, isW)) {
+            if (ti->lpszText == LPSTR_TEXTCALLBACKW) {
                 TRACE("add CALLBACK\n");
                 toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
             }
@@ -1163,7 +1154,7 @@ TOOLTIPS_AddToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW)
                 INT len = lstrlenW (ti->lpszText);
                 TRACE("add text %s\n", debugstr_w(ti->lpszText));
                 toolPtr->lpszText =    Alloc ((len + 1)*sizeof(WCHAR));
-                strcpyW (toolPtr->lpszText, ti->lpszText);
+                lstrcpyW (toolPtr->lpszText, ti->lpszText);
             }
             else {
                 INT len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1, NULL, 0);
@@ -1213,6 +1204,45 @@ static void TOOLTIPS_ResetSubclass (const TTTOOL_INFO *toolPtr)
             TOOLTIPS_SubclassProc, 1, 0);
 }
 
+static void TOOLTIPS_FreeToolText(TTTOOL_INFO *toolPtr)
+{
+    if (toolPtr->lpszText)
+    {
+        if (!IS_INTRESOURCE(toolPtr->lpszText) && toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
+            Free(toolPtr->lpszText);
+        toolPtr->lpszText = NULL;
+    }
+}
+
+static void TOOLTIPS_SetToolText(TTTOOL_INFO *toolPtr, WCHAR *text, BOOL is_unicode)
+{
+    int len;
+
+    TOOLTIPS_FreeToolText (toolPtr);
+
+    if (IS_INTRESOURCE(text))
+        toolPtr->lpszText = text;
+    else if (text == LPSTR_TEXTCALLBACKW)
+        toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
+    else if (text)
+    {
+        if (is_unicode)
+        {
+            len = lstrlenW(text);
+            toolPtr->lpszText = Alloc ((len + 1) * sizeof(WCHAR));
+            if (toolPtr->lpszText)
+                lstrcpyW (toolPtr->lpszText, text);
+        }
+        else
+        {
+            len = MultiByteToWideChar(CP_ACP, 0, (char *)text, -1, NULL, 0);
+            toolPtr->lpszText = Alloc (len * sizeof(WCHAR));
+            if (toolPtr->lpszText)
+                MultiByteToWideChar(CP_ACP, 0, (char *)text, -1, toolPtr->lpszText, len);
+        }
+    }
+}
+
 static LRESULT
 TOOLTIPS_DelToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW)
 {
@@ -1236,14 +1266,8 @@ TOOLTIPS_DelToolT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW)
     /* make sure the tooltip has disappeared before deleting it */
     TOOLTIPS_Hide(infoPtr);
 
-    /* delete text string */
     toolPtr = &infoPtr->tools[nTool];
-    if (toolPtr->lpszText) {
-       if ( (toolPtr->lpszText != LPSTR_TEXTCALLBACKW) &&
-            !IS_INTRESOURCE(toolPtr->lpszText) )
-           Free (toolPtr->lpszText);
-    }
-
+    TOOLTIPS_FreeToolText (toolPtr);
     TOOLTIPS_ResetSubclass (toolPtr);
 
     /* delete tool from tool list */
@@ -1680,7 +1704,7 @@ TOOLTIPS_SetTitleT (TOOLTIPS_INFO *infoPtr, UINT_PTR uTitleIcon, LPCWSTR pszTitl
     {
         if (isW)
         {
-            size = (strlenW(pszTitle)+1)*sizeof(WCHAR);
+            size = (lstrlenW(pszTitle)+1)*sizeof(WCHAR);
             infoPtr->pszTitle = Alloc(size);
             if (!infoPtr->pszTitle)
                 return FALSE;
@@ -1708,7 +1732,6 @@ TOOLTIPS_SetTitleT (TOOLTIPS_INFO *infoPtr, UINT_PTR uTitleIcon, LPCWSTR pszTitl
     return TRUE;
 }
 
-
 static LRESULT
 TOOLTIPS_SetToolInfoT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW)
 {
@@ -1733,36 +1756,7 @@ TOOLTIPS_SetToolInfoT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW)
     toolPtr->rect   = ti->rect;
     toolPtr->hinst  = ti->hinst;
 
-    if (IS_INTRESOURCE(ti->lpszText)) {
-       TRACE("set string id %x\n", LOWORD(ti->lpszText));
-       toolPtr->lpszText = ti->lpszText;
-    }
-    else {
-       if (TOOLTIPS_IsCallbackString(ti->lpszText, isW))
-           toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
-       else {
-           if ( (toolPtr->lpszText) &&
-                !IS_INTRESOURCE(toolPtr->lpszText) ) {
-               if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
-                    Free (toolPtr->lpszText);
-               toolPtr->lpszText = NULL;
-           }
-           if (ti->lpszText) {
-               if (isW) {
-                   INT len = lstrlenW (ti->lpszText);
-                   toolPtr->lpszText = Alloc ((len+1)*sizeof(WCHAR));
-                   strcpyW (toolPtr->lpszText, ti->lpszText);
-               }
-               else {
-                   INT len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText,
-                                             -1, NULL, 0);
-                   toolPtr->lpszText = Alloc (len * sizeof(WCHAR));
-                   MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1,
-                                       toolPtr->lpszText, len);
-               }
-           }
-       }
-    }
+    TOOLTIPS_SetToolText (toolPtr, ti->lpszText, isW);
 
     if (ti->cbSize >= TTTOOLINFOW_V2_SIZE)
        toolPtr->lParam = ti->lParam;
@@ -1857,38 +1851,9 @@ TOOLTIPS_UpdateTipTextT (TOOLTIPS_INFO *infoPtr, const TTTOOLINFOW *ti, BOOL isW
 
     toolPtr = &infoPtr->tools[nTool];
 
-    /* copy tool text */
     toolPtr->hinst  = ti->hinst;
 
-    if (IS_INTRESOURCE(ti->lpszText)){
-       toolPtr->lpszText = ti->lpszText;
-    }
-    else if (ti->lpszText) {
-       if (TOOLTIPS_IsCallbackString(ti->lpszText, isW))
-           toolPtr->lpszText = LPSTR_TEXTCALLBACKW;
-       else {
-           if ( (toolPtr->lpszText)  &&
-                !IS_INTRESOURCE(toolPtr->lpszText) ) {
-               if( toolPtr->lpszText != LPSTR_TEXTCALLBACKW)
-                    Free (toolPtr->lpszText);
-               toolPtr->lpszText = NULL;
-           }
-           if (ti->lpszText) {
-               if (isW) {
-                   INT len = lstrlenW (ti->lpszText);
-                   toolPtr->lpszText = Alloc ((len+1)*sizeof(WCHAR));
-                   strcpyW (toolPtr->lpszText, ti->lpszText);
-               }
-               else {
-                   INT len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText,
-                                               -1, NULL, 0);
-                   toolPtr->lpszText = Alloc (len * sizeof(WCHAR));
-                   MultiByteToWideChar(CP_ACP, 0, (LPSTR)ti->lpszText, -1,
-                                       toolPtr->lpszText, len);
-               }
-           }
-       }
-    }
+    TOOLTIPS_SetToolText(toolPtr, ti->lpszText, isW);
 
     if(infoPtr->nCurrentTool == -1) return 0;
     /* force repaint */
@@ -1937,25 +1902,16 @@ TOOLTIPS_Destroy (TOOLTIPS_INFO *infoPtr)
     TTTOOL_INFO *toolPtr;
     UINT i;
 
-    /* free tools */
-    if (infoPtr->tools) {
-       for (i = 0; i < infoPtr->uNumTools; i++) {
-           toolPtr = &infoPtr->tools[i];
-           if (toolPtr->lpszText) {
-               if ( (toolPtr->lpszText != LPSTR_TEXTCALLBACKW) &&
-                    !IS_INTRESOURCE(toolPtr->lpszText) )
-               {
-                   Free (toolPtr->lpszText);
-                   toolPtr->lpszText = NULL;
-               }
-           }
-
-            TOOLTIPS_ResetSubclass (toolPtr);
-        }
+    for (i = 0; i < infoPtr->uNumTools; i++)
+    {
+        toolPtr = &infoPtr->tools[i];
 
-       Free (infoPtr->tools);
+        TOOLTIPS_FreeToolText (toolPtr);
+        TOOLTIPS_ResetSubclass (toolPtr);
     }
 
+    Free (infoPtr->tools);
+
     /* free title string */
     Free (infoPtr->pszTitle);
     /* free title icon if not a standard one */
@@ -2114,7 +2070,7 @@ TOOLTIPS_SetFont (TOOLTIPS_INFO *infoPtr, HFONT hFont, BOOL redraw)
 static inline LRESULT
 TOOLTIPS_GetTextLength(const TOOLTIPS_INFO *infoPtr)
 {
-    return strlenW(infoPtr->szTipText);
+    return lstrlenW(infoPtr->szTipText);
 }
 
 /******************************************************************
@@ -2136,7 +2092,7 @@ TOOLTIPS_OnWMGetText (const TOOLTIPS_INFO *infoPtr, WPARAM size, LPWSTR pszText)
     if(!size)
         return 0;
 
-    res = min(strlenW(infoPtr->szTipText)+1, size);
+    res = min(lstrlenW(infoPtr->szTipText)+1, size);
     memcpy(pszText, infoPtr->szTipText, res*sizeof(WCHAR));
     pszText[res-1] = '\0';
     return res-1;
index 233baed..502228d 100644 (file)
@@ -59,9 +59,8 @@ typedef struct
     HWND hwndBuddyLA;
     HWND hwndBuddyRB;
     INT  fLocation;
-    INT  flags;
+    DWORD flags;
     BOOL bUnicode;
-    BOOL bFocussed;
     RECT rcChannel;
     RECT rcSelection;
     RECT rcThumb;
@@ -78,15 +77,19 @@ typedef struct
 /* Used by TRACKBAR_Refresh to find out which parts of the control
    need to be recalculated */
 
-#define TB_THUMBPOSCHANGED      1
-#define TB_THUMBSIZECHANGED     2
-#define TB_THUMBCHANGED        (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
-#define TB_SELECTIONCHANGED     4
-#define TB_DRAG_MODE            8     /* we're dragging the slider */
-#define TB_AUTO_PAGE_LEFT      16
-#define TB_AUTO_PAGE_RIGHT     32
-#define TB_AUTO_PAGE           (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
-#define TB_THUMB_HOT            64    /* mouse hovers above thumb */
+#define TB_THUMBPOSCHANGED      0x00000001
+#define TB_THUMBSIZECHANGED     0x00000002
+#define TB_THUMBCHANGED        (TB_THUMBPOSCHANGED | TB_THUMBSIZECHANGED)
+#define TB_SELECTIONCHANGED     0x00000004
+#define TB_DRAG_MODE            0x00000008     /* we're dragging the slider */
+#define TB_AUTO_PAGE_LEFT       0x00000010
+#define TB_AUTO_PAGE_RIGHT      0x00000020
+#define TB_AUTO_PAGE           (TB_AUTO_PAGE_LEFT | TB_AUTO_PAGE_RIGHT)
+#define TB_THUMB_HOT            0x00000040    /* mouse hovers above thumb */
+
+/* Page was set with TBM_SETPAGESIZE */
+#define TB_USER_PAGE            0x00000080
+#define TB_IS_FOCUSED           0x00000100
 
 /* helper defines for TRACKBAR_DrawTic */
 #define TIC_EDGE                0x20
@@ -1000,7 +1003,7 @@ TRACKBAR_Refresh (TRACKBAR_INFO *infoPtr, HDC hdcDst)
     }
 
     /* draw focus rectangle */
-    if (infoPtr->bFocussed) {
+    if (infoPtr->flags & TB_IS_FOCUSED) {
        DrawFocusRect(hdc, &rcClient);
     }
 
@@ -1122,7 +1125,7 @@ TRACKBAR_GetNumTics (const TRACKBAR_INFO *infoPtr)
 }
 
 
-static int comp_tics (const void *ap, const void *bp)
+static int __cdecl comp_tics (const void *ap, const void *bp)
 {
     const DWORD a = *(const DWORD *)ap;
     const DWORD b = *(const DWORD *)bp;
@@ -1197,16 +1200,30 @@ TRACKBAR_SetLineSize (TRACKBAR_INFO *infoPtr, LONG lLineSize)
     return lTemp;
 }
 
+static void TRACKBAR_UpdatePageSize(TRACKBAR_INFO *infoPtr)
+{
+    if (infoPtr->flags & TB_USER_PAGE)
+        return;
+
+    infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
+    if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
+}
 
 static inline LONG
 TRACKBAR_SetPageSize (TRACKBAR_INFO *infoPtr, LONG lPageSize)
 {
     LONG lTemp = infoPtr->lPageSize;
 
-    if (lPageSize != -1)
-        infoPtr->lPageSize = lPageSize;
+    if (lPageSize == -1)
+    {
+        infoPtr->flags &= ~TB_USER_PAGE;
+        TRACKBAR_UpdatePageSize(infoPtr);
+    }
     else
-        infoPtr->lPageSize = TB_DEFAULTPAGESIZE;
+    {
+        infoPtr->flags |= TB_USER_PAGE;
+        infoPtr->lPageSize = lPageSize;
+    }
 
     return lTemp;
 }
@@ -1233,7 +1250,6 @@ TRACKBAR_SetPos (TRACKBAR_INFO *infoPtr, BOOL fPosition, LONG lPosition)
     return 0;
 }
 
-
 static inline LRESULT
 TRACKBAR_SetRange (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG range)
 {
@@ -1250,8 +1266,7 @@ TRACKBAR_SetRange (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG range)
     if (infoPtr->lPos > infoPtr->lRangeMax)
         infoPtr->lPos = infoPtr->lRangeMax;
 
-    infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
-    if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
+    TRACKBAR_UpdatePageSize(infoPtr);
 
     if (changed) {
         if (infoPtr->dwStyle & TBS_AUTOTICKS)
@@ -1277,8 +1292,7 @@ TRACKBAR_SetRangeMax (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG lMax)
         infoPtr->flags |= TB_THUMBPOSCHANGED;
     }
 
-    infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
-    if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
+    TRACKBAR_UpdatePageSize(infoPtr);
 
     if (changed && (infoPtr->dwStyle & TBS_AUTOTICKS))
         TRACKBAR_RecalculateTics (infoPtr);
@@ -1300,8 +1314,7 @@ TRACKBAR_SetRangeMin (TRACKBAR_INFO *infoPtr, BOOL redraw, LONG lMin)
         infoPtr->flags |= TB_THUMBPOSCHANGED;
     }
 
-    infoPtr->lPageSize = (infoPtr->lRangeMax - infoPtr->lRangeMin) / 5;
-    if (infoPtr->lPageSize == 0) infoPtr->lPageSize = 1;
+    TRACKBAR_UpdatePageSize(infoPtr);
 
     if (changed && (infoPtr->dwStyle & TBS_AUTOTICKS))
         TRACKBAR_RecalculateTics (infoPtr);
@@ -1459,25 +1472,31 @@ TRACKBAR_SetUnicodeFormat (TRACKBAR_INFO *infoPtr, BOOL fUnicode)
     return bTemp;
 }
 
+static int get_scaled_metric(const TRACKBAR_INFO *infoPtr, int value)
+{
+    return MulDiv(value, GetDpiForWindow(infoPtr->hwndSelf), 96);
+}
 
 static LRESULT
 TRACKBAR_InitializeThumb (TRACKBAR_INFO *infoPtr)
 {
+    int client_size;
     RECT rect;
-    int clientWidth, clientMetric;
 
-    /* initial thumb length */
-    clientMetric = (infoPtr->dwStyle & TBS_ENABLESELRANGE) ? 23 : 21;
-    GetClientRect(infoPtr->hwndSelf,&rect);
-    if (infoPtr->dwStyle & TBS_VERT) {
-       clientWidth = rect.right - rect.left;
-    } else {
-       clientWidth = rect.bottom - rect.top;
+    infoPtr->uThumbLen = get_scaled_metric(infoPtr, infoPtr->dwStyle & TBS_ENABLESELRANGE ? 23 : 21);
+
+    if (!(infoPtr->dwStyle & TBS_FIXEDLENGTH))
+    {
+        GetClientRect(infoPtr->hwndSelf, &rect);
+        if (infoPtr->dwStyle & TBS_VERT)
+            client_size = rect.right - rect.left;
+        else
+            client_size = rect.bottom - rect.top;
+
+        if (client_size < infoPtr->uThumbLen)
+            infoPtr->uThumbLen = client_size > get_scaled_metric(infoPtr, 9) ?
+                client_size - get_scaled_metric(infoPtr, 5) : get_scaled_metric(infoPtr, 4);
     }
-    if (clientWidth >= clientMetric)
-        infoPtr->uThumbLen = clientMetric;
-    else
-        infoPtr->uThumbLen = clientWidth > 9 ? clientWidth - 6 : 4;
 
     TRACKBAR_CalcChannel (infoPtr);
     TRACKBAR_UpdateThumb (infoPtr);
@@ -1564,7 +1583,7 @@ static LRESULT
 TRACKBAR_KillFocus (TRACKBAR_INFO *infoPtr)
 {
     TRACE("\n");
-    infoPtr->bFocussed = FALSE;
+    infoPtr->flags &= ~TB_IS_FOCUSED;
     TRACKBAR_InvalidateAll(infoPtr);
 
     return 0;
@@ -1651,7 +1670,7 @@ static LRESULT
 TRACKBAR_SetFocus (TRACKBAR_INFO *infoPtr)
 {
     TRACE("\n");
-    infoPtr->bFocussed = TRUE;
+    infoPtr->flags |= TB_IS_FOCUSED;
     TRACKBAR_InvalidateAll(infoPtr);
 
     return 0;
index 945084d..5bd60bf 100644 (file)
@@ -40,9 +40,6 @@
  *   Scroll (instead of repaint) as much as possible.
  */
 
-#include "config.h"
-#include "wine/port.h"
-
 #include <assert.h>
 #include <ctype.h>
 #include <stdarg.h>
@@ -61,7 +58,6 @@
 #include "comctl32.h"
 #include "uxtheme.h"
 #include "vssym32.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 #include "wine/exception.h"
 #include "wine/heap.h"
@@ -758,7 +754,7 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item,
        else {
            int len = max(lstrlenW(callback.item.pszText) + 1,
                          TEXT_CALLBACK_SIZE);
-           LPWSTR newText = heap_realloc(item->pszText, len);
+           LPWSTR newText = heap_realloc(item->pszText, len*sizeof(WCHAR));
 
            TRACE("returned wstr %s, len=%d\n",
                  debugstr_w(callback.item.pszText), len);
@@ -766,7 +762,7 @@ TREEVIEW_UpdateDispInfo(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item,
            if (newText)
            {
                item->pszText = newText;
-               strcpyW(item->pszText, callback.item.pszText);
+               lstrcpyW(item->pszText, callback.item.pszText);
                item->cchTextMax = len;
            }
            /* If realloc fails we have nothing to do, but keep original text */
@@ -901,7 +897,7 @@ TREEVIEW_ComputeTextWidth(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, HDC
        hOldFont = SelectObject(hdc, TREEVIEW_FontForItem(infoPtr, item));
     }
 
-    GetTextExtentPoint32W(hdc, item->pszText, strlenW(item->pszText), &sz);
+    GetTextExtentPoint32W(hdc, item->pszText, lstrlenW(item->pszText), &sz);
     item->textWidth = sz.cx;
 
     if (hDC == 0)
@@ -950,7 +946,7 @@ TREEVIEW_RecalculateVisibleOrder(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *start)
 
     infoPtr->maxVisibleOrder = order;
 
-    for (item = start; item != NULL;
+    for (item = infoPtr->root->firstChild; item != NULL;
         item = TREEVIEW_GetNextListItem(infoPtr, item))
     {
        TREEVIEW_ComputeItemRect(infoPtr, item);
@@ -1125,8 +1121,10 @@ TREEVIEW_DoSetItemT(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item,
     if (tvItem->mask & TVIF_TEXT)
     {
         item->textWidth = 0; /* force width recalculation */
-       if (tvItem->pszText != LPSTR_TEXTCALLBACKW && tvItem->pszText != NULL) /* covers != TEXTCALLBACKA too, and undocumented: pszText of NULL also means TEXTCALLBACK */
-       {
+
+        /* Covers != TEXTCALLBACKA too, and undocumented: pszText of NULL also means TEXTCALLBACK */
+        if (tvItem->pszText != LPSTR_TEXTCALLBACKW && tvItem->pszText != NULL)
+        {
             int len;
             LPWSTR newText;
             if (isW)
@@ -1134,12 +1132,14 @@ TREEVIEW_DoSetItemT(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item,
             else
                 len = MultiByteToWideChar(CP_ACP, 0, (LPSTR)tvItem->pszText, -1, NULL, 0);
 
-            newText  = heap_realloc(item->pszText, len * sizeof(WCHAR));
+            /* Allocate new block to make pointer comparison in item_changed() work. */
+            newText = heap_alloc(len * sizeof(WCHAR));
 
             if (newText == NULL) return FALSE;
 
             callbackClear |= TVIF_TEXT;
 
+            heap_free(item->pszText);
             item->pszText = newText;
             item->cchTextMax = len;
             if (isW)
@@ -2199,7 +2199,7 @@ TREEVIEW_SetItemT(TREEVIEW_INFO *infoPtr, const TVITEMEXW *tvItem, BOOL isW)
     if (!TREEVIEW_ValidItem(infoPtr, item))
        return FALSE;
 
-    /* store the original item values */
+    /* Store the original item values. Text buffer will be freed in TREEVIEW_DoSetItemT() below. */
     originalItem = *item;
 
     if (!TREEVIEW_DoSetItemT(infoPtr, item, tvItem, isW))
@@ -2653,7 +2653,9 @@ TREEVIEW_DrawItem(const TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
 
             ImageList_DrawEx(infoPtr->himlNormal, imageIndex, hdc,
                          item->imageOffset, centery - infoPtr->normalImageHeight / 2,
-                         0, 0, infoPtr->clrBk, item->state & TVIS_CUT ? GETBKCOLOR(infoPtr->clrBk) : CLR_DEFAULT,
+                         0, 0,
+                         TREEVIEW_IsFullRowSelect(infoPtr) ? nmcdhdr.clrTextBk : infoPtr->clrBk,
+                         item->state & TVIS_CUT ? GETBKCOLOR(infoPtr->clrBk) : CLR_DEFAULT,
                          style);
        }
     }
@@ -2681,7 +2683,7 @@ TREEVIEW_DrawItem(const TREEVIEW_INFO *infoPtr, HDC hdc, TREEVIEW_ITEM *item)
                   debugstr_w(item->pszText), wine_dbgstr_rect(&rcText));
 
            /* Draw it */
-           GetTextExtentPoint32W(hdc, item->pszText, strlenW(item->pszText), &sz);
+           GetTextExtentPoint32W(hdc, item->pszText, lstrlenW(item->pszText), &sz);
 
            align = SetTextAlign(hdc, TA_LEFT | TA_TOP);
            ExtTextOutW(hdc, rcText.left + 2, (rcText.top + rcText.bottom - sz.cy) / 2,
@@ -3859,7 +3861,7 @@ TREEVIEW_Command(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
                hOldFont = SelectObject(hdc, hFont);
            }
 
-           if (GetTextExtentPoint32W(hdc, buffer, strlenW(buffer), &sz))
+           if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
            {
                TEXTMETRICW textMetric;
 
@@ -3939,7 +3941,7 @@ TREEVIEW_EditLabel(TREEVIEW_INFO *infoPtr, HTREEITEM hItem)
 
     /* Get string length in pixels */
     if (hItem->pszText)
-        GetTextExtentPoint32W(hdc, hItem->pszText, strlenW(hItem->pszText),
+        GetTextExtentPoint32W(hdc, hItem->pszText, lstrlenW(hItem->pszText),
                         &sz);
     else
         GetTextExtentPoint32A(hdc, "", 0, &sz);
@@ -3985,6 +3987,7 @@ TREEVIEW_EditLabel(TREEVIEW_INFO *infoPtr, HTREEITEM hItem)
     infoPtr->wpEditOrig = (WNDPROC)SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC,
                                                  (DWORD_PTR)
                                                  TREEVIEW_Edit_SubclassProc);
+    SendMessageW(hwndEdit, EM_SETLIMITTEXT, MAX_PATH - 1, 0);
     if (hItem->pszText)
         SetWindowTextW(hwndEdit, hItem->pszText);
 
@@ -4010,8 +4013,8 @@ TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
     TREEVIEW_ITEM *editedItem = infoPtr->editItem;
     NMTVDISPINFOW tvdi;
     BOOL bCommit;
-    WCHAR tmpText[1024] = { '\0' };
-    WCHAR *newText = tmpText;
+    WCHAR tmpText[MAX_PATH] = { '\0' };
+    WCHAR *newText;
     int iLength = 0;
 
     if (!IsWindow(infoPtr->hwndEdit)) return FALSE;
@@ -4024,18 +4027,13 @@ TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
     if (!bCancel)
     {
         if (!infoPtr->bNtfUnicode)
-            iLength = GetWindowTextA(infoPtr->hwndEdit, (LPSTR)tmpText, 1023);
+            iLength = GetWindowTextA(infoPtr->hwndEdit, (LPSTR)tmpText, ARRAY_SIZE(tmpText));
         else
-            iLength = GetWindowTextW(infoPtr->hwndEdit, tmpText, 1023);
-
-       if (iLength >= 1023)
-       {
-           ERR("Insufficient space to retrieve new item label\n");
-       }
+            iLength = GetWindowTextW(infoPtr->hwndEdit, tmpText, ARRAY_SIZE(tmpText));
 
         tvdi.item.mask = TVIF_TEXT;
        tvdi.item.pszText = tmpText;
-       tvdi.item.cchTextMax = iLength + 1;
+       tvdi.item.cchTextMax = ARRAY_SIZE(tmpText);
     }
     else
     {
@@ -4049,13 +4047,15 @@ TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
     {
         if (!infoPtr->bNtfUnicode)
         {
-            DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, NULL, 0 );
+            DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)tvdi.item.pszText, -1, NULL, 0 );
             newText = heap_alloc(len * sizeof(WCHAR));
-            MultiByteToWideChar( CP_ACP, 0, (LPSTR)tmpText, -1, newText, len );
+            MultiByteToWideChar( CP_ACP, 0, (LPSTR)tvdi.item.pszText, -1, newText, len );
             iLength = len - 1;
         }
+        else
+            newText = tvdi.item.pszText;
 
-        if (strcmpW(newText, editedItem->pszText) != 0)
+        if (lstrcmpW(newText, editedItem->pszText) != 0)
         {
             WCHAR *ptr = heap_realloc(editedItem->pszText, sizeof(WCHAR)*(iLength + 1));
             if (ptr == NULL)
@@ -4071,7 +4071,7 @@ TREEVIEW_EndEditLabelNow(TREEVIEW_INFO *infoPtr, BOOL bCancel)
             {
                 editedItem->pszText = ptr;
                 editedItem->cchTextMax = iLength + 1;
-                strcpyW(editedItem->pszText, newText);
+                lstrcpyW(editedItem->pszText, newText);
                 TREEVIEW_ComputeTextWidth(infoPtr, editedItem, 0);
             }
         }
@@ -4431,7 +4431,7 @@ TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, LPARAM lParam)
     hOldFont = SelectObject(hdc, infoPtr->hFont);
 
     if (dragItem->pszText)
-        GetTextExtentPoint32W(hdc, dragItem->pszText, strlenW(dragItem->pszText),
+        GetTextExtentPoint32W(hdc, dragItem->pszText, lstrlenW(dragItem->pszText),
                          &size);
     else
         GetTextExtentPoint32A(hdc, "", 0, &size);
@@ -4459,7 +4459,7 @@ TREEVIEW_CreateDragImage(TREEVIEW_INFO *infoPtr, LPARAM lParam)
     SetRect(&rc, cx, 0, size.cx, size.cy);
 
     if (dragItem->pszText)
-        DrawTextW(hdc, dragItem->pszText, strlenW(dragItem->pszText), &rc,
+        DrawTextW(hdc, dragItem->pszText, lstrlenW(dragItem->pszText), &rc,
                   DT_LEFT);
 
     SelectObject(hdc, hOldFont);
@@ -4708,16 +4708,16 @@ static INT TREEVIEW_ProcessLetterKeys(TREEVIEW_INFO *infoPtr, WPARAM charCode, L
         item.mask = TVIF_TEXT;
         item.hItem = idx;
         item.pszText = buffer;
-        item.cchTextMax = sizeof(buffer);
+        item.cchTextMax = ARRAY_SIZE(buffer);
         TREEVIEW_GetItemT( infoPtr, &item, TRUE );
 
         /* check for a match */
-        if (strncmpiW(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
+        if (wcsnicmp(item.pszText,infoPtr->szSearchParam,infoPtr->nSearchParamLength) == 0) {
             nItem=idx;
             break;
         } else if ( (charCode != 0) && (nItem == NULL) &&
                     (nItem != infoPtr->selectedItem) &&
-                    (strncmpiW(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
+                    (wcsnicmp(item.pszText,infoPtr->szSearchParam,1) == 0) ) {
             /* This would work but we must keep looking for a longer match */
             nItem=idx;
         }
@@ -5045,7 +5045,7 @@ static LRESULT
 TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
 {
     short wheelDelta;
-    UINT pulScrollLines = 3;
+    INT pulScrollLines = 3;
 
     if (wParam & (MK_SHIFT | MK_CONTROL))
         return DefWindowProcW(infoPtr->hwnd, WM_MOUSEWHEEL, wParam, lParam);
@@ -5069,8 +5069,8 @@ TREEVIEW_MouseWheel(TREEVIEW_INFO *infoPtr, WPARAM wParam, LPARAM lParam)
        int maxDy;
        int lineScroll;
 
-       lineScroll = pulScrollLines * (float)infoPtr->wheelRemainder / WHEEL_DELTA;
-       infoPtr->wheelRemainder -= WHEEL_DELTA * lineScroll / (int)pulScrollLines;
+       lineScroll = pulScrollLines * infoPtr->wheelRemainder / WHEEL_DELTA;
+       infoPtr->wheelRemainder -= WHEEL_DELTA * lineScroll / pulScrollLines;
 
        newDy = infoPtr->firstVisible->visibleOrder - lineScroll;
        maxDy = infoPtr->maxVisibleOrder;
index 6b328ff..75dcade 100644 (file)
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 #include <stdarg.h>
@@ -33,7 +34,6 @@
 #include "uxtheme.h"
 #include "vssym32.h"
 #include "wine/heap.h"
-#include "wine/unicode.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(updown);
@@ -170,13 +170,16 @@ static BOOL UPDOWN_HasBuddyBorder(const UPDOWN_INFO *infoPtr)
  * rect     - will hold the rectangle
  * arrow    - FLAG_INCR to get the "increment" rect (up or right)
  *            FLAG_DECR to get the "decrement" rect (down or left)
- *            If both flags are present, the envelope is returned.
  */
-static void UPDOWN_GetArrowRect (const UPDOWN_INFO* infoPtr, RECT *rect, int arrow)
+static void UPDOWN_GetArrowRect (const UPDOWN_INFO* infoPtr, RECT *rect, unsigned int arrow)
 {
     HTHEME theme = GetWindowTheme (infoPtr->Self);
     const int border = theme ? DEFAULT_BUDDYBORDER_THEMED : DEFAULT_BUDDYBORDER;
     const int spacer = theme ? DEFAULT_BUDDYSPACER_THEMED : DEFAULT_BUDDYSPACER;
+    int size;
+
+    assert(arrow && (arrow & (FLAG_INCR | FLAG_DECR)) != (FLAG_INCR | FLAG_DECR));
+
     GetClientRect (infoPtr->Self, rect);
 
     /*
@@ -200,21 +203,20 @@ static void UPDOWN_GetArrowRect (const UPDOWN_INFO* infoPtr, RECT *rect, int arr
 
     /*
      * We're calculating the midpoint to figure-out where the
-     * separation between the buttons will lay. We make sure that we
-     * round the uneven numbers by adding 1.
+     * separation between the buttons will lay.
      */
     if (infoPtr->dwStyle & UDS_HORZ) {
-        int len = rect->right - rect->left + 1; /* compute the width */
+        size = (rect->right - rect->left) / 2;
         if (arrow & FLAG_INCR)
-            rect->left = rect->left + len/2;
-        if (arrow & FLAG_DECR)
-            rect->right =  rect->left + len/2 - (theme ? 0 : 1);
+            rect->left = rect->right - size;
+        else if (arrow & FLAG_DECR)
+            rect->right = rect->left + size;
     } else {
-        int len = rect->bottom - rect->top + 1; /* compute the height */
+        size = (rect->bottom - rect->top) / 2;
         if (arrow & FLAG_INCR)
-            rect->bottom =  rect->top + len/2 - (theme ? 0 : 1);
-        if (arrow & FLAG_DECR)
-            rect->top =  rect->top + len/2;
+            rect->bottom = rect->top + size;
+        else if (arrow & FLAG_DECR)
+            rect->top = rect->bottom - size;
     }
 }
 
@@ -285,7 +287,7 @@ static BOOL UPDOWN_GetBuddyInt (UPDOWN_INFO *infoPtr)
         *dst = 0;
 
         /* try to convert the number and validate it */
-        newVal = strtolW(txt, &src, infoPtr->Base);
+        newVal = wcstol(txt, &src, infoPtr->Base);
         if(*src || !UPDOWN_InBounds (infoPtr, newVal)) return FALSE;
     }
 
index 2b34250..e6797f0 100644 (file)
@@ -57,7 +57,6 @@ dll/win32/browseui            # Out of sync
 dll/win32/cabinet             # Synced to WineStaging-4.18
 dll/win32/clusapi             # Synced to WineStaging-3.3
 dll/win32/comcat              # Synced to WineStaging-3.3
-dll/win32/comctl32            # Synced to WineStaging-3.3
 dll/win32/comdlg32            # Synced to WineStaging-4.18
 dll/win32/compstui            # Synced to WineStaging-4.18
 dll/win32/credui              # Synced to WineStaging-4.18
@@ -232,6 +231,11 @@ dll/win32/xinput9_1_0         # Synced to WineStaging-2.9
 dll/win32/xmllite             # Synced to WineStaging-4.18
 dll/win32/xolehlp             # Synced to WineStaging-3.21
 
+comctl32 -
+  dll/win32/comctl32/button.c              # Forked at Wine-3.3
+  dll/win32/comctl32/datetime.c            # Synced to Wine-6.0
+  dll/win32/comctl32/*                     # Synced to Wine-5.0
+
 dll/cpl/inetcpl               # Synced to WineStaging-4.18
 
 win32ss/printing/monitors/localmon/ui/  # Synced to WineStaging-4.18 (known there as /dll/win32/localui)
index 923d826..d8fe9d6 100644 (file)
 #include "v6util.h"
 #include "msg.h"
 
+#ifdef __REACTOS__
+#define WM_CTLCOLOR 0x0019
+#endif
+
 #define EDITBOX_SEQ_INDEX  0
 #define NUM_MSG_SEQUENCES  1
 
@@ -46,6 +50,8 @@ static HWND hComboExParentWnd, hMainWnd;
 static HINSTANCE hMainHinst;
 static const char ComboExTestClass[] = "ComboExTestClass";
 
+static HBRUSH brush_red;
+
 static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
 
 #define MAX_CHARS 100
@@ -507,6 +513,8 @@ static BOOL init(void)
     wc.lpfnWndProc = ComboExTestWndProc;
     RegisterClassA(&wc);
 
+    brush_red = CreateSolidBrush(RGB(255, 0, 0));
+
     hMainWnd = CreateWindowA(WC_STATICA, "Test", WS_OVERLAPPEDWINDOW, 10, 10, 300, 300, NULL, NULL, NULL, 0);
     ShowWindow(hMainWnd, SW_SHOW);
 
@@ -533,6 +541,7 @@ static void cleanup(void)
     UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
 
     DestroyWindow(hMainWnd);
+    DeleteObject(brush_red);
 }
 
 static void test_comboex_subclass(void)
@@ -609,7 +618,7 @@ static HWND create_combobox(DWORD style)
     return CreateWindowA(WC_COMBOBOXA, "Combo", WS_VISIBLE|WS_CHILD|style, 5, 5, 100, 100, hMainWnd, (HMENU)COMBO_ID, NULL, 0);
 }
 
-static int font_height(HFONT hFont)
+static int get_font_height(HFONT hFont)
 {
     TEXTMETRICA tm;
     HFONT hFontOld;
@@ -627,11 +636,12 @@ static int font_height(HFONT hFont)
 static void test_combo_setitemheight(DWORD style)
 {
     HWND hCombo = create_combobox(style);
+    int i, font_height, height;
+    HFONT hFont;
     RECT r;
-    int i;
 
     GetClientRect(hCombo, &r);
-    expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8);
+    expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8);
     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
     MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
     todo_wine expect_rect(r, 5, 5, 105, 105);
@@ -644,6 +654,22 @@ static void test_combo_setitemheight(DWORD style)
     }
 
     DestroyWindow(hCombo);
+
+    /* Set item height below text height, force resize. */
+    hCombo = create_combobox(style);
+
+    hFont = (HFONT)SendMessageA(hCombo, WM_GETFONT, 0, 0);
+    font_height = get_font_height(hFont);
+    SendMessageA(hCombo, CB_SETITEMHEIGHT, -1, font_height / 2);
+    height = SendMessageA(hCombo, CB_GETITEMHEIGHT, -1, 0);
+todo_wine
+    ok(height == font_height / 2, "Unexpected item height %d, expected %d.\n", height, font_height / 2);
+
+    SetWindowPos(hCombo, NULL, 10, 10, 150, 5 * font_height, SWP_SHOWWINDOW);
+    height = SendMessageA(hCombo, CB_GETITEMHEIGHT, -1, 0);
+    ok(height > font_height, "Unexpected item height %d, font height %d.\n", height, font_height);
+
+    DestroyWindow(hCombo);
 }
 
 static void test_combo_setfont(DWORD style)
@@ -658,7 +684,7 @@ static void test_combo_setfont(DWORD style)
     hFont2 = CreateFontA(8, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
 
     GetClientRect(hCombo, &r);
-    expect_rect(r, 0, 0, 100, font_height(GetStockObject(SYSTEM_FONT)) + 8);
+    expect_rect(r, 0, 0, 100, get_font_height(GetStockObject(SYSTEM_FONT)) + 8);
     SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
     MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
     todo_wine expect_rect(r, 5, 5, 105, 105);
@@ -667,39 +693,39 @@ static void test_combo_setfont(DWORD style)
        of the window when it was created.  The size of the calculated
        dropped area changes only by how much the selection area
        changes, not by how much the list area changes.  */
-    if (font_height(hFont1) == 10 && font_height(hFont2) == 8)
+    if (get_font_height(hFont1) == 10 && get_font_height(hFont2) == 8)
     {
         SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
         GetClientRect(hCombo, &r);
         expect_rect(r, 0, 0, 100, 18);
         SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
-        todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1)));
+        todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1)));
 
         SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont2, FALSE);
         GetClientRect(hCombo, &r);
         expect_rect(r, 0, 0, 100, 16);
         SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
-        todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont2)));
+        todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont2)));
 
         SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont1, FALSE);
         GetClientRect(hCombo, &r);
         expect_rect(r, 0, 0, 100, 18);
         SendMessageA(hCombo, CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&r);
         MapWindowPoints(HWND_DESKTOP, hMainWnd, (LPPOINT)&r, 2);
-        todo_wine expect_rect(r, 5, 5, 105, 105 - (font_height(GetStockObject(SYSTEM_FONT)) - font_height(hFont1)));
+        todo_wine expect_rect(r, 5, 5, 105, 105 - (get_font_height(GetStockObject(SYSTEM_FONT)) - get_font_height(hFont1)));
     }
     else
     {
         ok(0, "Expected Marlett font heights 10/8, got %d/%d\n",
-           font_height(hFont1), font_height(hFont2));
+           get_font_height(hFont1), get_font_height(hFont2));
     }
 
     for (i = 1; i < 30; i++)
     {
         HFONT hFont = CreateFontA(i, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, SYMBOL_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, "Marlett");
-        int height = font_height(hFont);
+        int height = get_font_height(hFont);
 
         SendMessageA(hCombo, WM_SETFONT, (WPARAM)hFont, FALSE);
         GetClientRect(hCombo, &r);
@@ -717,6 +743,7 @@ static LRESULT (CALLBACK *old_parent_proc)(HWND hwnd, UINT msg, WPARAM wparam, L
 static LPCSTR expected_edit_text;
 static LPCSTR expected_list_text;
 static BOOL selchange_fired;
+static HWND lparam_for_WM_CTLCOLOR;
 
 static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
 {
@@ -748,6 +775,20 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPAR
             break;
         }
         break;
+    case WM_CTLCOLOR:
+    case WM_CTLCOLORMSGBOX:
+    case WM_CTLCOLOREDIT:
+    case WM_CTLCOLORLISTBOX:
+    case WM_CTLCOLORBTN:
+    case WM_CTLCOLORDLG:
+    case WM_CTLCOLORSCROLLBAR:
+    case WM_CTLCOLORSTATIC:
+        if (lparam_for_WM_CTLCOLOR)
+        {
+            ok(lparam_for_WM_CTLCOLOR == (HWND)lparam, "Expected %p, got %p\n", lparam_for_WM_CTLCOLOR, (HWND)lparam);
+            return (LRESULT) brush_red;
+        }
+        break;
     }
 
     return CallWindowProcA(old_parent_proc, hwnd, msg, wparam, lparam);
@@ -1254,6 +1295,80 @@ static void test_combo_dropdown_size(DWORD style)
     }
 }
 
+static void test_combo_ctlcolor(void)
+{
+    static const int messages[] =
+    {
+        WM_CTLCOLOR,
+        WM_CTLCOLORMSGBOX,
+        WM_CTLCOLOREDIT,
+        WM_CTLCOLORLISTBOX,
+        WM_CTLCOLORBTN,
+        WM_CTLCOLORDLG,
+        WM_CTLCOLORSCROLLBAR,
+        WM_CTLCOLORSTATIC,
+    };
+
+    HBRUSH brush, global_brush;
+    COMBOBOXINFO info;
+    unsigned int i;
+    HWND combo;
+
+    combo = create_combobox(CBS_DROPDOWN);
+    ok(!!combo, "Failed to create combo window.\n");
+
+    old_parent_proc = (void *)SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)parent_wnd_proc);
+
+    get_combobox_info(combo, &info);
+
+    lparam_for_WM_CTLCOLOR = info.hwndItem;
+
+    /* Parent returns valid brush handle. */
+    for (i = 0; i < ARRAY_SIZE(messages); ++i)
+    {
+        brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
+        ok(brush == brush_red, "%u: unexpected brush %p, expected got %p.\n", i, brush, brush_red);
+    }
+
+    /* Parent returns NULL brush. */
+    global_brush = brush_red;
+    brush_red = NULL;
+
+    for (i = 0; i < ARRAY_SIZE(messages); ++i)
+    {
+        brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
+        ok(!brush, "%u: unexpected brush %p.\n", i, brush);
+    }
+
+    brush_red = global_brush;
+
+    lparam_for_WM_CTLCOLOR = 0;
+
+    /* Parent does default processing. */
+    for (i = 0; i < ARRAY_SIZE(messages); ++i)
+    {
+        brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
+        ok(!!brush && brush != brush_red, "%u: unexpected brush %p.\n", i, brush);
+    }
+
+    SetWindowLongPtrA(hMainWnd, GWLP_WNDPROC, (ULONG_PTR)old_parent_proc);
+    DestroyWindow(combo);
+
+    /* Combo without a parent. */
+    combo = CreateWindowA(WC_COMBOBOXA, "Combo", CBS_DROPDOWN, 5, 5, 100, 100, NULL, NULL, NULL, 0);
+    ok(!!combo, "Failed to create combo window.\n");
+
+    get_combobox_info(combo, &info);
+
+    for (i = 0; i < ARRAY_SIZE(messages); ++i)
+    {
+        brush = (HBRUSH)SendMessageA(combo, messages[i], 0, (LPARAM)info.hwndItem);
+        ok(!brush, "%u: unexpected brush %p.\n", i, brush);
+    }
+
+    DestroyWindow(combo);
+}
+
 START_TEST(combo)
 {
     ULONG_PTR ctx_cookie;
@@ -1297,6 +1412,7 @@ START_TEST(combo)
     test_combo_listbox_styles(CBS_DROPDOWNLIST);
     test_combo_dropdown_size(0);
     test_combo_dropdown_size(CBS_NOINTEGRALHEIGHT);
+    test_combo_ctlcolor();
 
     cleanup();
     unload_v6_module(ctx_cookie, hCtx);
index 6013caf..cec6025 100644 (file)
@@ -1189,6 +1189,8 @@ static void test_char_from_pos(void)
 {
     int lo, hi, mid, ret, i;
     HWND hwEdit;
+    HDC dc;
+    SIZE size;
 
     hwEdit = create_editcontrol(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
     SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"aa");
@@ -1321,6 +1323,24 @@ static void test_char_from_pos(void)
     ret = SendMessageA(hwEdit, EM_POSFROMCHAR, 2, 0);
     ok(-1 == ret, "expected -1 got %d\n", ret);
     DestroyWindow(hwEdit);
+
+    /* Scrolled to the right with partially visible line, position on next line. */
+    hwEdit = create_editcontrol(ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
+
+    dc = GetDC(hwEdit);
+    GetTextExtentPoint32A(dc, "w", 1, &size);
+    ReleaseDC(hwEdit, dc);
+
+    SetWindowPos(hwEdit, NULL, 0, 0, size.cx * 15, size.cy * 5, SWP_NOMOVE | SWP_NOZORDER);
+    SendMessageA(hwEdit, WM_SETTEXT, 0, (LPARAM)"wwwwwwwwwwwwwwwwwwww\r\n\r\n");
+    SendMessageA(hwEdit, EM_SETSEL, 40, 40);
+
+    lo = (short)SendMessageA(hwEdit, EM_POSFROMCHAR, 22, 0);
+    ret = (short)SendMessageA(hwEdit, EM_POSFROMCHAR, 20, 0);
+    ret -= 20 * size.cx; /* Calculate expected position, 20 characters back. */
+    ok(ret == lo, "Unexpected position %d vs %d.\n", lo, ret);
+
+    DestroyWindow(hwEdit);
 }
 
 /* Test if creating edit control without ES_AUTOHSCROLL and ES_AUTOVSCROLL
@@ -3240,18 +3260,17 @@ static void test_cue_banner(void)
     static WCHAR getcuetestW[5] = {'T',0};
     static const WCHAR testcmp1W[] = {'T','e','s','t',0};
     static const WCHAR testcmp2W[] = {'T','e','s',0};
-    static const WCHAR emptyW[] = {0};
 
     hwnd_edit = create_editcontrolW(ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0);
 
     ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5);
-    if (lstrcmpW(getcuetestW, emptyW) != 0)
+    if (getcuetestW[0])
     {
         win_skip("skipping for Win XP and 2003 Server.\n");
         DestroyWindow(hwnd_edit);
         return;
     }
-    ok(lstrcmpW(getcuetestW, emptyW) == 0, "First char is %c\n", getcuetestW[0]);
+    ok(!getcuetestW[0], "First char is %c\n", getcuetestW[0]);
     ok(ret == FALSE, "EM_GETCUEBANNER should have returned FALSE.\n");
 
     lstrcpyW(getcuetestW, testcmp1W);
@@ -3279,12 +3298,12 @@ static void test_cue_banner(void)
     ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n");
     ok(lstrcmpW(getcuetestW, testcmp1W) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW));
 
-    ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)emptyW);
+    ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)L"");
     ok(ret == TRUE, "EM_SETCUEBANNER should have returned TRUE.\n");
 
     ret = SendMessageW(hwnd_edit, EM_GETCUEBANNER, (WPARAM)getcuetestW, 5);
     ok(ret == TRUE, "EM_GETCUEBANNER should have returned TRUE.\n");
-    ok(lstrcmpW(getcuetestW, emptyW) == 0, "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW));
+    ok(!getcuetestW[0], "EM_GETCUEBANNER returned string %s.\n", wine_dbgstr_w(getcuetestW));
 
     /* EM_GETCUEBANNER's buffer size includes null char */
     ret = SendMessageW(hwnd_edit, EM_SETCUEBANNER, 0, (LPARAM)testcmp1W);
index 3e538c7..2e982e9 100644 (file)
 #include "v6util.h"
 #include "resources.h"
 
-#ifdef __REACTOS__
-#include <ole2.h>
-#endif
-
 #define IMAGELIST_MAGIC (('L' << 8) | 'I')
 
 #include "pshpack2.h"
@@ -2064,7 +2060,7 @@ static void check_color_table(const char *name, HDC hdc, HIMAGELIST himl, UINT i
 static void get_default_color_table(HDC hdc, int bpp, RGBQUAD *table)
 {
 #ifdef __REACTOS__
-    char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)];
+     char bmi_buffer[FIELD_OFFSET(BITMAPINFO, bmiColors) + 256 * sizeof(RGBQUAD)];
 #else
     char bmi_buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
 #endif
index 70e2128..4d44137 100644 (file)
@@ -707,16 +707,22 @@ static void test_LB_SETSEL(void)
     ok(ret == 0, "Unexpected return value %d.\n", ret);
     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
     ok(ret == 0, "Unexpected anchor index %d.\n", ret);
+    ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
+    ok(ret == 0, "Unexpected caret index %d.\n", ret);
 
     ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
     ok(ret == 0, "Unexpected return value %d.\n", ret);
     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+    ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
+    ok(ret == 1, "Unexpected caret index %d.\n", ret);
 
     ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
     ok(ret == 0, "Unexpected return value %d.\n", ret);
     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+    ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
+    ok(ret == 1, "Unexpected caret index %d.\n", ret);
 
     DestroyWindow(list);
 
@@ -731,16 +737,22 @@ static void test_LB_SETSEL(void)
     ok(ret == 0, "Unexpected return value %d.\n", ret);
     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
     ok(ret == 0, "Unexpected anchor index %d.\n", ret);
+    ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
+    ok(ret == 0, "Unexpected caret index %d.\n", ret);
 
     ret = SendMessageA(list, LB_SETSEL, TRUE, 1);
     ok(ret == 0, "Unexpected return value %d.\n", ret);
     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+    ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
+    ok(ret == 1, "Unexpected caret index %d.\n", ret);
 
     ret = SendMessageA(list, LB_SETSEL, FALSE, 1);
     ok(ret == 0, "Unexpected return value %d.\n", ret);
     ret = SendMessageA(list, LB_GETANCHORINDEX, 0, 0);
     ok(ret == 1, "Unexpected anchor index %d.\n", ret);
+    ret = SendMessageA(list, LB_GETCARETINDEX, 0, 0);
+    ok(ret == 1, "Unexpected caret index %d.\n", ret);
 
     DestroyWindow(list);
 }
@@ -769,11 +781,27 @@ static void test_listbox_height(void)
     r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
     ok( r == 20, "height wrong\n");
 
+    /* Before Windows 10 1709 (or 1703?) the item height was limited to 255.
+     * Since then, with comctl32 V6 the limit is 65535.
+     */
     r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 256, 0 ));
-    ok( r == -1, "Failed to set item height, %d.\n", r);
+    ok(r == 0 || broken(r == -1), "Failed to set item height, %d.\n", r);
+    if (r == -1)
+    {
+        r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+        ok( r == 20, "Unexpected item height %d.\n", r);
+    }
+    else
+    {
+        r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+        ok( r == 256, "Unexpected item height %d.\n", r);
 
-    r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
-    ok( r == 20, "Unexpected item height %d.\n", r);
+        r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 65535, 0 ));
+        ok(r == 0, "Failed to set item height, %d.\n", r);
+
+        r = SendMessageA(hList, LB_GETITEMHEIGHT, 0, 0 );
+        ok( r == 65535, "Unexpected item height %d.\n", r);
+    }
 
     r = SendMessageA( hList, LB_SETITEMHEIGHT, 0, MAKELPARAM( 0xff, 0 ));
     ok( r == 0, "send message failed\n");
@@ -1803,7 +1831,7 @@ static void test_listbox_dlgdir(void)
     ok (res == 0, "DlgDirSelectEx() with no selection returned %d, expected 0\n", res);
     /* WinXP-SP2 leaves pathBuffer untouched, but Win98 fills it with garbage. */
     /*
-    ok (strlen(pathBuffer) == 0, "DlgDirSelectEx() with no selection filled buffer with %s\n", pathBuffer);
+    ok (!*pathBuffer, "DlgDirSelectEx() with no selection filled buffer with %s\n", pathBuffer);
     */
     /* Test proper drive/dir/file recognition */
     itemCount = SendMessageA(g_listBox, LB_GETCOUNT, 0, 0);