diff --git a/dll/win32/comctl32/button.c b/dll/win32/comctl32/button.c index 9002697d60c..c979483b80b 100644 --- a/dll/win32/comctl32/button.c +++ b/dll/win32/comctl32/button.c @@ -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); diff --git a/dll/win32/comctl32/combo.c b/dll/win32/comctl32/combo.c index 0c67b2ee729..e083a58034f 100644 --- a/dll/win32/comctl32/combo.c +++ b/dll/win32/comctl32/combo.c @@ -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; + item_height = lphc->item_height; - if (lphc->hFont) - hPrevFont = SelectObject( hDC, lphc->hFont ); - - GetTextMetricsW(hDC, &tm); - - baseUnitY = tm.tmHeight; - - if( hPrevFont ) - SelectObject( hDC, hPrevFont ); - - ReleaseDC(hwnd, hDC); - - 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); + /* Chop off the bottom part to fit with the height of the text area. */ + combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE); - /* - * The button starts the same vertical position as the text area. - */ - CopyRect(lprButton, lprEdit); + /* The button starts the same vertical position as the text area. */ + combo->buttonRect = combo->textRect; - /* - * 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; - } - - /* - * 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 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()); - } - - /* - * 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); - - /* - * 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())) + /* 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 { - lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE()); + /* + * 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; + } - /* - * 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(); + /* 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(); + + /* 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()); + + /* Adjust the size of the listbox popup. */ + if (CB_GETTYPE(combo) == CBS_SIMPLE) + { + GetClientRect(combo->self, &combo->droppedRect); + combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE(); } else - lprLB->right = lprLB->left + lphc->droppedWidth; - } + { + /* 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()); - /* don't allow negative window width */ - if (lprEdit->right < lprEdit->left) - lprEdit->right = lprEdit->left; + /* 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("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit)); + /* Disallow negative window width */ + if (combo->textRect.right < combo->textRect.left) + combo->textRect.right = combo->textRect.left; - TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton)); - - 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); + BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE; - /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of - * sizing messages */ + /* 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)); - 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(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)); - 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 (drop) + { + if (combo->wState & CBF_DROPPED) + { + combo->wState &= ~CBF_DROPPED; + ShowWindow(combo->hWndLBox, SW_HIDE); + } - if( bDrop ) - { - if( lphc->wState & CBF_DROPPED ) - { - lphc->wState &= ~CBF_DROPPED; - ShowWindow( lphc->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: diff --git a/dll/win32/comctl32/comboex.c b/dll/win32/comctl32/comboex.c index 3c3714ac569..c3c39be3058 100644 --- a/dll/win32/comctl32/comboex.c +++ b/dll/win32/comctl32/comboex.c @@ -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)) { diff --git a/dll/win32/comctl32/comctl32.h b/dll/win32/comctl32/comctl32.h index 40baabb24ff..6f9c951b9e6 100644 --- a/dll/win32/comctl32/comctl32.h +++ b/dll/win32/comctl32/comctl32.h @@ -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 */ diff --git a/dll/win32/comctl32/comctl32undoc.c b/dll/win32/comctl32/comctl32undoc.c index 639db05d1bd..753b0f5f501 100644 --- a/dll/win32/comctl32/comctl32undoc.c +++ b/dll/win32/comctl32/comctl32undoc.c @@ -26,8 +26,6 @@ * COMCTL32.DLL (internally). * */ -#include "config.h" -#include "wine/port.h" #include #include @@ -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); diff --git a/dll/win32/comctl32/commctrl.c b/dll/win32/comctl32/commctrl.c index a3bbd5fc31d..f85282413e9 100644 --- a/dll/win32/comctl32/commctrl.c +++ b/dll/win32/comctl32/commctrl.c @@ -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 diff --git a/dll/win32/comctl32/edit.c b/dll/win32/comctl32/edit.c index ccd9373b199..6d124ce64a4 100644 --- a/dll/win32/comctl32/edit.c +++ b/dll/win32/comctl32/edit.c @@ -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 #include #include @@ -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; diff --git a/dll/win32/comctl32/header.c b/dll/win32/comctl32/header.c index e72324ce1f1..522f6dd1676 100644 --- a/dll/win32/comctl32/header.c +++ b/dll/win32/comctl32/header.c @@ -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)) diff --git a/dll/win32/comctl32/hotkey.c b/dll/win32/comctl32/hotkey.c index 7c3bff7f750..58717a2a493 100644 --- a/dll/win32/comctl32/hotkey.c +++ b/dll/win32/comctl32/hotkey.c @@ -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 index 00000000000..4b3f07b3aae Binary files /dev/null and b/dll/win32/comctl32/idb_cmdlink.bmp differ diff --git a/dll/win32/comctl32/imagelist.c b/dll/win32/comctl32/imagelist.c index 78407dfdd94..8d74aac6ba6 100644 --- a/dll/win32/comctl32/imagelist.c +++ b/dll/win32/comctl32/imagelist.c @@ -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; diff --git a/dll/win32/comctl32/ipaddress.c b/dll/win32/comctl32/ipaddress.c index aa2b6efc41d..aad2f5d86dd 100644 --- a/dll/win32/comctl32/ipaddress.c +++ b/dll/win32/comctl32/ipaddress.c @@ -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); diff --git a/dll/win32/comctl32/listbox.c b/dll/win32/comctl32/listbox.c index 897f2dc2c69..804edeca47a 100644 --- a/dll/win32/comctl32/listbox.c +++ b/dll/win32/comctl32/listbox.c @@ -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 @@ -33,15 +31,15 @@ #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)) { - if (!buffer) - return strlenW(descr->items[index].str); + WCHAR *str = get_item_string(descr, index); - TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str)); + if (!buffer) + return lstrlenW(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 (start >= descr->nb_items) start = -1; - item = descr->items + start + 1; + if (descr->style & LBS_NODATA) return LB_ERR; + + 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++) + 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; + } } - for (i = 0, item = descr->items; i <= start; i++, item++) - { - if (!strncmpiW( str, item->str, len )) return i; - CHECK_DRIVE(item); - } -#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; - } + if (!resize_storage(descr, descr->nb_items + 1)) return LB_ERR; - /* Insert the item structure */ - - 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) + if (!resize_storage(descr, count)) + return LB_ERRSPACE; + descr->nb_items = count; + + if (count) { - while (count > descr->nb_items) - if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0) - return ret; - } - else if (count < descr->nb_items) - { - 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; diff --git a/dll/win32/comctl32/listview.c b/dll/win32/comctl32/listview.c index 53e8b546bf2..5279c7b0d27 100644 --- a/dll/win32/comctl32/listview.c +++ b/dll/win32/comctl32/listview.c @@ -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) diff --git a/dll/win32/comctl32/monthcal.c b/dll/win32/comctl32/monthcal.c index 744b9d382ce..00e0f79fe9a 100644 --- a/dll/win32/comctl32/monthcal.c +++ b/dll/win32/comctl32/monthcal.c @@ -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; } diff --git a/dll/win32/comctl32/pager.c b/dll/win32/comctl32/pager.c index fe68515e673..4763ed5640d 100644 --- a/dll/win32/comctl32/pager.c +++ b/dll/win32/comctl32/pager.c @@ -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); diff --git a/dll/win32/comctl32/propsheet.c b/dll/win32/comctl32/propsheet.c index fa348c249f5..9600e81482a 100644 --- a/dll/win32/comctl32/propsheet.c +++ b/dll/win32/comctl32/propsheet.c @@ -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 ); } diff --git a/dll/win32/comctl32/rebar.c b/dll/win32/comctl32/rebar.c index 19019d7a547..818ae9948f3 100644 --- a/dll/win32/comctl32/rebar.c +++ b/dll/win32/comctl32/rebar.c @@ -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<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) { diff --git a/dll/win32/comctl32/static.c b/dll/win32/comctl32/static.c index 1d2e39f84ee..c81db5d445a 100644 --- a/dll/win32/comctl32/static.c +++ b/dll/win32/comctl32/static.c @@ -18,11 +18,6 @@ * 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 )) diff --git a/dll/win32/comctl32/status.c b/dll/win32/comctl32/status.c index 34a299eb744..879b281840f 100644 --- a/dll/win32/comctl32/status.c +++ b/dll/win32/comctl32/status.c @@ -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); diff --git a/dll/win32/comctl32/string.c b/dll/win32/comctl32/string.c index 112d9d37b43..d5bc6d3577f 100644 --- a/dll/win32/comctl32/string.c +++ b/dll/win32/comctl32/string.c @@ -22,9 +22,6 @@ * */ -#include "config.h" -#include "wine/port.h" - #include #include #include /* 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 ); } /************************************************************************* diff --git a/dll/win32/comctl32/syslink.c b/dll/win32/comctl32/syslink.c index 91796a32e44..a00ef9e4a3a 100644 --- a/dll/win32/comctl32/syslink.c +++ b/dll/win32/comctl32/syslink.c @@ -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 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. */ diff --git a/dll/win32/comctl32/taskdialog.c b/dll/win32/comctl32/taskdialog.c index 1ab9e133dac..3d98c644e84 100644 --- a/dll/win32/comctl32/taskdialog.c +++ b/dll/win32/comctl32/taskdialog.c @@ -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 @@ -34,74 +35,73 @@ #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; - -struct taskdialog_control -{ - struct list entry; - DLGITEMTEMPLATE *template; - unsigned int template_size; -}; - -struct taskdialog_button_desc -{ - int id; - const WCHAR *text; - unsigned int width; - unsigned int line; - HINSTANCE hinst; -}; - -struct taskdialog_template_desc -{ - 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; -}; +static const UINT ID_TIMER = 1; struct taskdialog_info { HWND hwnd; - PFTASKDIALOGCALLBACK callback; - LONG_PTR callback_data; + 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; }; -static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height) +struct button_layout_info { - if (width) - *width = MulDiv(*width, 4, desc->x_baseunit); - if (height) - *height = MulDiv(*height, 8, desc->y_baseunit); -} + LONG width; + LONG line; +}; -static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, LONG *width, LONG *height) +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) { - if (width) - *width = MulDiv(*width, desc->x_baseunit, 4); - if (height) - *height = MulDiv(*height, desc->y_baseunit, 8); + if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4); + if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8); } static void template_write_data(char **ptr, const void *src, unsigned int size) @@ -110,210 +110,887 @@ static void template_write_data(char **ptr, const void *src, unsigned int size) *ptr += size; } -/* 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 unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret) { - 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; + HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(), + MONITOR_DEFAULTTOPRIMARY); + MONITORINFO info; - 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; - } - } + info.cbSize = sizeof(info); + GetMonitorInfoW(monitor, &info); + + if ((taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) && taskconfig->hwndParent) + GetWindowRect(taskconfig->hwndParent, ret); else - { - textW = text; - length = strlenW(textW); - } + *ret = info.rcWork; - hdc = GetDC(0); - oldfont = SelectObject(hdc, desc->font); - - 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); - - SelectObject(hdc, oldfont); - ReleaseDC(0, hdc); - - sz->cx = rect.right - rect.left; - sz->cy = rect.bottom - rect.top; + return info.rcWork.right - info.rcWork.left; } -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 WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length) { - struct taskdialog_control *control = Alloc(sizeof(*control)); - unsigned int size, class_size, text_size; - DLGITEMTEMPLATE *template; - static const WCHAR nulW; - const WCHAR *textW; + 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 DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig) +{ + 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; - class_size = (strlenW(class) + 1) * sizeof(WCHAR); + /* 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; +} + +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) +{ + const WCHAR *textW = NULL; + INT length; + WCHAR *ret; if (IS_INTRESOURCE(text)) - text_size = LoadStringW(hInstance, (UINT_PTR)text, (WCHAR *)&textW, 0) * sizeof(WCHAR); + { + if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule, + (UINT_PTR)text, (WCHAR *)&textW, 0))) + return NULL; + } else { textW = text; - text_size = strlenW(textW) * sizeof(WCHAR); + length = lstrlenW(textW); } - size = sizeof(DLGITEMTEMPLATE); - size += class_size; - size += text_size + sizeof(WCHAR); - size += sizeof(WORD); /* creation data */ + ret = Alloc((length + 1) * sizeof(WCHAR)); + if (ret) memcpy(ret, textW, length * sizeof(WCHAR)); - control->template = template = Alloc(size); - control->template_size = size; - - 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)); - - list_add_tail(&desc->controls, &control->entry); - desc->control_count++; - return ALIGNED_LENGTH(size, 3); + return ret; } -static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc *desc, WORD id, const WCHAR *str) +static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info) { - unsigned int size; - SIZE sz; - - if (!str) - return 0; - - taskdialog_get_text_extent(desc, str, TRUE, &sz); - - 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; + return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS; } -static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc) +static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info) { - return taskdialog_add_static_label(desc, ID_MAIN_INSTRUCTION, desc->taskconfig->pszMainInstruction); + return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON); } -static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc) +static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size, + BOOL syslink) { - return taskdialog_add_static_label(desc, ID_CONTENT, desc->taskconfig->pszContent); + DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK; + HFONT hfont, old_hfont; + HDC hdc; + RECT rect = {0}; + WCHAR *text; + INT text_length; + + if (syslink) + { + SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size); + return; + } + + 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) + { + 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); } -static void taskdialog_init_button(struct taskdialog_button_desc *button, struct taskdialog_template_desc *desc, - int id, const WCHAR *text, BOOL custom_button) +static void taskdialog_get_button_size(HWND hwnd, LONG max_width, SIZE *size) { - SIZE sz; - - taskdialog_get_text_extent(desc, text, custom_button, &sz); - - 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; - - if (id == desc->taskconfig->nDefaultButton) - desc->default_button = button; + size->cx = max_width; + size->cy = 0; + SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)size); } -static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons, - unsigned int *button_count) +static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size) { - DWORD flags = desc->taskconfig->dwCommonButtons; + 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; -#define TASKDIALOG_INIT_COMMON_BUTTON(id) \ - do { \ - taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \ - } while(0) + hdc = GetDC(hwnd); + hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0); + old_hfont = SelectObject(hdc, hfont); - 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); + icon_width = DIALOG_EXPANDO_ICON_WIDTH; + icon_height = DIALOG_EXPANDO_ICON_HEIGHT; + taskdialog_du_to_px(dialog_info, &icon_width, &icon_height); -#undef TASKDIALOG_INIT_COMMON_BUTTON + 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); + + 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_buttons(struct taskdialog_template_desc *desc) +static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon) { - 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; + 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; +} + +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; + + if (!icon) return; + + 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 void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info) +{ + 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 void taskdialog_add_main_icon(struct taskdialog_info *dialog_info) +{ + 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 HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink) +{ + WCHAR *textW; + HWND hwnd; + const WCHAR *class; + DWORD style = WS_CHILD | WS_VISIBLE; + + if (!text) return NULL; + + 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); + + SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0); + return hwnd; +} + +static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info) +{ + const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig; + NONCLIENTMETRICSW ncm; + + if (!taskconfig->pszMainInstruction) return; + + 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); + + dialog_info->main_instruction = + taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE); +} + +static void taskdialog_add_content(struct taskdialog_info *dialog_info) +{ + 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; - if (!(buttons = Alloc(buttons_size * sizeof(*buttons)))) - return 0; + dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons)); + if (!dialog_info->buttons) return; - /* 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); + 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); - /* Common buttons */ - taskdialog_init_common_buttons(desc, buttons, &count); +#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) - /* There must be at least one button */ - if (count == 0) - taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE); + 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 (!desc->default_button) - desc->default_button = &buttons[0]; + if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK); +#undef TASKDIALOG_INIT_COMMON_BUTTON - /* For easy handling just allocate as many lines as buttons, the worst case. */ - line_widths = Alloc(count * sizeof(*line_widths)); + dialog_info->button_count = count; +} + +static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info) +{ + if (!dialog_info->taskconfig->u2.hFooterIcon) return; + + 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); +} + +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)); +} + +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; + } + + 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; + /* 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); - info.cbSize = sizeof(info); - GetMonitorInfoW(monitor, &info); + /* 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); - if (desc->taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && desc->taskconfig->hwndParent) - GetWindowRect(desc->taskconfig->hwndParent, ret); - else - *ret = info.rcWork; + /* Add height for spacing, title height and frame height */ + dialog_height += v_spacing; + dialog_height += GetSystemMetrics(SM_CYCAPTION); + dialog_height += GetSystemMetrics(SM_CXDLGFRAME); - pixels_to_dialogunits(desc, &ret->left, &ret->top); - pixels_to_dialogunits(desc, &ret->right, &ret->bottom); - - 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; -} - -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; - /* 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); + hdc = dis->hDC; + hwnd = dis->hwndItem; - size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD); - size += title_size; - size += 2; /* font size */ + SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0); - list_init(&desc.controls); - desc.taskconfig = taskconfig; - desc.control_count = 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); +} + +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); + memset(dialog_info, 0, sizeof(*dialog_info)); + dialog_info->taskconfig = taskconfig; + dialog_info->hwnd = hwnd; + dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont); - screen_width = taskdialog_get_reference_rect(&desc, &ref_rect); + 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); - 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; + 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); - size += taskdialog_add_main_instruction(&desc); - size += taskdialog_add_content(&desc); - size += taskdialog_add_buttons(&desc); - - 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; + 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); - ptr = (char *)(template + 1); - ptr += 2; /* menu */ - ptr += 2; /* class */ - template_write_data(&ptr, titleW, title_size); - template_write_data(&ptr, &fontsize, sizeof(fontsize)); + /* 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); - /* write control entries */ - LIST_FOR_EACH_ENTRY_SAFE(control, control2, &desc.controls, struct taskdialog_control, entry) - { - ALIGN_POINTER(ptr, 3); + 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); - template_write_data(&ptr, control->template, control->template_size); + if (!dialog_info->has_cancel) DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND); - /* list item won't be needed later */ - list_remove(&control->entry); - Free(control->template); - Free(control); - } - - DeleteObject(desc.font); - return template; + 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; } diff --git a/dll/win32/comctl32/theming.c b/dll/win32/comctl32/theming.c index ec17033729b..d66c7844532 100644 --- a/dll/win32/comctl32/theming.c +++ b/dll/win32/comctl32/theming.c @@ -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); -} diff --git a/dll/win32/comctl32/toolbar.c b/dll/win32/comctl32/toolbar.c index 441b7ff1db8..844f4e4ca68 100644 --- a/dll/win32/comctl32/toolbar.c +++ b/dll/win32/comctl32/toolbar.c @@ -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; } diff --git a/dll/win32/comctl32/tooltips.c b/dll/win32/comctl32/tooltips.c index a1729380d93..8720902455e 100644 --- a/dll/win32/comctl32/tooltips.c +++ b/dll/win32/comctl32/tooltips.c @@ -93,10 +93,10 @@ #include #include +#include #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; - } - } + for (i = 0; i < infoPtr->uNumTools; i++) + { + toolPtr = &infoPtr->tools[i]; - TOOLTIPS_ResetSubclass (toolPtr); - } - - 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; diff --git a/dll/win32/comctl32/trackbar.c b/dll/win32/comctl32/trackbar.c index 233baedf274..502228d7b98 100644 --- a/dll/win32/comctl32/trackbar.c +++ b/dll/win32/comctl32/trackbar.c @@ -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; diff --git a/dll/win32/comctl32/treeview.c b/dll/win32/comctl32/treeview.c index 945084d60bf..5bd60bf5c17 100644 --- a/dll/win32/comctl32/treeview.c +++ b/dll/win32/comctl32/treeview.c @@ -40,9 +40,6 @@ * Scroll (instead of repaint) as much as possible. */ -#include "config.h" -#include "wine/port.h" - #include #include #include @@ -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; diff --git a/dll/win32/comctl32/updown.c b/dll/win32/comctl32/updown.c index 6b328ff90f9..75dcade9afd 100644 --- a/dll/win32/comctl32/updown.c +++ b/dll/win32/comctl32/updown.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include #include #include #include @@ -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; } diff --git a/media/doc/WINESYNC.txt b/media/doc/WINESYNC.txt index 2b34250f63c..e6797f0cc67 100644 --- a/media/doc/WINESYNC.txt +++ b/media/doc/WINESYNC.txt @@ -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) diff --git a/modules/rostests/winetests/comctl32/combo.c b/modules/rostests/winetests/comctl32/combo.c index 923d826b305..d8fe9d6b253 100644 --- a/modules/rostests/winetests/comctl32/combo.c +++ b/modules/rostests/winetests/comctl32/combo.c @@ -27,6 +27,10 @@ #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); diff --git a/modules/rostests/winetests/comctl32/edit.c b/modules/rostests/winetests/comctl32/edit.c index 6013cafb59d..cec60250770 100644 --- a/modules/rostests/winetests/comctl32/edit.c +++ b/modules/rostests/winetests/comctl32/edit.c @@ -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); diff --git a/modules/rostests/winetests/comctl32/imagelist.c b/modules/rostests/winetests/comctl32/imagelist.c index 3e538c7c0ce..2e982e99c64 100644 --- a/modules/rostests/winetests/comctl32/imagelist.c +++ b/modules/rostests/winetests/comctl32/imagelist.c @@ -41,10 +41,6 @@ #include "v6util.h" #include "resources.h" -#ifdef __REACTOS__ -#include -#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 diff --git a/modules/rostests/winetests/comctl32/listbox.c b/modules/rostests/winetests/comctl32/listbox.c index 70e212892a4..4d44137e4d0 100644 --- a/modules/rostests/winetests/comctl32/listbox.c +++ b/modules/rostests/winetests/comctl32/listbox.c @@ -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); diff --git a/modules/rostests/winetests/comctl32/listview.c b/modules/rostests/winetests/comctl32/listview.c index 45f2b60de2c..20b9873d30d 100644 --- a/modules/rostests/winetests/comctl32/listview.c +++ b/modules/rostests/winetests/comctl32/listview.c @@ -956,6 +956,37 @@ static void test_images(void) ok(EqualRect(&r1, &r2), "rectangle should be the same\n"); DestroyWindow(hwnd); + + /* I_IMAGECALLBACK set for item, try to get image with invalid subitem. */ + hwnd = create_listview_control(LVS_REPORT); + ok(hwnd != NULL, "Failed to create listview.\n"); + + memset(&item, 0, sizeof(item)); + item.mask = LVIF_IMAGE; + item.iImage = I_IMAGECALLBACK; + r = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item); + ok(!r, "Failed to insert item.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + memset(&item, 0, sizeof(item)); + item.mask = LVIF_IMAGE; + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get item.\n"); + + ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq, "get image dispinfo 1", FALSE); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + memset(&item, 0, sizeof(item)); + item.mask = LVIF_IMAGE; + item.iSubItem = 1; + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get item.\n"); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "get image dispinfo 2", FALSE); + + DestroyWindow(hwnd); } static void test_checkboxes(void) @@ -1546,6 +1577,19 @@ static void test_columns(void) "get subitem text after column added", FALSE); DestroyWindow(hwnd); + + /* Columns are not created right away. */ + hwnd = create_listview_control(LVS_REPORT); + ok(hwnd != NULL, "Failed to create a listview window.\n"); + + insert_item(hwnd, 0); + + header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0); + ok(IsWindow(header), "Expected header handle.\n"); + rc = SendMessageA(header, HDM_GETITEMCOUNT, 0, 0); + ok(!rc, "Unexpected column count.\n"); + + DestroyWindow(hwnd); } /* test setting imagelist between WM_NCCREATE and WM_CREATE */ @@ -1872,6 +1916,8 @@ static LRESULT WINAPI cd_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM NMHDR *nmhdr = (NMHDR*)lParam; if(nmhdr->code == NM_CUSTOMDRAW) { NMLVCUSTOMDRAW *nmlvcd = (NMLVCUSTOMDRAW*)nmhdr; + BOOL showsel_always = !!(GetWindowLongA(nmlvcd->nmcd.hdr.hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS); + BOOL is_selected = !!(nmlvcd->nmcd.uItemState & CDIS_SELECTED); struct message msg; msg.message = message; @@ -1887,25 +1933,40 @@ static LRESULT WINAPI cd_wndproc(HWND hwnd, UINT message, WPARAM wParam, LPARAM SetBkColor(nmlvcd->nmcd.hdc, c0ffee); return CDRF_NOTIFYITEMDRAW|CDRF_NOTIFYPOSTPAINT; case CDDS_ITEMPREPAINT: + clr = GetBkColor(nmlvcd->nmcd.hdc); + todo_wine_if(nmlvcd->iSubItem) + ok(clr == c0ffee, "Unexpected background color %#x.\n", clr); nmlvcd->clrTextBk = CLR_DEFAULT; nmlvcd->clrText = RGB(0, 255, 0); return CDRF_NOTIFYSUBITEMDRAW|CDRF_NOTIFYPOSTPAINT; case CDDS_ITEMPREPAINT | CDDS_SUBITEM: clr = GetBkColor(nmlvcd->nmcd.hdc); - ok(nmlvcd->clrTextBk == CLR_DEFAULT, "got 0x%x\n", nmlvcd->clrTextBk); - ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText); - if (!(GetWindowLongW(nmhdr->hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS)) + todo_wine_if(showsel_always && is_selected && nmlvcd->iSubItem) { - todo_wine_if(nmlvcd->iSubItem) - ok(clr == c0ffee, "clr=%.8x\n", clr); + ok(nmlvcd->clrTextBk == CLR_DEFAULT, "Unexpected text background %#x.\n", nmlvcd->clrTextBk); + ok(nmlvcd->clrText == RGB(0, 255, 0), "Unexpected text color %#x.\n", nmlvcd->clrText); } + if (showsel_always && is_selected && nmlvcd->iSubItem) + ok(clr == GetSysColor(COLOR_3DFACE), "Unexpected background color %#x.\n", clr); + else + todo_wine_if(nmlvcd->iSubItem) + ok(clr == c0ffee, "clr=%.8x\n", clr); return CDRF_NOTIFYPOSTPAINT; case CDDS_ITEMPOSTPAINT | CDDS_SUBITEM: clr = GetBkColor(nmlvcd->nmcd.hdc); - if (!(GetWindowLongW(nmhdr->hwndFrom, GWL_STYLE) & LVS_SHOWSELALWAYS)) - todo_wine ok(clr == c0ffee, "clr=%.8x\n", clr); - ok(nmlvcd->clrTextBk == CLR_DEFAULT, "got 0x%x\n", nmlvcd->clrTextBk); - ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText); + if (showsel_always && is_selected) + ok(clr == GetSysColor(COLOR_3DFACE), "Unexpected background color %#x.\n", clr); + else + { + todo_wine + ok(clr == c0ffee, "Unexpected background color %#x.\n", clr); + } + + todo_wine_if(showsel_always) + { + ok(nmlvcd->clrTextBk == CLR_DEFAULT, "Unexpected text background color %#x.\n", nmlvcd->clrTextBk); + ok(nmlvcd->clrText == RGB(0, 255, 0), "got 0x%x\n", nmlvcd->clrText); + } return CDRF_DODEFAULT; } return CDRF_DODEFAULT; @@ -1939,8 +2000,7 @@ static void test_customdraw(void) UpdateWindow(hwnd); ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, "parent customdraw, LVS_REPORT", FALSE); - /* check colors when item is selected */ - SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | LVS_SHOWSELALWAYS); + /* Check colors when item is selected. */ item.mask = LVIF_STATE; item.stateMask = LVIS_SELECTED; item.state = LVIS_SELECTED; @@ -1949,7 +2009,15 @@ static void test_customdraw(void) flush_sequences(sequences, NUM_MSG_SEQUENCES); InvalidateRect(hwnd, NULL, TRUE); UpdateWindow(hwnd); - ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, "parent customdraw, LVS_REPORT, selection", FALSE); + ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, + "parent customdraw, item selected, LVS_REPORT, selection", FALSE); + + SetWindowLongW(hwnd, GWL_STYLE, GetWindowLongW(hwnd, GWL_STYLE) | LVS_SHOWSELALWAYS); + flush_sequences(sequences, NUM_MSG_SEQUENCES); + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + ok_sequence(sequences, PARENT_CD_SEQ_INDEX, parent_report_cd_seq, + "parent customdraw, item selected, LVS_SHOWSELALWAYS, LVS_REPORT", FALSE); DestroyWindow(hwnd); @@ -3956,25 +4024,6 @@ static void test_getitemposition(void) DestroyWindow(hwnd); } -static void test_columnscreation(void) -{ - HWND hwnd, header; - DWORD r; - - hwnd = create_listview_control(LVS_REPORT); - ok(hwnd != NULL, "failed to create a listview window\n"); - - insert_item(hwnd, 0); - - /* headers columns aren't created automatically */ - header = (HWND)SendMessageA(hwnd, LVM_GETHEADER, 0, 0); - ok(IsWindow(header), "Expected header handle\n"); - r = SendMessageA(header, HDM_GETITEMCOUNT, 0, 0); - expect(0, r); - - DestroyWindow(hwnd); -} - static void test_getitemrect(void) { HWND hwnd; @@ -4574,6 +4623,17 @@ static void test_indentation(void) ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq, "get indent dispinfo", FALSE); + /* Ask for iIndent with invalid subitem. */ + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + memset(&item, 0, sizeof(item)); + item.mask = LVIF_INDENT; + item.iSubItem = 1; + r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(r, "Failed to get item.\n"); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "get indent dispinfo 2", FALSE); + DestroyWindow(hwnd); } @@ -6081,6 +6141,44 @@ static void test_callback_mask(void) mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0); ok(mask == ~0u, "got 0x%08x\n", mask); + /* Ask for state, invalid subitem. */ + insert_item(hwnd, 0); + + ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, LVIS_FOCUSED, 0); + ok(ret, "Failed to set callback mask.\n"); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + memset(&item, 0, sizeof(item)); + item.iSubItem = 1; + item.mask = LVIF_STATE; + item.stateMask = LVIS_SELECTED; + ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(ret, "Failed to get item data.\n"); + + memset(&item, 0, sizeof(item)); + item.mask = LVIF_STATE; + item.stateMask = LVIS_SELECTED; + ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(ret, "Failed to get item data.\n"); + + ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, callback mask/invalid subitem 1", TRUE); + + flush_sequences(sequences, NUM_MSG_SEQUENCES); + + memset(&item, 0, sizeof(item)); + memset(&g_itema, 0, sizeof(g_itema)); + item.iSubItem = 1; + item.mask = LVIF_STATE; + item.stateMask = LVIS_FOCUSED | LVIS_SELECTED; + ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); + ok(ret, "Failed to get item data.\n"); + ok(g_itema.iSubItem == 1, "Unexpected LVN_DISPINFO subitem %d.\n", g_itema.iSubItem); + ok(g_itema.stateMask == LVIS_FOCUSED, "Unexpected state mask %#x.\n", g_itema.stateMask); + + ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq, + "parent seq, callback mask/invalid subitem 2", FALSE); + DestroyWindow(hwnd); /* LVS_OWNERDATA, mask LVIS_FOCUSED */ @@ -6294,7 +6392,6 @@ static void test_state_image(void) item.iSubItem = 2; r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item); ok(r, "Failed to get subitem state.\n"); - todo_wine ok(item.state == 0, "Unexpected state %#x.\n", item.state); item.mask = LVIF_TEXT; @@ -6594,7 +6691,6 @@ START_TEST(listview) test_hittest(); test_getviewrect(); test_getitemposition(); - test_columnscreation(); test_editbox(); test_notifyformat(); test_indentation(); @@ -6649,7 +6745,6 @@ START_TEST(listview) test_ownerdata(); test_norecompute(); test_nosortheader(); - test_columnscreation(); test_indentation(); test_finditem(); test_hover(); diff --git a/modules/rostests/winetests/comctl32/misc.c b/modules/rostests/winetests/comctl32/misc.c index a52744fc2e9..c027852047a 100644 --- a/modules/rostests/winetests/comctl32/misc.c +++ b/modules/rostests/winetests/comctl32/misc.c @@ -224,11 +224,7 @@ static void test_LoadIconWithScaleDown(void) pLoadIconWithScaleDown = (void *)GetProcAddress(hinst, "LoadIconWithScaleDown"); if (!pLoadIconMetric || !pLoadIconWithScaleDown) { -#ifdef __REACTOS__ - skip("LoadIconMetric or pLoadIconWithScaleDown not exported by name\n"); -#else win_skip("LoadIconMetric or pLoadIconWithScaleDown not exported by name\n"); -#endif FreeLibrary(hinst); return; } diff --git a/modules/rostests/winetests/comctl32/msg.h b/modules/rostests/winetests/comctl32/msg.h index 17e74a36bf5..024158868d2 100644 --- a/modules/rostests/winetests/comctl32/msg.h +++ b/modules/rostests/winetests/comctl32/msg.h @@ -18,7 +18,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifdef __REACTOS__ #pragma once +#endif #include #include @@ -164,7 +166,7 @@ static void dump_sequence( struct msg_sequence **seq, int sequence_index, } } -static inline void ok_sequence_(struct msg_sequence **seq, int sequence_index, +static void ok_sequence_(struct msg_sequence **seq, int sequence_index, const struct message *expected_list, const char *context, BOOL todo, const char *file, int line) { @@ -388,7 +390,7 @@ done: ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__) -static inline void init_msg_sequences(struct msg_sequence **seq, int n) +static void init_msg_sequences(struct msg_sequence **seq, int n) { int i; diff --git a/modules/rostests/winetests/comctl32/pager.c b/modules/rostests/winetests/comctl32/pager.c index 4b5dfe6d96b..fba3cb8bef3 100644 --- a/modules/rostests/winetests/comctl32/pager.c +++ b/modules/rostests/winetests/comctl32/pager.c @@ -30,33 +30,18 @@ static HWND parent_wnd, child1_wnd, child2_wnd; static INT notify_format; static BOOL notify_query_received; -static WCHAR test_w[] = {'t', 'e', 's', 't', 0}; -static CHAR test_a[] = {'t', 'e', 's', 't', 0}; +static const CHAR test_a[] = "test"; +static const WCHAR test_w[] = L"test"; /* Double zero so that it's safe to cast it to WCHAR * */ -static CHAR te_a[] = {'t', 'e', 0, 0}; -static WCHAR empty_w[] = {0}; -static CHAR empty_a[] = {0}; -static CHAR large_a[] = "You should have received a copy of the GNU Lesser General Public License along with this ..."; -static WCHAR large_w[] = -{ - 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v', 'e', - 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L', 'e', 's', - 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', ' ', 'L', 'i', 'c', 'e', - 'n', 's', 'e', ' ', 'a', 'l', 'o', 'n', 'g', ' ', 'w', 'i', 't', 'h', ' ', 't', 'h', 'i', 's', ' ', '.', '.', '.', 0 -}; -static WCHAR large_truncated_65_w[65] = -{ - 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v', - 'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', 'N', 'U', ' ', 'L', - 'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', 'b', 'l', 'i', 'c', 0 -}; -static WCHAR large_truncated_80_w[80] = -{ - 'Y', 'o', 'u', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', 'h', 'a', 'v', 'e', ' ', 'r', 'e', 'c', 'e', - 'i', 'v', 'e', 'd', ' ', 'a', ' ', 'c', 'o', 'p', 'y', ' ', 'o', 'f', ' ', 't', 'h', 'e', ' ', 'G', - 'N', 'U', ' ', 'L', 'e', 's', 's', 'e', 'r', ' ', 'G', 'e', 'n', 'e', 'r', 'a', 'l', ' ', 'P', 'u', - 'b', 'l', 'i', 'c', ' ', 'L', 'i', 'c', 'e', 'n', 's', 'e', ' ', 'a', 'l', 'o', 'n', 'g', ' ', 'w' -}; +static const CHAR te_a[] = {'t', 'e', 0, 0}; +static const CHAR large_a[] = + "You should have received a copy of the GNU Lesser General Public License along with this ..."; +static const WCHAR large_w[] = + L"You should have received a copy of the GNU Lesser General Public License along with this ..."; +static const WCHAR large_truncated_65_w[65] = + L"You should have received a copy of the GNU Lesser General Public"; +static const WCHAR large_truncated_80_w[80] = + L"You should have received a copy of the GNU Lesser General Public License along w"; static WCHAR buffer[64]; /* Text field conversion test behavior flags. */ @@ -96,26 +81,26 @@ static struct notify_test_info struct notify_test_send { /* Data sent to pager */ - WCHAR *send_text; + const WCHAR *send_text; INT send_text_size; INT send_text_max; /* Data expected by parent of pager */ - void *expect_text; + const void *expect_text; }; struct notify_test_receive { /* Data sent to pager */ - WCHAR *send_text; + const WCHAR *send_text; INT send_text_size; INT send_text_max; /* Data for parent to write */ - CHAR *write_pointer; - CHAR *write_text; + const CHAR *write_pointer; + const CHAR *write_text; INT write_text_size; INT write_text_max; /* Data when message returned */ - void *return_text; + const void *return_text; INT return_text_max; }; @@ -145,69 +130,69 @@ static const struct notify_test_send test_dont_convert_send_data[] = static const struct notify_test_receive test_convert_receive_data[] = { - {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_w, ARRAY_SIZE(buffer)}, - {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_w, ARRAY_SIZE(buffer)}, - {NULL, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, NULL, ARRAY_SIZE(buffer)}, - {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), large_a, NULL, 0, -1, large_truncated_65_w, ARRAY_SIZE(buffer)}, - {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), empty_a, 0, 0, 1, empty_w, 1}, + {L"", sizeof(L""), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_w, ARRAY_SIZE(buffer)}, + {L"", sizeof(L""), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_w, ARRAY_SIZE(buffer)}, + {NULL, sizeof(L""), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, NULL, ARRAY_SIZE(buffer)}, + {L"", sizeof(L""), ARRAY_SIZE(buffer), large_a, NULL, 0, -1, large_truncated_65_w, ARRAY_SIZE(buffer)}, + {L"", sizeof(L""), ARRAY_SIZE(buffer), "", 0, 0, 1, L"", 1}, }; static const struct notify_test_receive test_dont_convert_receive_data[] = { - {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_a, ARRAY_SIZE(buffer)}, - {empty_w, sizeof(empty_w), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_a, ARRAY_SIZE(buffer)}, + {L"", sizeof(L""), ARRAY_SIZE(buffer), NULL, test_a, sizeof(test_a), -1, test_a, ARRAY_SIZE(buffer)}, + {L"", sizeof(L""), ARRAY_SIZE(buffer), test_a, NULL, 0, -1, test_a, ARRAY_SIZE(buffer)}, }; static const struct notify_test_tooltip { /* Data for parent to write */ - CHAR *write_sztext; + const CHAR *write_sztext; INT write_sztext_size; - CHAR *write_lpsztext; + const CHAR *write_lpsztext; HMODULE write_hinst; /* Data when message returned */ - WCHAR *return_sztext; + const WCHAR *return_sztext; INT return_sztext_size; - WCHAR *return_lpsztext; + const WCHAR *return_lpsztext; HMODULE return_hinst; /* Data expected by parent */ - CHAR *expect_sztext; + const CHAR *expect_sztext; /* Data send to parent */ - WCHAR *send_sztext; + const WCHAR *send_sztext; INT send_sztext_size; - WCHAR *send_lpsztext; + const WCHAR *send_lpsztext; } test_tooltip_data[] = { - {NULL, 0, NULL, NULL, empty_w, -1, empty_w}, + {NULL, 0, NULL, NULL, L"", -1, L""}, {test_a, sizeof(test_a), NULL, NULL, test_w, -1, test_w}, {test_a, sizeof(test_a), test_a, NULL, test_w, -1, test_w}, - {test_a, sizeof(test_a), (CHAR *)1, (HMODULE)0xdeadbeef, empty_w, -1, (WCHAR *)1, (HMODULE)0xdeadbeef}, + {test_a, sizeof(test_a), (CHAR *)1, (HMODULE)0xdeadbeef, L"", -1, (WCHAR *)1, (HMODULE)0xdeadbeef}, {test_a, sizeof(test_a), test_a, (HMODULE)0xdeadbeef, test_w, -1, test_w, (HMODULE)0xdeadbeef}, {NULL, 0, test_a, NULL, test_w, -1, test_w}, {test_a, 2, test_a, NULL, test_w, -1, test_w}, {NULL, 0, NULL, NULL, test_w, -1, test_w, NULL, test_a, test_w, sizeof(test_w)}, - {NULL, 0, NULL, NULL, empty_w, -1, empty_w, NULL, empty_a, NULL, 0, test_w}, + {NULL, 0, NULL, NULL, L"", -1, L"", NULL, "", NULL, 0, test_w}, {NULL, 0, large_a, NULL, large_truncated_80_w, sizeof(large_truncated_80_w), large_w} }; static const struct notify_test_datetime_format { /* Data send to parent */ - WCHAR *send_pszformat; + const WCHAR *send_pszformat; /* Data expected by parent */ - CHAR *expect_pszformat; + const CHAR *expect_pszformat; /* Data for parent to write */ - CHAR *write_szdisplay; + const CHAR *write_szdisplay; INT write_szdisplay_size; - CHAR *write_pszdisplay; + const CHAR *write_pszdisplay; /* Data when message returned */ - WCHAR *return_szdisplay; + const WCHAR *return_szdisplay; INT return_szdisplay_size; - WCHAR *return_pszdisplay; + const WCHAR *return_pszdisplay; } test_datetime_format_data[] = { {test_w, test_a}, - {NULL, NULL, NULL, 0, test_a, empty_w, -1, test_w}, + {NULL, NULL, NULL, 0, test_a, L"", -1, test_w}, {NULL, NULL, test_a, sizeof(test_a), NULL, test_w, -1, test_w}, {NULL, NULL, test_a, 2, test_a, (WCHAR *)te_a, -1, test_w}, {NULL, NULL, NULL, 0, large_a, NULL, 0, large_w} @@ -624,7 +609,7 @@ static void notify_generic_text_handler(CHAR **text, INT *text_max) send_data = (notify_test_info.test_id == CONVERT_SEND ? test_convert_send_data : test_dont_convert_send_data) + notify_test_info.sub_test_id; if (notify_test_info.flags & ZERO_SEND) - ok(!lstrcmpA(*text, empty_a), "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n", + ok(!*text[0], "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n", notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, *text); else if (notify_test_info.flags & CONVERT_SEND) ok(!lstrcmpA(send_data->expect_text, *text), "Code 0x%08x test 0x%08x sub test %d expect %s, got %s\n", @@ -658,12 +643,12 @@ static void notify_generic_text_handler(CHAR **text, INT *text_max) else if(notify_test_info.unicode == HDN_GETDISPINFOW) *text = heap_strdup(receive_data->write_pointer); else - *text = receive_data->write_pointer; + *text = (char *)receive_data->write_pointer; if (text_max && receive_data->write_text_max != -1) *text_max = receive_data->write_text_max; break; } case SEND_EMPTY_IF_NULL: - ok(!lstrcmpA(*text, empty_a), "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n", + ok(!*text[0], "Code 0x%08x test 0x%08x sub test %d expect empty text, got %s\n", notify_test_info.unicode, notify_test_info.test_id, notify_test_info.sub_test_id, *text); break; case DONT_SEND_EMPTY_IF_NULL: @@ -682,7 +667,7 @@ static void notify_tooltip_handler(NMTTDISPINFOA *nm) ok(!lstrcmpA(data->expect_sztext, nm->szText), "Sub test %d expect %s, got %s\n", notify_test_info.sub_test_id, data->expect_sztext, nm->szText); if (data->write_sztext) memcpy(nm->szText, data->write_sztext, data->write_sztext_size); - if (data->write_lpsztext) nm->lpszText = data->write_lpsztext; + if (data->write_lpsztext) nm->lpszText = (char *)data->write_lpsztext; if (data->write_hinst) nm->hinst = data->write_hinst; } @@ -1025,8 +1010,9 @@ static void test_notify_generic_text_helper(HWND pager, const struct generic_tex if (data->return_text) { if (para->flags & CONVERT_RECEIVE) - ok(!lstrcmpW(data->return_text, *para->text), "Code 0x%08x sub test %d expect %s, got %s\n", - para->code_unicode, i, wine_dbgstr_w((WCHAR *)data->return_text), wine_dbgstr_w(*para->text)); + ok(!wcsncmp(data->return_text, *para->text, *para->text_max), + "Code 0x%08x sub test %d expect %s, got %s\n", para->code_unicode, i, + wine_dbgstr_w((WCHAR *)data->return_text), wine_dbgstr_w(*para->text)); else ok(!lstrcmpA(data->return_text, (CHAR *)*para->text), "Code 0x%08x sub test %d expect %s, got %s\n", para->code_unicode, i, (CHAR *)data->return_text, (CHAR *)*para->text); @@ -1106,8 +1092,8 @@ static void test_wm_notify_header(HWND pager) HD_TEXTFILTERW hdtf = {0}; hdi.mask = HDI_TEXT | HDI_FILTER; - hdi.pszText = test_w; - hdtf.pszText = test_w; + hdi.pszText = (WCHAR *)test_w; + hdtf.pszText = (WCHAR *)test_w; nmh.pitem = &hdi; nmh.pitem->pvFilter = &hdtf; send_notify(pager, HDN_BEGINDRAG, HDN_BEGINDRAG, (LPARAM)&nmh, TRUE); @@ -1142,7 +1128,7 @@ static void test_wm_notify_tooltip(HWND pager) memset(&nmttdi, 0, sizeof(nmttdi)); if (data->send_sztext) memcpy(nmttdi.szText, data->send_sztext, data->send_sztext_size); - if (data->send_lpsztext) nmttdi.lpszText = data->send_lpsztext; + if (data->send_lpsztext) nmttdi.lpszText = (WCHAR *)data->send_lpsztext; send_notify(pager, TTN_GETDISPINFOW, TTN_GETDISPINFOA, (LPARAM)&nmttdi, FALSE); if (data->return_sztext) { diff --git a/modules/rostests/winetests/comctl32/precomp.h b/modules/rostests/winetests/comctl32/precomp.h index 046bc865207..eb5b9266e3b 100644 --- a/modules/rostests/winetests/comctl32/precomp.h +++ b/modules/rostests/winetests/comctl32/precomp.h @@ -23,4 +23,13 @@ #include "resources.h" #include "v6util.h" +#ifdef __REACTOS__ +#include + +#define WM_KEYF1 0x004d +#define WM_CTLCOLOR 0x0019 + +#define WC_DIALOG (MAKEINTATOM(0x8002)) +#endif + #endif /* !_COMCTL32_WINETEST_PRECOMP_H_ */ diff --git a/modules/rostests/winetests/comctl32/propsheet.c b/modules/rostests/winetests/comctl32/propsheet.c index 7d9da3f72b0..380c2c79c5e 100644 --- a/modules/rostests/winetests/comctl32/propsheet.c +++ b/modules/rostests/winetests/comctl32/propsheet.c @@ -23,11 +23,11 @@ #include "msg.h" #include "resources.h" - #include "wine/test.h" #ifdef __REACTOS__ -#include +#undef WC_DIALOG +#define WC_DIALOG (MAKEINTATOM(0x8002)) #endif static HWND parenthwnd; @@ -1168,10 +1168,8 @@ static void test_bad_control_class(void) psh.hwndParent = GetDesktopWindow(); U3(psh).phpage = &hpsp; -#ifndef __REACTOS__ /* FIXME: Inspect why this causes a hang */ ret = pPropertySheetA(&psh); ok(ret == 0, "got %ld\n", ret); -#endif /* Need to recreate hpsp otherwise the test fails under Windows */ hpsp = pCreatePropertySheetPageA(&psp); diff --git a/modules/rostests/winetests/comctl32/rebar.c b/modules/rostests/winetests/comctl32/rebar.c index 6b4a71b0542..6c82295d40b 100644 --- a/modules/rostests/winetests/comctl32/rebar.c +++ b/modules/rostests/winetests/comctl32/rebar.c @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +/* make sure the structures work with a comctl32 v5.x */ +#define _WIN32_WINNT 0x500 +#define _WIN32_IE 0x500 + #include #include @@ -495,6 +499,7 @@ static void test_layout(void) REBARBANDINFOA rbi; HIMAGELIST himl; REBARINFO ri; + int count; rbsize_results_init(); @@ -656,9 +661,27 @@ static void test_layout(void) SendMessageA(hRebar, RB_INSERTBANDA, -1, (LPARAM)&rbi); check_sizes(); - rbsize_results_free(); DestroyWindow(hRebar); pImageList_Destroy(himl); + + /* One hidden band. */ + hRebar = create_rebar_control(); + + rbi.cbSize = REBARBANDINFOA_V6_SIZE; + rbi.fMask = RBBIM_STYLE | RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_CHILD; + rbi.fStyle = RBBS_HIDDEN; + rbi.cx = 200; + rbi.cxMinChild = 100; + rbi.cyMinChild = 30; + rbi.hwndChild = NULL; + + SendMessageA(hRebar, RB_INSERTBANDA, -1, (LPARAM)&rbi); + count = SendMessageA(hRebar, RB_GETROWCOUNT, 0, 0); + ok(!count, "Unexpected row count %d.\n", count); + + DestroyWindow(hRebar); + + rbsize_results_free(); } #if 0 /* use this to generate more tests */ diff --git a/modules/rostests/winetests/comctl32/resources.h b/modules/rostests/winetests/comctl32/resources.h index 53522c0a09d..af136183af3 100644 --- a/modules/rostests/winetests/comctl32/resources.h +++ b/modules/rostests/winetests/comctl32/resources.h @@ -49,4 +49,5 @@ #define IDC_PS_COMBO1 1020 #define IDC_PS_PUSHBUTTON1 1021 + #endif /* __WINE_COMCTL32_TEST_RESOURCES_H */ diff --git a/modules/rostests/winetests/comctl32/static.c b/modules/rostests/winetests/comctl32/static.c index 1cc7e8a01df..a65e3527282 100644 --- a/modules/rostests/winetests/comctl32/static.c +++ b/modules/rostests/winetests/comctl32/static.c @@ -20,9 +20,7 @@ #include #include -#ifndef __REACTOS__ #define STRICT -#endif #define WIN32_LEAN_AND_MEAN #include #include "commctrl.h" diff --git a/modules/rostests/winetests/comctl32/subclass.c b/modules/rostests/winetests/comctl32/subclass.c index b2b47319b16..fc778d67115 100644 --- a/modules/rostests/winetests/comctl32/subclass.c +++ b/modules/rostests/winetests/comctl32/subclass.c @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#define _WIN32_WINNT 0x0501 /* For SetWindowSubclass/etc */ + #include #include diff --git a/modules/rostests/winetests/comctl32/taskdialog.c b/modules/rostests/winetests/comctl32/taskdialog.c index 35dde1cce5b..b0e744fdd72 100644 --- a/modules/rostests/winetests/comctl32/taskdialog.c +++ b/modules/rostests/winetests/comctl32/taskdialog.c @@ -30,6 +30,7 @@ #include "msg.h" #ifdef __REACTOS__ +#undef WM_KEYF1 #define WM_KEYF1 0x004d #endif diff --git a/modules/rostests/winetests/comctl32/toolbar.c b/modules/rostests/winetests/comctl32/toolbar.c index 41807258f4f..3688394d715 100644 --- a/modules/rostests/winetests/comctl32/toolbar.c +++ b/modules/rostests/winetests/comctl32/toolbar.c @@ -159,6 +159,7 @@ static LRESULT parent_wnd_notify(LPARAM lParam) break; case TBN_GETINFOTIPA: + case TBN_GETINFOTIPW: { NMTBGETINFOTIPA *tbgit = (NMTBGETINFOTIPA*)lParam; @@ -2029,6 +2030,9 @@ static void test_tooltip(void) g_ResetDispTextPtr = TRUE; SendMessageA(hToolbar, WM_NOTIFY, 0, (LPARAM)&nmtti); + /* Same for TBN_GETINFOTIPW */ + SendMessageA(hToolbar, TB_SETUNICODEFORMAT, TRUE, 0); + SendMessageA(hToolbar, WM_NOTIFY, 0, (LPARAM)&nmtti); g_ResetDispTextPtr = FALSE; DestroyWindow(hToolbar); diff --git a/modules/rostests/winetests/comctl32/tooltips.c b/modules/rostests/winetests/comctl32/tooltips.c index 0cb7afd6880..6156bffa13b 100644 --- a/modules/rostests/winetests/comctl32/tooltips.c +++ b/modules/rostests/winetests/comctl32/tooltips.c @@ -197,7 +197,7 @@ static void test_customdraw(void) { 50, 50, 300, 300, NULL, NULL, NULL, 0); - ok(parent != NULL, "Creation of main window failed\n"); + ok(parent != NULL, "%d: Creation of main window failed\n", iterationNumber); /* Make it show */ ShowWindow(parent, SW_SHOWNORMAL); @@ -209,7 +209,7 @@ static void test_customdraw(void) { CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, parent, NULL, GetModuleHandleA(NULL), 0); - ok(hwndTip != NULL, "Creation of tooltip window failed\n"); + ok(hwndTip != NULL, "%d: Creation of tooltip window failed\n", iterationNumber); /* Set up parms for the wndproc to handle */ CD_Stages = 0; @@ -230,7 +230,7 @@ static void test_customdraw(void) { toolInfo.lParam = 0xdeadbeef; GetClientRect (parent, &toolInfo.rect); ret = SendMessageA(hwndTip, TTM_ADDTOOLA, 0, (LPARAM)&toolInfo); - ok(ret, "Failed to add the tool.\n"); + ok(ret, "%d: Failed to add the tool.\n", iterationNumber); /* Make tooltip appear quickly */ SendMessageA(hwndTip, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(1,0)); @@ -245,23 +245,23 @@ static void test_customdraw(void) { /* Check CustomDraw results */ ok(CD_Stages == expectedResults[iterationNumber].ExpectedCalls || broken(CD_Stages == (expectedResults[iterationNumber].ExpectedCalls & ~TEST_CDDS_POSTPAINT)), /* nt4 */ - "CustomDraw run %d stages %x, expected %x\n", iterationNumber, CD_Stages, + "%d: CustomDraw stages %x, expected %x\n", iterationNumber, CD_Stages, expectedResults[iterationNumber].ExpectedCalls); } ret = SendMessageA(hwndTip, TTM_GETCURRENTTOOLA, 0, 0); - ok(ret, "Failed to get current tool %#lx.\n", ret); + ok(ret, "%d: Failed to get current tool %#lx.\n", iterationNumber, ret); memset(&toolInfo, 0xcc, sizeof(toolInfo)); toolInfo.cbSize = sizeof(toolInfo); toolInfo.lpszText = NULL; toolInfo.lpReserved = (void *)0xdeadbeef; SendMessageA(hwndTip, TTM_GETCURRENTTOOLA, 0, (LPARAM)&toolInfo); - ok(toolInfo.hwnd == parent, "Unexpected hwnd %p.\n", toolInfo.hwnd); - ok(toolInfo.hinst == GetModuleHandleA(NULL), "Unexpected hinst %p.\n", toolInfo.hinst); - ok(toolInfo.uId == 0x1234abcd, "Unexpected uId %lx.\n", toolInfo.uId); - ok(toolInfo.lParam == 0, "Unexpected lParam %lx.\n", toolInfo.lParam); - ok(toolInfo.lpReserved == (void *)0xdeadbeef, "Unexpected lpReserved %p.\n", toolInfo.lpReserved); + ok(toolInfo.hwnd == parent, "%d: Unexpected hwnd %p.\n", iterationNumber, toolInfo.hwnd); + ok(toolInfo.hinst == GetModuleHandleA(NULL), "%d: Unexpected hinst %p.\n", iterationNumber, toolInfo.hinst); + ok(toolInfo.uId == 0x1234abcd, "%d: Unexpected uId %lx.\n", iterationNumber, toolInfo.uId); + ok(toolInfo.lParam == 0, "%d: Unexpected lParam %lx.\n", iterationNumber, toolInfo.lParam); + ok(toolInfo.lpReserved == (void *)0xdeadbeef, "%d: Unexpected lpReserved %p.\n", iterationNumber, toolInfo.lpReserved); /* Clean up */ DestroyWindow(hwndTip); diff --git a/modules/rostests/winetests/comctl32/treeview.c b/modules/rostests/winetests/comctl32/treeview.c index ff76a83326d..2985ad4811a 100644 --- a/modules/rostests/winetests/comctl32/treeview.c +++ b/modules/rostests/winetests/comctl32/treeview.c @@ -42,6 +42,8 @@ static BOOL g_get_rect_in_expand; static BOOL g_disp_A_to_W; static BOOL g_disp_set_stateimage; static BOOL g_beginedit_alter_text; +static const char *g_endedit_overwrite_contents; +static char *g_endedit_overwrite_ptr; static HFONT g_customdraw_font; static BOOL g_v6; @@ -1320,7 +1322,19 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam, break; } - case TVN_ENDLABELEDITA: return TRUE; + case TVN_ENDLABELEDITA: + { + NMTVDISPINFOA *disp = (NMTVDISPINFOA *)lParam; + if (disp->item.mask & TVIF_TEXT) + { + ok(disp->item.cchTextMax == MAX_PATH, "cchTextMax is %d\n", disp->item.cchTextMax); + if (g_endedit_overwrite_contents) + strcpy(disp->item.pszText, g_endedit_overwrite_contents); + if (g_endedit_overwrite_ptr) + disp->item.pszText = g_endedit_overwrite_ptr; + } + return TRUE; + } case TVN_ITEMEXPANDINGA: { UINT newmask = pTreeView->itemNew.mask & ~TVIF_CHILDREN; @@ -1576,7 +1590,7 @@ static void test_itemedit(void) DWORD r; HWND edit; TVITEMA item; - CHAR buffA[20]; + CHAR buffA[500]; HWND hTree; hTree = create_treeview_control(0); @@ -1667,6 +1681,84 @@ static void test_itemedit(void) GetWindowTextA(edit, buffA, ARRAY_SIZE(buffA)); ok(!strcmp(buffA, ""), "got string %s\n", buffA); + r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit); + expect(0, r); + + /* How much text can be typed? */ + edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot); + ok(IsWindow(edit), "Expected valid handle\n"); + r = SendMessageA(edit, EM_GETLIMITTEXT, 0, 0); + expect(MAX_PATH - 1, r); + /* WM_SETTEXT can set more... */ + memset(buffA, 'a', ARRAY_SIZE(buffA)); + buffA[ARRAY_SIZE(buffA)-1] = 0; + r = SetWindowTextA(edit, buffA); + expect(TRUE, r); + r = GetWindowTextA(edit, buffA, ARRAY_SIZE(buffA)); + ok( r == ARRAY_SIZE(buffA) - 1, "got %d\n", r ); + /* ...but it's trimmed to MAX_PATH chars when editing ends */ + r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit); + expect(0, r); + item.mask = TVIF_TEXT; + item.hItem = hRoot; + item.pszText = buffA; + item.cchTextMax = ARRAY_SIZE(buffA); + r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, r); + expect(MAX_PATH - 1, lstrlenA(item.pszText)); + + /* We can't get around that MAX_PATH limit by increasing EM_SETLIMITTEXT */ + edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot); + ok(IsWindow(edit), "Expected valid handle\n"); + SendMessageA(edit, EM_SETLIMITTEXT, ARRAY_SIZE(buffA)-1, 0); + memset(buffA, 'a', ARRAY_SIZE(buffA)); + buffA[ARRAY_SIZE(buffA)-1] = 0; + r = SetWindowTextA(edit, buffA); + expect(TRUE, r); + r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit); + expect(0, r); + item.mask = TVIF_TEXT; + item.hItem = hRoot; + item.pszText = buffA; + item.cchTextMax = ARRAY_SIZE(buffA); + r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, r); + expect(MAX_PATH - 1, lstrlenA(item.pszText)); + + /* Overwriting of pszText contents in TVN_ENDLABELEDIT */ + edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot); + ok(IsWindow(edit), "Expected valid handle\n"); + r = SetWindowTextA(edit, "old"); + expect(TRUE, r); + g_endedit_overwrite_contents = ""; + r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit); + expect(0, r); + g_endedit_overwrite_contents = NULL; + item.mask = TVIF_TEXT; + item.hItem = hRoot; + item.pszText = buffA; + item.cchTextMax = ARRAY_SIZE(buffA); + r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, r); + expect(0, strcmp(item.pszText, "")); + + /* Overwriting of pszText pointer in TVN_ENDLABELEDIT */ + edit = (HWND)SendMessageA(hTree, TVM_EDITLABELA, 0, (LPARAM)hRoot); + ok(IsWindow(edit), "Expected valid handle\n"); + r = SetWindowTextA(edit, "old"); + expect(TRUE, r); + g_endedit_overwrite_ptr = (char*) ""; + r = SendMessageA(hTree, WM_COMMAND, MAKEWPARAM(0, EN_KILLFOCUS), (LPARAM)edit); + expect(0, r); + g_endedit_overwrite_ptr = NULL; + item.mask = TVIF_TEXT; + item.hItem = hRoot; + item.pszText = buffA; + item.cchTextMax = ARRAY_SIZE(buffA); + r = SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&item); + expect(TRUE, r); + expect(0, strcmp(item.pszText, "")); + DestroyWindow(hTree); } diff --git a/modules/rostests/winetests/comctl32/updown.c b/modules/rostests/winetests/comctl32/updown.c index e7ceeffd4cd..d0fff562499 100644 --- a/modules/rostests/winetests/comctl32/updown.c +++ b/modules/rostests/winetests/comctl32/updown.c @@ -791,7 +791,7 @@ static void test_updown_create(void) flush_sequences(sequences, NUM_MSG_SEQUENCES); GetWindowTextA(g_edit, text, MAX_PATH); - ok(lstrlenA(text) == 0, "Expected empty string\n"); + ok(!*text, "Expected empty string\n"); ok_sequence(sequences, EDIT_SEQ_INDEX, get_edit_text_seq, "get edit text", FALSE); DestroyWindow(updown); @@ -865,7 +865,7 @@ static void test_UDS_SETBUDDYINT(void) ok(style & UDS_SETBUDDYINT, "Expected UDS_SETBUDDY to be set\n"); SendMessageA(updown, UDM_SETPOS, 0, 20); GetWindowTextA(g_edit, text, ARRAY_SIZE(text)); - ok(lstrlenA(text) == 0, "Expected empty string\n"); + ok(!*text, "Expected empty string\n"); DestroyWindow(updown); /* creating with UDS_SETBUDDYINT */ diff --git a/modules/rostests/winetests/comctl32/v6util.h b/modules/rostests/winetests/comctl32/v6util.h index 8f547a070a2..84d20220823 100644 --- a/modules/rostests/winetests/comctl32/v6util.h +++ b/modules/rostests/winetests/comctl32/v6util.h @@ -20,7 +20,9 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#ifdef __REACTOS__ #pragma once +#endif #ifdef __i386__ #define ARCH "x86" @@ -60,7 +62,7 @@ static const CHAR manifest[] = "\n" "\n"; -static inline void unload_v6_module(ULONG_PTR cookie, HANDLE hCtx) +static void unload_v6_module(ULONG_PTR cookie, HANDLE hCtx) { DeactivateActCtx(0, cookie); ReleaseActCtx(hCtx); @@ -68,7 +70,7 @@ static inline void unload_v6_module(ULONG_PTR cookie, HANDLE hCtx) DeleteFileA(manifest_name); } -static inline BOOL load_v6_module(ULONG_PTR *pcookie, HANDLE *hCtx) +static BOOL load_v6_module(ULONG_PTR *pcookie, HANDLE *hCtx) { ACTCTX_SECTION_KEYED_DATA data; DWORD written; diff --git a/sdk/include/psdk/winuser.h b/sdk/include/psdk/winuser.h index 9d95a817163..442add4548e 100644 --- a/sdk/include/psdk/winuser.h +++ b/sdk/include/psdk/winuser.h @@ -714,6 +714,9 @@ extern "C" { #define IDI_EXCLAMATION 32515 #define IDI_ASTERISK 32516 #define IDI_WINLOGO 32517 +#if(WINVER >= 0x0600) +#define IDI_SHIELD 32518 +#endif /* WINVER >= 0x0600 */ #endif #define IDI_WARNING IDI_EXCLAMATION #define IDI_ERROR IDI_HAND diff --git a/sdk/tools/winesync/comctl32.cfg b/sdk/tools/winesync/comctl32.cfg new file mode 100644 index 00000000000..5a614d7bbf3 --- /dev/null +++ b/sdk/tools/winesync/comctl32.cfg @@ -0,0 +1,5 @@ +directories: + dlls/comctl32: dll/win32/comctl32 +files: null +tags: + wine: wine-5.0