For SOME reason comctl32 has been synched manually multiple times to different versions and different pots
This PR aims to fix that
With the exception of button.c which all in all is a massive fork over wines code entirely.
and datetime.c which is at wine 6.0
Comctl32 is now at wine-5.0
typedef struct _BUTTON_INFO
{
HWND hwnd;
+ HWND parent;
LONG state;
HFONT font;
+ WCHAR *note;
+ INT note_length;
union
{
HICON icon;
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.
*/
case WM_NCDESTROY:
SetWindowLongPtrW( hWnd, 0, 0 );
+ heap_free(infoPtr->note);
heap_free(infoPtr);
break;
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);
#include "uxtheme.h"
#include "vssym32.h"
#include "commctrl.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/heap.h"
#define ID_CB_LISTBOX 1000
#define ID_CB_EDIT 1001
+static void CBCalcPlacement(HEADCOMBO *combo);
+static void CBResetPos(HEADCOMBO *combo);
+
/***********************************************************************
* COMBO_Init
*
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
*
* This height was determined through experimentation.
* CBCalcPlacement will add 2*COMBO_YBORDERSIZE pixels for the border
*/
-static INT CBGetTextAreaHeight(
- HWND hwnd,
- LPHEADCOMBO lphc)
+static INT CBGetTextAreaHeight(HEADCOMBO *lphc, BOOL clip_item_height)
{
- INT iTextItemHeight;
+ INT item_height, text_height;
- if( lphc->editHeight ) /* explicitly set height */
+ if (clip_item_height && !CB_OWNERDRAWN(lphc))
{
- iTextItemHeight = lphc->editHeight;
+ text_height = combo_get_text_height(lphc);
+ if (lphc->item_height < text_height)
+ lphc->item_height = text_height;
}
- else
- {
- TEXTMETRICW tm;
- HDC hDC = GetDC(hwnd);
- HFONT hPrevFont = 0;
- INT baseUnitY;
-
- if (lphc->hFont)
- hPrevFont = SelectObject( hDC, lphc->hFont );
-
- GetTextMetricsW(hDC, &tm);
-
- baseUnitY = tm.tmHeight;
-
- if( hPrevFont )
- SelectObject( hDC, hPrevFont );
-
- ReleaseDC(hwnd, hDC);
+ item_height = lphc->item_height;
- iTextItemHeight = baseUnitY + 4;
- }
/*
* Check the ownerdraw case if we haven't asked the parent the size
{
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;
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
/*
* Keep the size for the next time
*/
- lphc->editHeight = iTextItemHeight;
+ lphc->item_height = item_height;
}
- return iTextItemHeight;
+ return item_height;
}
/***********************************************************************
* 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);
* 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);
}
/***********************************************************************
*
* Set up component coordinates given valid lphc->RectCombo.
*/
-static void CBCalcPlacement(
- HWND hwnd,
- LPHEADCOMBO lphc,
- LPRECT lprEdit,
- LPRECT lprButton,
- LPRECT lprLB)
+static void CBCalcPlacement(HEADCOMBO *combo)
{
- /*
- * Again, start with the client rectangle.
- */
- GetClientRect(hwnd, lprEdit);
+ /* Start with the client rectangle. */
+ GetClientRect(combo->self, &combo->textRect);
- /*
- * Remove the borders
- */
- InflateRect(lprEdit, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
+ /* Remove the borders */
+ InflateRect(&combo->textRect, -COMBO_XBORDERSIZE(), -COMBO_YBORDERSIZE());
- /*
- * Chop off the bottom part to fit with the height of the text area.
- */
- lprEdit->bottom = lprEdit->top + CBGetTextAreaHeight(hwnd, lphc);
-
- /*
- * The button starts the same vertical position as the text area.
- */
- CopyRect(lprButton, lprEdit);
+ /* Chop off the bottom part to fit with the height of the text area. */
+ combo->textRect.bottom = combo->textRect.top + CBGetTextAreaHeight(combo, FALSE);
- /*
- * If the combobox is "simple" there is no button.
- */
- if( CB_GETTYPE(lphc) == CBS_SIMPLE )
- lprButton->left = lprButton->right = lprButton->bottom = 0;
- else
- {
- /*
- * Let's assume the combobox button is the same width as the
- * scrollbar button.
- * size the button horizontally and cut-off the text area.
- */
- lprButton->left = lprButton->right - GetSystemMetrics(SM_CXVSCROLL);
- lprEdit->right = lprButton->left;
- }
+ /* The button starts the same vertical position as the text area. */
+ combo->buttonRect = combo->textRect;
- /*
- * In the case of a dropdown, there is an additional spacing between the
- * text area and the button.
- */
- if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
- {
- lprEdit->right -= COMBO_EDITBUTTONSPACE();
- }
+ /* If the combobox is "simple" there is no button. */
+ if (CB_GETTYPE(combo) == CBS_SIMPLE)
+ combo->buttonRect.left = combo->buttonRect.right = combo->buttonRect.bottom = 0;
+ else
+ {
+ /*
+ * Let's assume the combobox button is the same width as the
+ * scrollbar button.
+ * size the button horizontally and cut-off the text area.
+ */
+ combo->buttonRect.left = combo->buttonRect.right - GetSystemMetrics(SM_CXVSCROLL);
+ combo->textRect.right = combo->buttonRect.left;
+ }
- /*
- * If we have an edit control, we space it away from the borders slightly.
- */
- if (CB_GETTYPE(lphc) != CBS_DROPDOWNLIST)
- {
- InflateRect(lprEdit, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
- }
+ /* In the case of a dropdown, there is an additional spacing between the text area and the button. */
+ if (CB_GETTYPE(combo) == CBS_DROPDOWN)
+ combo->textRect.right -= COMBO_EDITBUTTONSPACE();
- /*
- * Adjust the size of the listbox popup.
- */
- if( CB_GETTYPE(lphc) == CBS_SIMPLE )
- {
- /*
- * Use the client rectangle to initialize the listbox rectangle
- */
- GetClientRect(hwnd, lprLB);
+ /* If we have an edit control, we space it away from the borders slightly. */
+ if (CB_GETTYPE(combo) != CBS_DROPDOWNLIST)
+ InflateRect(&combo->textRect, -EDIT_CONTROL_PADDING(), -EDIT_CONTROL_PADDING());
- /*
- * Then, chop-off the top part.
- */
- lprLB->top = lprEdit->bottom + COMBO_YBORDERSIZE();
- }
- else
- {
- /*
- * Make sure the dropped width is as large as the combobox itself.
- */
- if (lphc->droppedWidth < (lprButton->right + COMBO_XBORDERSIZE()))
+ /* Adjust the size of the listbox popup. */
+ if (CB_GETTYPE(combo) == CBS_SIMPLE)
{
- lprLB->right = lprLB->left + (lprButton->right + COMBO_XBORDERSIZE());
-
- /*
- * In the case of a dropdown, the popup listbox is offset to the right.
- * so, we want to make sure it's flush with the right side of the
- * combobox
- */
- if( CB_GETTYPE(lphc) == CBS_DROPDOWN )
- lprLB->right -= COMBO_EDITBUTTONSPACE();
+ GetClientRect(combo->self, &combo->droppedRect);
+ combo->droppedRect.top = combo->textRect.bottom + COMBO_YBORDERSIZE();
}
else
- lprLB->right = lprLB->left + lphc->droppedWidth;
- }
-
- /* don't allow negative window width */
- if (lprEdit->right < lprEdit->left)
- lprEdit->right = lprEdit->left;
+ {
+ /* Make sure the dropped width is as large as the combobox itself. */
+ if (combo->droppedWidth < (combo->buttonRect.right + COMBO_XBORDERSIZE()))
+ {
+ combo->droppedRect.right = combo->droppedRect.left + (combo->buttonRect.right + COMBO_XBORDERSIZE());
- TRACE("\ttext\t= (%s)\n", wine_dbgstr_rect(lprEdit));
+ /* In the case of a dropdown, the popup listbox is offset to the right. We want to make sure it's flush
+ with the right side of the combobox */
+ if (CB_GETTYPE(combo) == CBS_DROPDOWN)
+ combo->droppedRect.right -= COMBO_EDITBUTTONSPACE();
+ }
+ else
+ combo->droppedRect.right = combo->droppedRect.left + combo->droppedWidth;
+ }
- TRACE("\tbutton\t= (%s)\n", wine_dbgstr_rect(lprButton));
+ /* Disallow negative window width */
+ if (combo->textRect.right < combo->textRect.left)
+ combo->textRect.right = combo->textRect.left;
- TRACE("\tlbox\t= (%s)\n", wine_dbgstr_rect(lprLB));
+ TRACE("text %s, button %s, lbox %s.\n", wine_dbgstr_rect(&combo->textRect), wine_dbgstr_rect(&combo->buttonRect),
+ wine_dbgstr_rect(&combo->droppedRect));
}
/***********************************************************************
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
* 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
*
* 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;
if (CB_DISABLED(lphc))
buttonState |= DFCS_INACTIVE;
- DrawFrameControl(hdc, &rectButton, DFC_SCROLL, buttonState);
+ DrawFrameControl(hdc, &lphc->buttonRect, DFC_SCROLL, buttonState);
}
/***********************************************************************
/***********************************************************************
* 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
{
/*
* 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)
* This function sets window positions according to the updated
* component placement struct.
*/
-static void CBResetPos(
- LPHEADCOMBO lphc,
- const RECT *rectEdit,
- const RECT *rectLB,
- BOOL bRedraw)
+static void CBResetPos(HEADCOMBO *combo)
{
- BOOL bDrop = (CB_GETTYPE(lphc) != CBS_SIMPLE);
-
- /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
- * sizing messages */
-
- if( lphc->wState & CBF_EDIT )
- SetWindowPos( lphc->hWndEdit, 0,
- rectEdit->left, rectEdit->top,
- rectEdit->right - rectEdit->left,
- rectEdit->bottom - rectEdit->top,
- SWP_NOZORDER | SWP_NOACTIVATE | ((bDrop) ? SWP_NOREDRAW : 0) );
-
- SetWindowPos( lphc->hWndLBox, 0,
- rectLB->left, rectLB->top,
- rectLB->right - rectLB->left,
- rectLB->bottom - rectLB->top,
- SWP_NOACTIVATE | SWP_NOZORDER | ((bDrop) ? SWP_NOREDRAW : 0) );
-
- if( bDrop )
- {
- if( lphc->wState & CBF_DROPPED )
- {
- lphc->wState &= ~CBF_DROPPED;
- ShowWindow( lphc->hWndLBox, SW_HIDE );
- }
+ BOOL drop = CB_GETTYPE(combo) != CBS_SIMPLE;
+
+ /* NOTE: logs sometimes have WM_LBUTTONUP before a cascade of
+ * sizing messages */
+ if (combo->wState & CBF_EDIT)
+ SetWindowPos(combo->hWndEdit, 0, combo->textRect.left, combo->textRect.top,
+ combo->textRect.right - combo->textRect.left,
+ combo->textRect.bottom - combo->textRect.top,
+ SWP_NOZORDER | SWP_NOACTIVATE | (drop ? SWP_NOREDRAW : 0));
+
+ SetWindowPos(combo->hWndLBox, 0, combo->droppedRect.left, combo->droppedRect.top,
+ combo->droppedRect.right - combo->droppedRect.left,
+ combo->droppedRect.bottom - combo->droppedRect.top,
+ SWP_NOACTIVATE | SWP_NOZORDER | (drop ? SWP_NOREDRAW : 0));
+
+ if (drop)
+ {
+ if (combo->wState & CBF_DROPPED)
+ {
+ combo->wState &= ~CBF_DROPPED;
+ ShowWindow(combo->hWndLBox, SW_HIDE);
+ }
- if( bRedraw && !(lphc->wState & CBF_NOREDRAW) )
- RedrawWindow( lphc->self, NULL, 0,
- RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW );
- }
+ if (!(combo->wState & CBF_NOREDRAW))
+ RedrawWindow(combo->self, NULL, 0, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW);
+ }
}
/***********************************************************************
* COMBO_Size
*/
-static void COMBO_Size( LPHEADCOMBO lphc )
+static void COMBO_Size( HEADCOMBO *lphc )
{
+ if (!lphc->hWndLBox || (lphc->wState & CBF_NORESIZE))
+ return;
+
/*
* Those controls are always the same height. So we have to make sure
* they are not resized to another value.
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
/*
* 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);
}
*/
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.
*/
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
{
{
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
{
}
case WM_SIZE:
- if (lphc->hWndLBox && !(lphc->wState & CBF_NORESIZE))
- COMBO_Size( lphc );
+ COMBO_Size( lphc );
return TRUE;
case WM_SETFONT:
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);
lphc->droppedWidth = 0;
/* recalculate the combobox area */
- CBCalcPlacement(hwnd, lphc, &lphc->textRect, &lphc->buttonRect, &lphc->droppedRect );
+ CBCalcPlacement(lphc);
/* fall through */
case CB_GETDROPPEDWIDTH:
#include "commctrl.h"
#include "comctl32.h"
#include "wine/debug.h"
-#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(comboex);
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;
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;
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)) {
#define IDT_CHECK 401
+/* Command Link arrow */
+#define IDB_CMDLINK 402
+
/* Cursors */
#define IDC_MOVEBUTTON 102
#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
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;
#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 */
* COMCTL32.DLL (internally).
*
*/
-#include "config.h"
-#include "wine/port.h"
#include <stdarg.h>
#include <string.h>
#include "objbase.h"
#include "winerror.h"
-#include "wine/unicode.h"
#include "comctl32.h"
#include "wine/debug.h"
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);
}
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];
}
return AddMRUData(hList, lpszString,
- (strlenW(lpszString) + 1) * sizeof(WCHAR));
+ (lstrlenW(lpszString) + 1) * sizeof(WCHAR));
}
/**************************************************************************
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);
{
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;
} 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);
}
*
* 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
*
* 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
*
*/
-#include "config.h"
-
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include "commctrl.h"
#include "uxtheme.h"
#include "vsstyle.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/heap.h"
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
*/
*/
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));
}
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;
}
/* 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;
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)
}
}
- if(es->flags & EF_FOCUSED)
EDIT_SetCaretPos(es, es->selection_end, es->flags & EF_AFTER_WRAP);
}
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));
+ }
}
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) {
/* 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];
/* 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);
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);
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 *);
/*********************************************************************
*
* 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)
{
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);
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));
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;
return 0;
lstrcpynW(dst, es->text, count);
- return strlenW(dst);
+ return lstrlenW(dst);
}
/*********************************************************************
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;
}
+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
HDC dc;
HFONT old_font = 0;
RECT clientRect;
+ DWORD margins;
es->font = font;
EDIT_InvalidateUniscribeData(es);
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);
/* 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);
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
{
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;
+ }
+}
+
/********************************************************************
*
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
SetWindowLongPtrW( es->hwndSelf, 0, 0 );
heap_free(es->undo_text);
+ heap_free(es->cue_banner_text);
heap_free(es);
return 0;
{
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;
}
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?
*/
case WM_MOUSEWHEEL:
{
int wheelDelta;
- UINT pulScrollLines = 3;
+ INT pulScrollLines = 3;
SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
if (wParam & (MK_SHIFT | MK_CONTROL))
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;
#include "windef.h"
#include "winbase.h"
-#include "wine/unicode.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
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))
{
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);
}
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;
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;
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;
#include "uxtheme.h"
#include "vsstyle.h"
#include "vssym32.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/heap.h"
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);
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++) {
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++;
}
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);
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * TODO:
- * - LBS_NODATA
*/
#include <string.h>
#include "commctrl.h"
#include "uxtheme.h"
#include "vssym32.h"
-#include "wine/unicode.h"
#include "wine/exception.h"
#include "wine/debug.h"
+#include "wine/heap.h"
#include "comctl32.h"
-WINE_DEFAULT_DEBUG_CHANNEL(listbox2);
+WINE_DEFAULT_DEBUG_CHANNEL(listbox);
-/* Items array granularity */
+/* Items array granularity (must be power of 2) */
#define LB_ARRAY_GRANULARITY 16
/* Scrolling timeout in ms */
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 */
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
*
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;
{
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)
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
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);
}
}
{
while (index < descr->nb_items)
{
- if ((pos += descr->items[index].height) > y) break;
+ if ((pos += get_item_height(descr, index)) > y) break;
index++;
}
}
while (index > 0)
{
index--;
- if ((pos -= descr->items[index].height) <= y) break;
+ if ((pos -= get_item_height(descr, index)) <= y) break;
}
}
}
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))
{
RECT r;
HRGN hrgn;
- if (!item)
+ if (index >= descr->nb_items)
{
if (action == ODA_FOCUS)
DrawFocusRect( hdc, 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 );
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 );
}
}
*/
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;
}
if (HAS_STRINGS(descr))
{
+ WCHAR *str = get_item_string(descr, index);
+
if (!buffer)
- return strlenW(descr->items[index].str);
+ return lstrlenW(str);
- TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(descr->items[index].str));
+ TRACE("index %d (0x%04x) %s\n", index, index, debugstr_w(str));
__TRY /* hide a Delphi bug that passes a read-only buffer */
{
- strcpyW( buffer, descr->items[index].str );
- len = strlenW(buffer);
+ lstrcpyW(buffer, str);
+ len = lstrlenW(buffer);
}
__EXCEPT_PAGE_FAULT
{
} 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;
}
{
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;
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;
}
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;
*/
static INT LISTBOX_FindString( LB_DESCR *descr, INT start, LPCWSTR str, BOOL exact )
{
- INT i;
- LB_ITEMDATA *item;
+ INT i, index;
+
+ if (descr->style & LBS_NODATA) return LB_ERR;
- if (start >= descr->nb_items) start = -1;
- item = descr->items + start + 1;
+ start++;
+ if (start >= descr->nb_items) start = 0;
if (HAS_STRINGS(descr))
{
if (!str || ! str[0] ) return LB_ERR;
if (exact)
{
- for (i = start + 1; i < descr->nb_items; i++, item++)
- if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
- for (i = 0, item = descr->items; i <= start; i++, item++)
- if (!LISTBOX_lstrcmpiW( descr->locale, str, item->str )) return i;
+ for (i = 0, index = start; i < descr->nb_items; i++, index++)
+ {
+ if (index == descr->nb_items) index = 0;
+ if (!LISTBOX_lstrcmpiW(descr->locale, str, get_item_string(descr, index)))
+ return index;
+ }
}
else
{
- /* Special case for drives and directories: ignore prefix */
-#define CHECK_DRIVE(item) \
- if ((item)->str[0] == '[') \
- { \
- if (!strncmpiW( str, (item)->str+1, len )) return i; \
- if (((item)->str[1] == '-') && !strncmpiW(str, (item)->str+2, len)) \
- return i; \
- }
+ /* Special case for drives and directories: ignore prefix */
+ INT len = lstrlenW(str);
+ WCHAR *item_str;
- INT len = strlenW(str);
- for (i = start + 1; i < descr->nb_items; i++, item++)
- {
- if (!strncmpiW( str, item->str, len )) return i;
- CHECK_DRIVE(item);
- }
- for (i = 0, item = descr->items; i <= start; i++, item++)
+ for (i = 0, index = start; i < descr->nb_items; i++, index++)
{
- if (!strncmpiW( str, item->str, len )) return i;
- CHECK_DRIVE(item);
+ if (index == descr->nb_items) index = 0;
+ item_str = get_item_string(descr, index);
+
+ if (!wcsnicmp(str, item_str, len)) return index;
+ if (item_str[0] == '[')
+ {
+ if (!wcsnicmp(str, item_str + 1, len)) return index;
+ if (item_str[1] == '-' && !wcsnicmp(str, item_str + 2, len)) return index;
+ }
}
-#undef CHECK_DRIVE
}
}
else
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;
}
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;
}
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;
}
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)
rect.right += descr->column_width;
rect.top = 0;
col_pos = descr->page_size - 1;
+ if (rect.left >= descr->width) break;
}
else
{
if (!theme || !(exstyle & WS_EX_CLIENTEDGE))
return;
- cxEdge = GetSystemMetrics(SM_CXEDGE),
+ cxEdge = GetSystemMetrics(SM_CXEDGE);
cyEdge = GetSystemMetrics(SM_CYEDGE);
GetWindowRect(descr->self, &r);
SetLastError(ERROR_INVALID_INDEX);
return LB_ERR;
}
- return descr->items[index].height;
+ return get_item_height(descr, index);
}
else return descr->item_height;
}
*/
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;
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 );
/***********************************************************************
* 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;
}
}
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
{
*/
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;
}
{
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);
}
}
{
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);
}
}
{
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 );
static LRESULT LISTBOX_InsertItem( LB_DESCR *descr, INT index,
LPWSTR str, ULONG_PTR data )
{
- LB_ITEMDATA *item;
- INT max_items;
INT oldfocus = descr->focus_item;
if (index == -1) index = descr->nb_items;
else if ((index < 0) || (index > descr->nb_items)) return LB_ERR;
- if (!descr->items) max_items = 0;
- else max_items = HeapSize( GetProcessHeap(), 0, descr->items ) / sizeof(*item);
- if (descr->nb_items == max_items)
- {
- /* We need to grow the array */
- max_items += LB_ARRAY_GRANULARITY;
- if (descr->items)
- item = HeapReAlloc( GetProcessHeap(), 0, descr->items,
- max_items * sizeof(LB_ITEMDATA) );
- else
- item = HeapAlloc( GetProcessHeap(), 0,
- max_items * sizeof(LB_ITEMDATA) );
- if (!item)
- {
- SEND_NOTIFICATION( descr, LBN_ERRSPACE );
- return LB_ERRSPACE;
- }
- descr->items = item;
- }
-
- /* Insert the item structure */
+ if (!resize_storage(descr, descr->nb_items + 1)) return LB_ERR;
- item = &descr->items[index];
- if (index < descr->nb_items)
- RtlMoveMemory( item + 1, item,
- (descr->nb_items - index) * sizeof(LB_ITEMDATA) );
- item->str = str;
- item->data = HAS_STRINGS(descr) ? 0 : data;
- item->height = 0;
- item->selected = FALSE;
+ insert_item_data(descr, index);
descr->nb_items++;
+ set_item_string(descr, index, str);
+ set_item_data(descr, index, HAS_STRINGS(descr) ? 0 : data);
+ set_item_height(descr, index, 0);
+ set_item_selected_state(descr, index, FALSE);
/* Get item height */
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 */
{
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;
*/
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 );
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) );
}
*/
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 );
{
INT i;
- for(i = descr->nb_items - 1; i>=0; i--) LISTBOX_DeleteItem( descr, i);
- HeapFree( GetProcessHeap(), 0, descr->items );
+ if (!(descr->style & LBS_NODATA))
+ for (i = descr->nb_items - 1; i >= 0; i--) LISTBOX_DeleteItem(descr, i);
+ HeapFree( GetProcessHeap(), 0, descr->u.items );
descr->nb_items = 0;
descr->top_item = 0;
descr->selected_item = -1;
descr->focus_item = 0;
descr->anchor_item = -1;
- descr->items = NULL;
+ descr->items_size = 0;
+ descr->u.items = NULL;
}
/***********************************************************************
* LISTBOX_SetCount
*/
-static LRESULT LISTBOX_SetCount( LB_DESCR *descr, INT count )
+static LRESULT LISTBOX_SetCount( LB_DESCR *descr, UINT count )
{
- LRESULT ret;
+ UINT orig_num = descr->nb_items;
- if (HAS_STRINGS(descr))
- {
- SetLastError(ERROR_SETCOUNT_ON_BAD_LB);
- return LB_ERR;
- }
+ if (!(descr->style & LBS_NODATA)) return LB_ERR;
- /* FIXME: this is far from optimal... */
- if (count > descr->nb_items)
- {
- while (count > descr->nb_items)
- if ((ret = LISTBOX_InsertString( descr, -1, 0 )) < 0)
- return ret;
- }
- else if (count < descr->nb_items)
+ if (!resize_storage(descr, count))
+ return LB_ERRSPACE;
+ descr->nb_items = count;
+
+ if (count)
{
- while (count < descr->nb_items)
- if ((ret = LISTBOX_RemoveItem( descr, (descr->nb_items - 1) )) < 0)
- return ret;
+ LISTBOX_UpdateScroll(descr);
+ if (count < orig_num)
+ {
+ descr->anchor_item = min(descr->anchor_item, count - 1);
+ if (descr->selected_item >= count)
+ descr->selected_item = -1;
+
+ /* If we removed the scrollbar, reset the top of the list */
+ if (count <= descr->page_size && orig_num > descr->page_size)
+ LISTBOX_SetTopItem(descr, 0, TRUE);
+
+ descr->focus_item = min(descr->focus_item, count - 1);
+ }
+
+ /* If it was empty before growing, set focus to the first item */
+ else if (orig_num == 0) LISTBOX_SetCaretIndex(descr, 0, FALSE);
}
+ else SendMessageW(descr->self, LB_RESETCONTENT, 0, 0);
InvalidateRect( descr->self, NULL, TRUE );
return LB_OKAY;
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 */
{
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 );
static LRESULT LISTBOX_HandleMouseWheel(LB_DESCR *descr, SHORT delta )
{
- UINT pulScrollLines = 3;
+ INT pulScrollLines = 3;
SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
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;
{
LISTBOX_SetCaretIndex( descr, index, FALSE );
LISTBOX_SetSelection( descr, index,
- !descr->items[index].selected,
+ !is_item_selected(descr, index),
(descr->style & LBS_NOTIFY) != 0);
}
else
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 );
}
}
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 */
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;
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;
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 */
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))
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;
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)
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;
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 *****************************************/
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;
/* 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;
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);
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
!(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;
}
* 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)
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 )
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;
/* 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;
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)
}
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;
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)
#include "comctl32.h"
#include "uxtheme.h"
#include "vssym32.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/heap.h"
/* 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 */
/* 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);
yearoffset = 0;
while (strW[yearoffset])
{
- if (!strncmpW(&strW[yearoffset], yearW, strlenW(yearW)))
+ if (!wcsncmp(&strW[yearoffset], yearW, lstrlenW(yearW)))
break;
yearoffset++;
}
monthoffset = 0;
while (strW[monthoffset])
{
- if (!strncmpW(&strW[monthoffset], monthW, strlenW(monthW)))
+ if (!wcsncmp(&strW[monthoffset], monthW, lstrlenW(monthW)))
break;
monthoffset++;
}
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);
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;
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);
}
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;
}
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 */
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
#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)
{
PAGER_Create (HWND hwnd, const CREATESTRUCTW *lpcs)
{
PAGER_INFO *infoPtr;
+ INT ret;
/* allocate memory for info structure */
infoPtr = heap_alloc_zero (sizeof(*infoPtr));
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;
}
PAGER_Destroy (PAGER_INFO *infoPtr)
{
SetWindowLongPtrW (infoPtr->hwndSelf, 0, 0);
+ heap_free (infoPtr->pwszBuffer);
heap_free (infoPtr);
return 0;
}
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)
{
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);
#include "uxtheme.h"
#include "wine/debug.h"
-#include "wine/unicode.h"
/******************************************************************************
* Data structures
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;
}
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)
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);
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;
}
}
- if(ret == 0)
- {
+ if(ret == 0 && msg.message)
PostQuitMessage(msg.wParam);
- ret = -1;
- }
if(ret != -1)
ret = psInfo->result;
}
else
{
- int len = (strlenW(str) + 1) * sizeof(WCHAR);
+ int len = (lstrlenW(str) + 1) * sizeof(WCHAR);
ret = Alloc( len );
if (ret) memcpy( ret, str, len );
}
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
-#include "wine/unicode.h"
#include "winuser.h"
#include "winnls.h"
#include "commctrl.h"
"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[] = {
"RBBIM_IDEALSIZE", /* 0x00000200 */
"RBBIM_LPARAM", /* 0x00000400 */
"RBBIM_HEADERSIZE", /* 0x00000800 */
+ "RBBIM_CHEVRONLOCATION", /* 0x00001000 */
+ "RBBIM_CHEVRONSTATE", /* 0x00002000 */
NULL };
-static CHAR line[200];
-
static const WCHAR themeClass[] = { 'R','e','b','a','r',0 };
static CHAR *
-REBAR_FmtStyle( UINT style)
+REBAR_FmtStyle(char *buffer, UINT style)
{
INT i = 0;
- *line = 0;
+ *buffer = 0;
while (band_stylename[i]) {
if (style & (1<<i)) {
- if (*line != 0) strcat(line, " | ");
- strcat(line, band_stylename[i]);
+ if (*buffer) strcat(buffer, " | ");
+ strcat(buffer, band_stylename[i]);
}
i++;
}
- return line;
+ return buffer;
}
static CHAR *
-REBAR_FmtMask( UINT mask)
+REBAR_FmtMask(char *buffer, UINT mask)
{
INT i = 0;
- *line = 0;
+ *buffer = 0;
while (band_maskname[i]) {
if (mask & (1<<i)) {
- if (*line != 0) strcat(line, " | ");
- strcat(line, band_maskname[i]);
+ if (*buffer) strcat(buffer, " | ");
+ strcat(buffer, band_maskname[i]);
}
i++;
}
- return line;
+ return buffer;
}
static VOID
REBAR_DumpBandInfo(const REBARBANDINFOW *pB)
{
+ char buff[300];
+
if( !TRACE_ON(rebar) ) return;
TRACE("band info: ");
if (pB->fMask & RBBIM_ID)
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)
static VOID
REBAR_DumpBand (const REBAR_INFO *iP)
{
+ char buff[300];
REBAR_BAND *pB;
UINT i;
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 )) {
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);
}
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);
}
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)
{
* 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.
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 ))
#include "windef.h"
#include "winbase.h"
-#include "wine/unicode.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
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;
}
part = &infoPtr->parts[nPart];
if ((~part->style & SBT_OWNERDRAW) && part->text)
- result = strlenW(part->text);
+ result = lstrlenW(part->text);
else
result = 0;
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++;
}
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);
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 {
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);
*
*/
-#include "config.h"
-#include "wine/port.h"
-
#include <stdarg.h>
#include <string.h>
#include <stdlib.h> /* atoi */
#include "comctl32.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
TRACE("(%p %p %d)\n", lpSrc, lpDest, nMaxLen);
if (!lpDest && lpSrc)
- return strlenW (lpSrc);
+ return lstrlenW (lpSrc);
if (nMaxLen == 0)
return 0;
return 0;
}
- len = strlenW (lpSrc);
+ len = lstrlenW (lpSrc);
if (len >= nMaxLen)
len = nMaxLen - 1;
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 {
if (!lpszStr || !lpszSearch || !*lpszSearch)
return NULL;
- iLen = strlenW(lpszSearch);
- end = lpszStr + strlenW(lpszStr);
+ iLen = lstrlenW(lpszSearch);
+ end = lpszStr + lstrlenW(lpszStr);
while (lpszStr + iLen <= end)
{
*/
INT WINAPI StrToIntW (LPCWSTR lpString)
{
- return atoiW(lpString);
+ return wcstol(lpString, NULL, 10);
}
/*************************************************************************
TRACE("(%s,%i)\n", debugstr_w(lpszStr), ch);
if (lpszStr)
- lpszRet = strchrW(lpszStr, ch);
+ lpszRet = wcschr(lpszStr, ch);
return lpszRet;
}
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;
LPWSTR WINAPI StrStrW(LPCWSTR lpszStr, LPCWSTR lpszSearch)
{
if (!lpszStr || !lpszSearch) return NULL;
- return strstrW( lpszStr, lpszSearch );
+ return wcsstr( lpszStr, lpszSearch );
}
/*************************************************************************
if (lpszStr)
{
- ch = toupperW(ch);
+ ch = towupper(ch);
while (*lpszStr)
{
- if (toupperW(*lpszStr) == ch)
+ if (towupper(*lpszStr) == ch)
return (LPWSTR)lpszStr;
lpszStr++;
}
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));
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;
int WINAPI StrCSpnW(LPCWSTR lpszStr, LPCWSTR lpszMatch)
{
if (!lpszStr || !lpszMatch) return 0;
- return strcspnW( lpszStr, lpszMatch );
+ return wcscspn( lpszStr, lpszMatch );
}
/*************************************************************************
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
#include "wine/list.h"
{
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)
{
{
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;
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;
}
}
}
- else if(!strncmpiW(current, SL_LINKCLOSE, ARRAY_SIZE(SL_LINKCLOSE)) && (CurrentType == slLink) && firsttag)
+ else if(!wcsnicmp(current, SL_LINKCLOSE, ARRAY_SIZE(SL_LINKCLOSE)) && (CurrentType == slLink) && firsttag)
{
/* there's a <a...> tag opened, first add the previous text, if present */
if(textstart != NULL && textlen > 0 && firsttag > textstart)
/* 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)
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)
/* 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)
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)
{
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--;
/* 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++;
/***********************************************************************
* 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.
*/
* Task dialog control
*
* Copyright 2017 Fabian Maurer
+ * Copyright 2018 Zhiyi Zhang
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "comctl32.h"
#include "wine/debug.h"
-#include "wine/list.h"
-#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(taskdialog);
-#define ALIGNED_LENGTH(_Len, _Align) (((_Len)+(_Align))&~(_Align))
-#define ALIGNED_POINTER(_Ptr, _Align) ((LPVOID)ALIGNED_LENGTH((ULONG_PTR)(_Ptr), _Align))
-#define ALIGN_LENGTH(_Len, _Align) _Len = ALIGNED_LENGTH(_Len, _Align)
-#define ALIGN_POINTER(_Ptr, _Align) _Ptr = ALIGNED_POINTER(_Ptr, _Align)
-
static const UINT DIALOG_MIN_WIDTH = 240;
static const UINT DIALOG_SPACING = 5;
static const UINT DIALOG_BUTTON_WIDTH = 50;
static const UINT DIALOG_BUTTON_HEIGHT = 14;
+static const UINT DIALOG_EXPANDO_ICON_WIDTH = 10;
+static const UINT DIALOG_EXPANDO_ICON_HEIGHT = 10;
+static const UINT DIALOG_TIMER_MS = 200;
-static const UINT ID_MAIN_INSTRUCTION = 0xf000;
-static const UINT ID_CONTENT = 0xf001;
+static const UINT ID_TIMER = 1;
-struct taskdialog_control
+struct taskdialog_info
{
- struct list entry;
- DLGITEMTEMPLATE *template;
- unsigned int template_size;
+ HWND hwnd;
+ const TASKDIALOGCONFIG *taskconfig;
+ DWORD last_timer_tick;
+ HFONT font;
+ HFONT main_instruction_font;
+ /* Control handles */
+ HWND main_icon;
+ HWND main_instruction;
+ HWND content;
+ HWND progress_bar;
+ HWND *radio_buttons;
+ INT radio_button_count;
+ HWND *command_links;
+ INT command_link_count;
+ HWND expanded_info;
+ HWND expando_button;
+ HWND verification_box;
+ HWND footer_icon;
+ HWND footer_text;
+ HWND *buttons;
+ INT button_count;
+ HWND default_button;
+ /* Dialog metrics */
+ struct
+ {
+ LONG x_baseunit;
+ LONG y_baseunit;
+ LONG h_spacing;
+ LONG v_spacing;
+ } m;
+ INT selected_radio_id;
+ BOOL verification_checked;
+ BOOL expanded;
+ BOOL has_cancel;
+ WCHAR *expanded_text;
+ WCHAR *collapsed_text;
};
-struct taskdialog_button_desc
+struct button_layout_info
{
- int id;
- const WCHAR *text;
- unsigned int width;
- unsigned int line;
- HINSTANCE hinst;
+ LONG width;
+ LONG line;
};
-struct taskdialog_template_desc
+static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam);
+static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id);
+static void taskdialog_layout(struct taskdialog_info *dialog_info);
+
+static void taskdialog_du_to_px(struct taskdialog_info *dialog_info, LONG *width, LONG *height)
{
- const TASKDIALOGCONFIG *taskconfig;
- unsigned int dialog_height;
- unsigned int dialog_width;
- struct list controls;
- WORD control_count;
- LONG x_baseunit;
- LONG y_baseunit;
- HFONT font;
- struct taskdialog_button_desc *default_button;
-};
+ if (width) *width = MulDiv(*width, dialog_info->m.x_baseunit, 4);
+ if (height) *height = MulDiv(*height, dialog_info->m.y_baseunit, 8);
+}
-struct taskdialog_info
+static void template_write_data(char **ptr, const void *src, unsigned int size)
{
- HWND hwnd;
- PFTASKDIALOGCALLBACK callback;
- LONG_PTR callback_data;
-};
+ memcpy(*ptr, src, size);
+ *ptr += size;
+}
-static void pixels_to_dialogunits(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
+static unsigned int taskdialog_get_reference_rect(const TASKDIALOGCONFIG *taskconfig, RECT *ret)
{
- if (width)
- *width = MulDiv(*width, 4, desc->x_baseunit);
- if (height)
- *height = MulDiv(*height, 8, desc->y_baseunit);
+ HMONITOR monitor = MonitorFromWindow(taskconfig->hwndParent ? taskconfig->hwndParent : GetActiveWindow(),
+ MONITOR_DEFAULTTOPRIMARY);
+ MONITORINFO info;
+
+ info.cbSize = sizeof(info);
+ GetMonitorInfoW(monitor, &info);
+
+ if ((taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW) && taskconfig->hwndParent)
+ GetWindowRect(taskconfig->hwndParent, ret);
+ else
+ *ret = info.rcWork;
+
+ return info.rcWork.right - info.rcWork.left;
}
-static void dialogunits_to_pixels(const struct taskdialog_template_desc *desc, LONG *width, LONG *height)
+static WCHAR *taskdialog_get_exe_name(WCHAR *name, DWORD length)
{
- if (width)
- *width = MulDiv(*width, desc->x_baseunit, 4);
- if (height)
- *height = MulDiv(*height, desc->y_baseunit, 8);
+ DWORD len = GetModuleFileNameW(NULL, name, length);
+ if (len && len < length)
+ {
+ WCHAR *p;
+ if ((p = wcsrchr(name, '/'))) name = p + 1;
+ if ((p = wcsrchr(name, '\\'))) name = p + 1;
+ return name;
+ }
+ else
+ return NULL;
}
-static void template_write_data(char **ptr, const void *src, unsigned int size)
+static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
{
- memcpy(*ptr, src, size);
- *ptr += size;
+ unsigned int size, title_size;
+ static const WORD fontsize = 0x7fff;
+ static const WCHAR emptyW[] = { 0 };
+ const WCHAR *titleW = NULL;
+ DLGTEMPLATE *template;
+ WCHAR pathW[MAX_PATH];
+ char *ptr;
+
+ /* Window title */
+ if (!taskconfig->pszWindowTitle)
+ titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
+ else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
+ {
+ if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
+ titleW = taskdialog_get_exe_name(pathW, ARRAY_SIZE(pathW));
+ }
+ else
+ titleW = taskconfig->pszWindowTitle;
+ if (!titleW)
+ titleW = emptyW;
+ title_size = (lstrlenW(titleW) + 1) * sizeof(WCHAR);
+
+ size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
+ size += title_size;
+ size += 2; /* font size */
+
+ template = Alloc(size);
+ if (!template) return NULL;
+
+ template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
+ if (taskconfig->dwFlags & TDF_CAN_BE_MINIMIZED) template->style |= WS_MINIMIZEBOX;
+ if (!(taskconfig->dwFlags & TDF_NO_SET_FOREGROUND)) template->style |= DS_SETFOREGROUND;
+ if (taskconfig->dwFlags & TDF_RTL_LAYOUT) template->dwExtendedStyle = WS_EX_LAYOUTRTL | WS_EX_RIGHT | WS_EX_RTLREADING;
+
+ ptr = (char *)(template + 1);
+ ptr += 2; /* menu */
+ ptr += 2; /* class */
+ template_write_data(&ptr, titleW, title_size);
+ template_write_data(&ptr, &fontsize, sizeof(fontsize));
+
+ return template;
}
-/* used to calculate size for the controls */
-static void taskdialog_get_text_extent(const struct taskdialog_template_desc *desc, const WCHAR *text,
- BOOL user_resource, SIZE *sz)
+static HWND taskdialog_find_button(HWND *buttons, INT count, INT id)
+{
+ INT button_id;
+ INT i;
+
+ for (i = 0; i < count; i++)
+ {
+ button_id = GetWindowLongW(buttons[i], GWLP_ID);
+ if (button_id == id) return buttons[i];
+ }
+
+ return NULL;
+}
+
+static void taskdialog_enable_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
+{
+ HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
+ if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
+ if (hwnd) EnableWindow(hwnd, enable);
+}
+
+static void taskdialog_click_button(struct taskdialog_info *dialog_info, INT id)
+{
+ if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, id, 0) == S_OK) EndDialog(dialog_info->hwnd, id);
+}
+
+static void taskdialog_button_set_shield(const struct taskdialog_info *dialog_info, INT id, BOOL elevate)
+{
+ HWND hwnd = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, id);
+ if (!hwnd) hwnd = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, id);
+ if (hwnd) SendMessageW(hwnd, BCM_SETSHIELD, 0, elevate);
+}
+
+static void taskdialog_enable_radio_button(const struct taskdialog_info *dialog_info, INT id, BOOL enable)
+{
+ HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
+ if (hwnd) EnableWindow(hwnd, enable);
+}
+
+static void taskdialog_click_radio_button(const struct taskdialog_info *dialog_info, INT id)
+{
+ HWND hwnd = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, id);
+ if (hwnd) SendMessageW(hwnd, BM_CLICK, 0, 0);
+}
+
+static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ return taskconfig->pfCallback
+ ? taskconfig->pfCallback(dialog_info->hwnd, notification, wparam, lparam, taskconfig->lpCallbackData)
+ : S_OK;
+}
+
+static void taskdialog_move_controls_vertically(HWND parent, HWND *controls, INT count, INT offset)
+{
+ RECT rect;
+ POINT pt;
+ INT i;
+
+ for (i = 0; i < count; i++)
+ {
+ if (!controls[i]) continue;
+
+ GetWindowRect(controls[i], &rect);
+ pt.x = rect.left;
+ pt.y = rect.top;
+ MapWindowPoints(HWND_DESKTOP, parent, &pt, 1);
+ SetWindowPos(controls[i], 0, pt.x, pt.y + offset, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+ }
+}
+
+static void taskdialog_toggle_expando_control(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ const WCHAR *text;
+ RECT info_rect, rect;
+ INT height, offset;
+
+ dialog_info->expanded = !dialog_info->expanded;
+ text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+ SendMessageW(dialog_info->expando_button, WM_SETTEXT, 0, (LPARAM)text);
+ ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
+
+ GetWindowRect(dialog_info->expanded_info, &info_rect);
+ /* If expanded information starts up not expanded, call taskdialog_layout()
+ * to to set size for expanded information control at least once */
+ if (IsRectEmpty(&info_rect))
+ {
+ taskdialog_layout(dialog_info);
+ return;
+ }
+ height = info_rect.bottom - info_rect.top + dialog_info->m.v_spacing;
+ offset = dialog_info->expanded ? height : -height;
+
+ /* Update vertical layout, move all controls after expanded information */
+ /* Move dialog */
+ GetWindowRect(dialog_info->hwnd, &rect);
+ SetWindowPos(dialog_info->hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top + offset,
+ SWP_NOMOVE | SWP_NOZORDER);
+ /* Move controls */
+ if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA))
+ {
+ taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->progress_bar, 1, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->expando_button, 1, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->verification_box, 1, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_icon, 1, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, &dialog_info->footer_text, 1, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->buttons, dialog_info->button_count, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->radio_buttons,
+ dialog_info->radio_button_count, offset);
+ taskdialog_move_controls_vertically(dialog_info->hwnd, dialog_info->command_links,
+ dialog_info->command_link_count, offset);
+ }
+}
+
+static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, HWND hwnd, WORD id)
+{
+ INT command_id;
+ HWND button, radio_button;
+
+ /* Prefer the id from hwnd because the id from WM_COMMAND is truncated to WORD */
+ command_id = hwnd ? GetWindowLongW(hwnd, GWLP_ID) : id;
+
+ if (hwnd && hwnd == dialog_info->expando_button)
+ {
+ taskdialog_toggle_expando_control(dialog_info);
+ taskdialog_notify(dialog_info, TDN_EXPANDO_BUTTON_CLICKED, dialog_info->expanded, 0);
+ return;
+ }
+
+ if (hwnd && hwnd == dialog_info->verification_box)
+ {
+ dialog_info->verification_checked = !dialog_info->verification_checked;
+ taskdialog_notify(dialog_info, TDN_VERIFICATION_CLICKED, dialog_info->verification_checked, 0);
+ return;
+ }
+
+ radio_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count, command_id);
+ if (radio_button)
+ {
+ dialog_info->selected_radio_id = command_id;
+ taskdialog_notify(dialog_info, TDN_RADIO_BUTTON_CLICKED, command_id, 0);
+ return;
+ }
+
+ button = taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, command_id);
+ if (!button) button = taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, command_id);
+ if (!button && command_id == IDOK)
+ {
+ button = dialog_info->command_link_count > 0 ? dialog_info->command_links[0] : dialog_info->buttons[0];
+ command_id = GetWindowLongW(button, GWLP_ID);
+ }
+
+ if (button && taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
+ EndDialog(dialog_info->hwnd, command_id);
+}
+
+static WCHAR *taskdialog_gettext(struct taskdialog_info *dialog_info, BOOL user_resource, const WCHAR *text)
{
- RECT rect = { 0, 0, desc->dialog_width - DIALOG_SPACING * 2, 0}; /* padding left and right of the control */
const WCHAR *textW = NULL;
- static const WCHAR nulW;
- unsigned int length;
- HFONT oldfont;
- HDC hdc;
+ INT length;
+ WCHAR *ret;
if (IS_INTRESOURCE(text))
{
- if (!(length = LoadStringW(user_resource ? desc->taskconfig->hInstance : COMCTL32_hModule,
- (UINT_PTR)text, (WCHAR *)&textW, 0)))
- {
- WARN("Failed to load text\n");
- textW = &nulW;
- length = 0;
- }
+ if (!(length = LoadStringW(user_resource ? dialog_info->taskconfig->hInstance : COMCTL32_hModule,
+ (UINT_PTR)text, (WCHAR *)&textW, 0)))
+ return NULL;
}
else
{
textW = text;
- length = strlenW(textW);
+ length = lstrlenW(textW);
}
- hdc = GetDC(0);
- oldfont = SelectObject(hdc, desc->font);
+ ret = Alloc((length + 1) * sizeof(WCHAR));
+ if (ret) memcpy(ret, textW, length * sizeof(WCHAR));
- dialogunits_to_pixels(desc, &rect.right, NULL);
- DrawTextW(hdc, textW, length, &rect, DT_LEFT | DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK);
- pixels_to_dialogunits(desc, &rect.right, &rect.bottom);
+ return ret;
+}
- SelectObject(hdc, oldfont);
- ReleaseDC(0, hdc);
+static BOOL taskdialog_hyperlink_enabled(struct taskdialog_info *dialog_info)
+{
+ return dialog_info->taskconfig->dwFlags & TDF_ENABLE_HYPERLINKS;
+}
- sz->cx = rect.right - rect.left;
- sz->cy = rect.bottom - rect.top;
+static BOOL taskdialog_use_command_link(struct taskdialog_info *dialog_info)
+{
+ return dialog_info->taskconfig->dwFlags & (TDF_USE_COMMAND_LINKS | TDF_USE_COMMAND_LINKS_NO_ICON);
}
-static unsigned int taskdialog_add_control(struct taskdialog_template_desc *desc, WORD id, const WCHAR *class,
- HINSTANCE hInstance, const WCHAR *text, DWORD style, short x, short y, short cx, short cy)
+static void taskdialog_get_label_size(struct taskdialog_info *dialog_info, HWND hwnd, LONG max_width, SIZE *size,
+ BOOL syslink)
{
- struct taskdialog_control *control = Alloc(sizeof(*control));
- unsigned int size, class_size, text_size;
- DLGITEMTEMPLATE *template;
- static const WCHAR nulW;
- const WCHAR *textW;
- char *ptr;
+ DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
+ HFONT hfont, old_hfont;
+ HDC hdc;
+ RECT rect = {0};
+ WCHAR *text;
+ INT text_length;
- class_size = (strlenW(class) + 1) * sizeof(WCHAR);
+ if (syslink)
+ {
+ SendMessageW(hwnd, LM_GETIDEALSIZE, max_width, (LPARAM)size);
+ return;
+ }
- if (IS_INTRESOURCE(text))
- text_size = LoadStringW(hInstance, (UINT_PTR)text, (WCHAR *)&textW, 0) * sizeof(WCHAR);
+ if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
+ style |= DT_RIGHT | DT_RTLREADING;
else
+ style |= DT_LEFT;
+
+ hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
+ text_length = GetWindowTextLengthW(hwnd);
+ text = Alloc((text_length + 1) * sizeof(WCHAR));
+ if (!text)
{
- textW = text;
- text_size = strlenW(textW) * sizeof(WCHAR);
+ size->cx = 0;
+ size->cy = 0;
+ return;
}
+ GetWindowTextW(hwnd, text, text_length + 1);
+ hdc = GetDC(hwnd);
+ old_hfont = SelectObject(hdc, hfont);
+ rect.right = max_width;
+ size->cy = DrawTextW(hdc, text, text_length, &rect, style);
+ size->cx = min(max_width, rect.right - rect.left);
+ if (old_hfont) SelectObject(hdc, old_hfont);
+ ReleaseDC(hwnd, hdc);
+ Free(text);
+}
- size = sizeof(DLGITEMTEMPLATE);
- size += class_size;
- size += text_size + sizeof(WCHAR);
- size += sizeof(WORD); /* creation data */
+static void taskdialog_get_button_size(HWND hwnd, LONG max_width, SIZE *size)
+{
+ size->cx = max_width;
+ size->cy = 0;
+ SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)size);
+}
- control->template = template = Alloc(size);
- control->template_size = size;
+static void taskdialog_get_expando_size(struct taskdialog_info *dialog_info, HWND hwnd, SIZE *size)
+{
+ DWORD style = DT_EXPANDTABS | DT_CALCRECT | DT_WORDBREAK;
+ HFONT hfont, old_hfont;
+ HDC hdc;
+ RECT rect = {0};
+ LONG icon_width, icon_height, text_offset;
+ LONG max_width, max_text_height;
- template->style = WS_VISIBLE | style;
- template->dwExtendedStyle = 0;
- template->x = x;
- template->y = y;
- template->cx = cx;
- template->cy = cy;
- template->id = id;
- ptr = (char *)(template + 1);
- template_write_data(&ptr, class, class_size);
- template_write_data(&ptr, textW, text_size);
- template_write_data(&ptr, &nulW, sizeof(nulW));
+ hdc = GetDC(hwnd);
+ hfont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
+ old_hfont = SelectObject(hdc, hfont);
+
+ icon_width = DIALOG_EXPANDO_ICON_WIDTH;
+ icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
+ taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
+
+ GetCharWidthW(hdc, '0', '0', &text_offset);
+ text_offset /= 2;
+
+ if (dialog_info->taskconfig->dwFlags & TDF_RTL_LAYOUT)
+ style |= DT_RIGHT | DT_RTLREADING;
+ else
+ style |= DT_LEFT;
+
+ max_width = DIALOG_MIN_WIDTH / 2;
+ taskdialog_du_to_px(dialog_info, &max_width, NULL);
- list_add_tail(&desc->controls, &control->entry);
- desc->control_count++;
- return ALIGNED_LENGTH(size, 3);
+ rect.right = max_width - icon_width - text_offset;
+ max_text_height = DrawTextW(hdc, dialog_info->expanded_text, -1, &rect, style);
+ size->cy = max(max_text_height, icon_height);
+ size->cx = rect.right - rect.left;
+
+ rect.right = max_width - icon_width - text_offset;
+ max_text_height = DrawTextW(hdc, dialog_info->collapsed_text, -1, &rect, style);
+ size->cy = max(size->cy, max_text_height);
+ size->cx = max(size->cx, rect.right - rect.left);
+ size->cx = min(size->cx, max_width);
+
+ if (old_hfont) SelectObject(hdc, old_hfont);
+ ReleaseDC(hwnd, hdc);
}
-static unsigned int taskdialog_add_static_label(struct taskdialog_template_desc *desc, WORD id, const WCHAR *str)
+static ULONG_PTR taskdialog_get_standard_icon(LPCWSTR icon)
{
- unsigned int size;
- SIZE sz;
+ if (icon == TD_WARNING_ICON)
+ return IDI_WARNING;
+ else if (icon == TD_ERROR_ICON)
+ return IDI_ERROR;
+ else if (icon == TD_INFORMATION_ICON)
+ return IDI_INFORMATION;
+ else if (icon == TD_SHIELD_ICON)
+ return IDI_SHIELD;
+ else
+ return (ULONG_PTR)icon;
+}
- if (!str)
- return 0;
+static void taskdialog_set_icon(struct taskdialog_info *dialog_info, INT element, HICON icon)
+{
+ DWORD flags = dialog_info->taskconfig->dwFlags;
+ INT cx = 0, cy = 0;
+ HICON hicon;
- taskdialog_get_text_extent(desc, str, TRUE, &sz);
+ if (!icon) return;
- desc->dialog_height += DIALOG_SPACING;
- size = taskdialog_add_control(desc, id, WC_STATICW, desc->taskconfig->hInstance, str, 0, DIALOG_SPACING,
- desc->dialog_height, sz.cx, sz.cy);
- desc->dialog_height += sz.cy + DIALOG_SPACING;
- return size;
+ if (((flags & TDF_USE_HICON_MAIN) && element == TDIE_ICON_MAIN)
+ || ((flags & TDF_USE_HICON_FOOTER) && element == TDIE_ICON_FOOTER))
+ hicon = icon;
+ else
+ {
+ if (element == TDIE_ICON_FOOTER)
+ {
+ cx = GetSystemMetrics(SM_CXSMICON);
+ cy = GetSystemMetrics(SM_CYSMICON);
+ }
+ hicon = LoadImageW(dialog_info->taskconfig->hInstance, (LPCWSTR)icon, IMAGE_ICON, cx, cy, LR_SHARED | LR_DEFAULTSIZE);
+ if (!hicon)
+ hicon = LoadImageW(NULL, (LPCWSTR)taskdialog_get_standard_icon((LPCWSTR)icon), IMAGE_ICON, cx, cy,
+ LR_SHARED | LR_DEFAULTSIZE);
+ }
+
+ if (!hicon) return;
+
+ if (element == TDIE_ICON_MAIN)
+ {
+ SendMessageW(dialog_info->hwnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)hicon);
+ SendMessageW(dialog_info->main_icon, STM_SETICON, (WPARAM)hicon, 0);
+ }
+ else if (element == TDIE_ICON_FOOTER)
+ SendMessageW(dialog_info->footer_icon, STM_SETICON, (WPARAM)hicon, 0);
+}
+
+static void taskdialog_set_element_text(struct taskdialog_info *dialog_info, TASKDIALOG_ELEMENTS element,
+ const WCHAR *text)
+{
+ HWND hwnd = NULL;
+ WCHAR *textW;
+
+ if (element == TDE_CONTENT)
+ hwnd = dialog_info->content;
+ else if (element == TDE_EXPANDED_INFORMATION)
+ hwnd = dialog_info->expanded_info;
+ else if (element == TDE_FOOTER)
+ hwnd = dialog_info->footer_text;
+ else if (element == TDE_MAIN_INSTRUCTION)
+ hwnd = dialog_info->main_instruction;
+
+ if (!hwnd) return;
+
+ textW = taskdialog_gettext(dialog_info, TRUE, text);
+ SendMessageW(hwnd, WM_SETTEXT, 0, (LPARAM)textW);
+ Free(textW);
}
-static unsigned int taskdialog_add_main_instruction(struct taskdialog_template_desc *desc)
+static void taskdialog_check_default_radio_buttons(struct taskdialog_info *dialog_info)
{
- return taskdialog_add_static_label(desc, ID_MAIN_INSTRUCTION, desc->taskconfig->pszMainInstruction);
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ HWND default_button;
+
+ if (!dialog_info->radio_button_count) return;
+
+ default_button = taskdialog_find_button(dialog_info->radio_buttons, dialog_info->radio_button_count,
+ taskconfig->nDefaultRadioButton);
+
+ if (!default_button && !(taskconfig->dwFlags & TDF_NO_DEFAULT_RADIO_BUTTON))
+ default_button = dialog_info->radio_buttons[0];
+
+ if (default_button)
+ {
+ SendMessageW(default_button, BM_SETCHECK, BST_CHECKED, 0);
+ taskdialog_on_button_click(dialog_info, default_button, 0);
+ }
}
-static unsigned int taskdialog_add_content(struct taskdialog_template_desc *desc)
+static void taskdialog_add_main_icon(struct taskdialog_info *dialog_info)
{
- return taskdialog_add_static_label(desc, ID_CONTENT, desc->taskconfig->pszContent);
+ if (!dialog_info->taskconfig->u.hMainIcon) return;
+
+ dialog_info->main_icon =
+ CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
+ taskdialog_set_icon(dialog_info, TDIE_ICON_MAIN, dialog_info->taskconfig->u.hMainIcon);
}
-static void taskdialog_init_button(struct taskdialog_button_desc *button, struct taskdialog_template_desc *desc,
- int id, const WCHAR *text, BOOL custom_button)
+static HWND taskdialog_create_label(struct taskdialog_info *dialog_info, const WCHAR *text, HFONT font, BOOL syslink)
{
- SIZE sz;
+ WCHAR *textW;
+ HWND hwnd;
+ const WCHAR *class;
+ DWORD style = WS_CHILD | WS_VISIBLE;
- taskdialog_get_text_extent(desc, text, custom_button, &sz);
+ if (!text) return NULL;
- button->id = id;
- button->text = text;
- button->width = max(DIALOG_BUTTON_WIDTH, sz.cx + DIALOG_SPACING * 2);
- button->line = 0;
- button->hinst = custom_button ? desc->taskconfig->hInstance : COMCTL32_hModule;
+ class = syslink ? WC_LINK : WC_STATICW;
+ if (syslink) style |= WS_TABSTOP;
+ textW = taskdialog_gettext(dialog_info, TRUE, text);
+ hwnd = CreateWindowW(class, textW, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
+ Free(textW);
- if (id == desc->taskconfig->nDefaultButton)
- desc->default_button = button;
+ SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, 0);
+ return hwnd;
}
-static void taskdialog_init_common_buttons(struct taskdialog_template_desc *desc, struct taskdialog_button_desc *buttons,
- unsigned int *button_count)
+static void taskdialog_add_main_instruction(struct taskdialog_info *dialog_info)
{
- DWORD flags = desc->taskconfig->dwCommonButtons;
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ NONCLIENTMETRICSW ncm;
-#define TASKDIALOG_INIT_COMMON_BUTTON(id) \
- do { \
- taskdialog_init_button(&buttons[(*button_count)++], desc, ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), FALSE); \
- } while(0)
+ if (!taskconfig->pszMainInstruction) return;
- if (flags & TDCBF_OK_BUTTON)
- TASKDIALOG_INIT_COMMON_BUTTON(OK);
- if (flags & TDCBF_YES_BUTTON)
- TASKDIALOG_INIT_COMMON_BUTTON(YES);
- if (flags & TDCBF_NO_BUTTON)
- TASKDIALOG_INIT_COMMON_BUTTON(NO);
- if (flags & TDCBF_RETRY_BUTTON)
- TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
- if (flags & TDCBF_CANCEL_BUTTON)
- TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
- if (flags & TDCBF_CLOSE_BUTTON)
- TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
+ ncm.cbSize = sizeof(ncm);
+ SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
+ /* 1.25 times the height */
+ ncm.lfMessageFont.lfHeight = ncm.lfMessageFont.lfHeight * 5 / 4;
+ ncm.lfMessageFont.lfWeight = FW_BOLD;
+ dialog_info->main_instruction_font = CreateFontIndirectW(&ncm.lfMessageFont);
-#undef TASKDIALOG_INIT_COMMON_BUTTON
+ dialog_info->main_instruction =
+ taskdialog_create_label(dialog_info, taskconfig->pszMainInstruction, dialog_info->main_instruction_font, FALSE);
}
-static unsigned int taskdialog_add_buttons(struct taskdialog_template_desc *desc)
+static void taskdialog_add_content(struct taskdialog_info *dialog_info)
{
- unsigned int count = 0, buttons_size, i, line_count, size = 0;
- unsigned int location_x, *line_widths, alignment = ~0u;
- const TASKDIALOGCONFIG *taskconfig = desc->taskconfig;
- struct taskdialog_button_desc *buttons;
+ dialog_info->content = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszContent, dialog_info->font,
+ taskdialog_hyperlink_enabled(dialog_info));
+}
+
+static void taskdialog_add_progress_bar(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ DWORD style = PBS_SMOOTH | PBS_SMOOTHREVERSE | WS_CHILD | WS_VISIBLE;
+
+ if (!(taskconfig->dwFlags & (TDF_SHOW_PROGRESS_BAR | TDF_SHOW_MARQUEE_PROGRESS_BAR))) return;
+ if (taskconfig->dwFlags & TDF_SHOW_MARQUEE_PROGRESS_BAR) style |= PBS_MARQUEE;
+ dialog_info->progress_bar =
+ CreateWindowW(PROGRESS_CLASSW, NULL, style, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, NULL);
+}
+
+static void taskdialog_add_radio_buttons(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ static const DWORD style = BS_AUTORADIOBUTTON | BS_MULTILINE | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
+ WCHAR *textW;
+ INT i;
+
+ if (!taskconfig->cRadioButtons || !taskconfig->pRadioButtons) return;
+
+ dialog_info->radio_buttons = Alloc(taskconfig->cRadioButtons * sizeof(*dialog_info->radio_buttons));
+ if (!dialog_info->radio_buttons) return;
+
+ dialog_info->radio_button_count = taskconfig->cRadioButtons;
+ for (i = 0; i < dialog_info->radio_button_count; i++)
+ {
+ textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pRadioButtons[i].pszButtonText);
+ dialog_info->radio_buttons[i] =
+ CreateWindowW(WC_BUTTONW, textW, i == 0 ? style | WS_GROUP : style, 0, 0, 0, 0, dialog_info->hwnd,
+ LongToHandle(taskconfig->pRadioButtons[i].nButtonID), 0, NULL);
+ SendMessageW(dialog_info->radio_buttons[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
+ Free(textW);
+ }
+}
+
+static void taskdialog_add_command_links(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ DWORD default_style = BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP, style;
+ BOOL is_default;
+ WCHAR *textW;
+ INT i;
+
+ if (!taskconfig->cButtons || !taskconfig->pButtons || !taskdialog_use_command_link(dialog_info)) return;
+
+ dialog_info->command_links = Alloc(taskconfig->cButtons * sizeof(*dialog_info->command_links));
+ if (!dialog_info->command_links) return;
+
+ dialog_info->command_link_count = taskconfig->cButtons;
+ for (i = 0; i < dialog_info->command_link_count; i++)
+ {
+ is_default = taskconfig->pButtons[i].nButtonID == taskconfig->nDefaultButton;
+ style = is_default ? default_style | BS_DEFCOMMANDLINK : default_style | BS_COMMANDLINK;
+ textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pButtons[i].pszButtonText);
+ dialog_info->command_links[i] = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd,
+ LongToHandle(taskconfig->pButtons[i].nButtonID), 0, NULL);
+ SendMessageW(dialog_info->command_links[i], WM_SETFONT, (WPARAM)dialog_info->font, 0);
+ Free(textW);
+
+ if (is_default && !dialog_info->default_button) dialog_info->default_button = dialog_info->command_links[i];
+ }
+}
+
+static void taskdialog_add_expanded_info(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+
+ if (!taskconfig->pszExpandedInformation) return;
+
+ dialog_info->expanded = taskconfig->dwFlags & TDF_EXPANDED_BY_DEFAULT;
+ dialog_info->expanded_info = taskdialog_create_label(dialog_info, taskconfig->pszExpandedInformation,
+ dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
+ ShowWindow(dialog_info->expanded_info, dialog_info->expanded ? SW_SHOWDEFAULT : SW_HIDE);
+}
+
+static void taskdialog_add_expando_button(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ const WCHAR *textW;
+
+ if (!taskconfig->pszExpandedInformation) return;
+
+ if (!taskconfig->pszCollapsedControlText && !taskconfig->pszExpandedControlText)
+ {
+ dialog_info->expanded_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_EXPANDED));
+ dialog_info->collapsed_text = taskdialog_gettext(dialog_info, FALSE, MAKEINTRESOURCEW(IDS_TD_COLLAPSED));
+ }
+ else
+ {
+ textW = taskconfig->pszExpandedControlText ? taskconfig->pszExpandedControlText
+ : taskconfig->pszCollapsedControlText;
+ dialog_info->expanded_text = taskdialog_gettext(dialog_info, TRUE, textW);
+ textW = taskconfig->pszCollapsedControlText ? taskconfig->pszCollapsedControlText
+ : taskconfig->pszExpandedControlText;
+ dialog_info->collapsed_text = taskdialog_gettext(dialog_info, TRUE, textW);
+ }
+
+ textW = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+
+ dialog_info->expando_button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_OWNERDRAW, 0,
+ 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
+ SendMessageW(dialog_info->expando_button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+}
+
+static void taskdialog_add_verification_box(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ static const DWORD style = BS_AUTOCHECKBOX | BS_MULTILINE | BS_LEFT | BS_TOP | WS_CHILD | WS_VISIBLE | WS_TABSTOP;
+ WCHAR *textW;
+
+ /* TDF_VERIFICATION_FLAG_CHECKED works even if pszVerificationText is not set */
+ if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED) dialog_info->verification_checked = TRUE;
+
+ if (!taskconfig->pszVerificationText) return;
+
+ textW = taskdialog_gettext(dialog_info, TRUE, taskconfig->pszVerificationText);
+ dialog_info->verification_box = CreateWindowW(WC_BUTTONW, textW, style, 0, 0, 0, 0, dialog_info->hwnd, 0, 0, 0);
+ SendMessageW(dialog_info->verification_box, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+ Free(textW);
+
+ if (taskconfig->dwFlags & TDF_VERIFICATION_FLAG_CHECKED)
+ SendMessageW(dialog_info->verification_box, BM_SETCHECK, BST_CHECKED, 0);
+}
+
+static void taskdialog_add_button(struct taskdialog_info *dialog_info, HWND *button, INT_PTR id, const WCHAR *text,
+ BOOL custom_button)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ WCHAR *textW;
+
+ textW = taskdialog_gettext(dialog_info, custom_button, text);
+ *button = CreateWindowW(WC_BUTTONW, textW, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, dialog_info->hwnd,
+ (HMENU)id, 0, NULL);
+ Free(textW);
+ SendMessageW(*button, WM_SETFONT, (WPARAM)dialog_info->font, 0);
+
+ if (id == taskconfig->nDefaultButton && !dialog_info->default_button) dialog_info->default_button = *button;
+}
+
+static void taskdialog_add_buttons(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ BOOL use_command_links = taskdialog_use_command_link(dialog_info);
+ DWORD flags = taskconfig->dwCommonButtons;
+ INT count, max_count;
/* Allocate enough memory for the custom and the default buttons. Maximum 6 default buttons possible. */
- buttons_size = 6;
- if (taskconfig->cButtons && taskconfig->pButtons)
- buttons_size += taskconfig->cButtons;
+ max_count = 6;
+ if (!use_command_links && taskconfig->cButtons && taskconfig->pButtons) max_count += taskconfig->cButtons;
+
+ dialog_info->buttons = Alloc(max_count * sizeof(*dialog_info->buttons));
+ if (!dialog_info->buttons) return;
+
+ for (count = 0; !use_command_links && count < taskconfig->cButtons; count++)
+ taskdialog_add_button(dialog_info, &dialog_info->buttons[count], taskconfig->pButtons[count].nButtonID,
+ taskconfig->pButtons[count].pszButtonText, TRUE);
+
+#define TASKDIALOG_INIT_COMMON_BUTTON(id) \
+ do \
+ { \
+ taskdialog_add_button(dialog_info, &dialog_info->buttons[count++], ID##id, MAKEINTRESOURCEW(IDS_BUTTON_##id), \
+ FALSE); \
+ } while (0)
+
+ if (flags & TDCBF_OK_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(OK);
+ if (flags & TDCBF_YES_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(YES);
+ if (flags & TDCBF_NO_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(NO);
+ if (flags & TDCBF_RETRY_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(RETRY);
+ if (flags & TDCBF_CANCEL_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CANCEL);
+ if (flags & TDCBF_CLOSE_BUTTON) TASKDIALOG_INIT_COMMON_BUTTON(CLOSE);
+
+ if (!count && !dialog_info->command_link_count) TASKDIALOG_INIT_COMMON_BUTTON(OK);
+#undef TASKDIALOG_INIT_COMMON_BUTTON
- if (!(buttons = Alloc(buttons_size * sizeof(*buttons))))
- return 0;
+ dialog_info->button_count = count;
+}
- /* Custom buttons */
- if (taskconfig->cButtons && taskconfig->pButtons)
- for (i = 0; i < taskconfig->cButtons; i++)
- taskdialog_init_button(&buttons[count++], desc, taskconfig->pButtons[i].nButtonID,
- taskconfig->pButtons[i].pszButtonText, TRUE);
+static void taskdialog_add_footer_icon(struct taskdialog_info *dialog_info)
+{
+ if (!dialog_info->taskconfig->u2.hFooterIcon) return;
- /* Common buttons */
- taskdialog_init_common_buttons(desc, buttons, &count);
+ dialog_info->footer_icon =
+ CreateWindowW(WC_STATICW, NULL, WS_CHILD | WS_VISIBLE | SS_ICON, 0, 0, 0, 0, dialog_info->hwnd, NULL, 0, 0);
+ taskdialog_set_icon(dialog_info, TDIE_ICON_FOOTER, dialog_info->taskconfig->u2.hFooterIcon);
+}
- /* There must be at least one button */
- if (count == 0)
- taskdialog_init_button(&buttons[count++], desc, IDOK, MAKEINTRESOURCEW(IDS_BUTTON_OK), FALSE);
+static void taskdialog_add_footer_text(struct taskdialog_info *dialog_info)
+{
+ dialog_info->footer_text = taskdialog_create_label(dialog_info, dialog_info->taskconfig->pszFooter,
+ dialog_info->font, taskdialog_hyperlink_enabled(dialog_info));
+}
- if (!desc->default_button)
- desc->default_button = &buttons[0];
+static LONG taskdialog_get_dialog_width(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
+ LONG max_width, main_icon_width, screen_width;
+ RECT rect;
+ SIZE size;
+
+ screen_width = taskdialog_get_reference_rect(taskconfig, &rect);
+ if ((taskconfig->dwFlags & TDF_SIZE_TO_CONTENT) && !taskconfig->cxWidth)
+ {
+ max_width = DIALOG_MIN_WIDTH;
+ taskdialog_du_to_px(dialog_info, &max_width, NULL);
+ main_icon_width = dialog_info->m.h_spacing;
+ if (dialog_info->main_icon) main_icon_width += GetSystemMetrics(SM_CXICON);
+ if (dialog_info->content)
+ {
+ taskdialog_get_label_size(dialog_info, dialog_info->content, 0, &size, syslink);
+ max_width = max(max_width, size.cx + main_icon_width + dialog_info->m.h_spacing * 2);
+ }
+ }
+ else
+ {
+ max_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
+ taskdialog_du_to_px(dialog_info, &max_width, NULL);
+ }
+ max_width = min(max_width, screen_width);
+ return max_width;
+}
+
+static void taskdialog_label_layout(struct taskdialog_info *dialog_info, HWND hwnd, INT start_x, LONG dialog_width,
+ LONG *dialog_height, BOOL syslink)
+{
+ LONG x, y, max_width;
+ SIZE size;
+
+ if (!hwnd) return;
+
+ x = start_x + dialog_info->m.h_spacing;
+ y = *dialog_height + dialog_info->m.v_spacing;
+ max_width = dialog_width - x - dialog_info->m.h_spacing;
+ taskdialog_get_label_size(dialog_info, hwnd, max_width, &size, syslink);
+ SetWindowPos(hwnd, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ *dialog_height = y + size.cy;
+}
+
+static void taskdialog_layout(struct taskdialog_info *dialog_info)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ BOOL syslink = taskdialog_hyperlink_enabled(dialog_info);
+ static BOOL first_time = TRUE;
+ RECT ref_rect;
+ LONG dialog_width, dialog_height = 0;
+ LONG h_spacing, v_spacing;
+ LONG main_icon_right, main_icon_bottom;
+ LONG expando_right, expando_bottom;
+ struct button_layout_info *button_layout_infos;
+ LONG button_min_width, button_height;
+ LONG *line_widths, line_count, align;
+ LONG footer_icon_right, footer_icon_bottom;
+ LONG x, y;
+ SIZE size;
+ INT i;
+
+ taskdialog_get_reference_rect(dialog_info->taskconfig, &ref_rect);
+ dialog_width = taskdialog_get_dialog_width(dialog_info);
+
+ h_spacing = dialog_info->m.h_spacing;
+ v_spacing = dialog_info->m.v_spacing;
+
+ /* Main icon */
+ main_icon_right = 0;
+ main_icon_bottom = 0;
+ if (dialog_info->main_icon)
+ {
+ x = h_spacing;
+ y = dialog_height + v_spacing;
+ size.cx = GetSystemMetrics(SM_CXICON);
+ size.cy = GetSystemMetrics(SM_CYICON);
+ SetWindowPos(dialog_info->main_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ main_icon_right = x + size.cx;
+ main_icon_bottom = y + size.cy;
+ }
+
+ /* Main instruction */
+ taskdialog_label_layout(dialog_info, dialog_info->main_instruction, main_icon_right, dialog_width, &dialog_height,
+ FALSE);
+
+ /* Content */
+ taskdialog_label_layout(dialog_info, dialog_info->content, main_icon_right, dialog_width, &dialog_height, syslink);
+
+ /* Expanded information */
+ if (!(taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
+ taskdialog_label_layout(dialog_info, dialog_info->expanded_info, main_icon_right, dialog_width, &dialog_height,
+ syslink);
+
+ /* Progress bar */
+ if (dialog_info->progress_bar)
+ {
+ x = main_icon_right + h_spacing;
+ y = dialog_height + v_spacing;
+ size.cx = dialog_width - x - h_spacing;
+ size.cy = GetSystemMetrics(SM_CYVSCROLL);
+ SetWindowPos(dialog_info->progress_bar, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ dialog_height = y + size.cy;
+ }
+
+ /* Radio buttons */
+ for (i = 0; i < dialog_info->radio_button_count; i++)
+ {
+ x = main_icon_right + h_spacing;
+ y = dialog_height + v_spacing;
+ taskdialog_get_button_size(dialog_info->radio_buttons[i], dialog_width - x - h_spacing, &size);
+ size.cx = dialog_width - x - h_spacing;
+ SetWindowPos(dialog_info->radio_buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ dialog_height = y + size.cy;
+ }
+
+ /* Command links */
+ for (i = 0; i < dialog_info->command_link_count; i++)
+ {
+ x = main_icon_right + h_spacing;
+ y = dialog_height;
+ /* Only add spacing for the first command links. There is no vertical spacing between command links */
+ if (!i)
+ y += v_spacing;
+ taskdialog_get_button_size(dialog_info->command_links[i], dialog_width - x - h_spacing, &size);
+ size.cx = dialog_width - x - h_spacing;
+ /* Add spacing */
+ size.cy += 4;
+ SetWindowPos(dialog_info->command_links[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ dialog_height = y + size.cy;
+ }
- /* For easy handling just allocate as many lines as buttons, the worst case. */
- line_widths = Alloc(count * sizeof(*line_widths));
+ dialog_height = max(dialog_height, main_icon_bottom);
+
+ expando_right = 0;
+ expando_bottom = dialog_height;
+ /* Expando control */
+ if (dialog_info->expando_button)
+ {
+ x = h_spacing;
+ y = dialog_height + v_spacing;
+ taskdialog_get_expando_size(dialog_info, dialog_info->expando_button, &size);
+ SetWindowPos(dialog_info->expando_button, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ expando_right = x + size.cx;
+ expando_bottom = y + size.cy;
+ }
+
+ /* Verification box */
+ if (dialog_info->verification_box)
+ {
+ x = h_spacing;
+ y = expando_bottom + v_spacing;
+ size.cx = DIALOG_MIN_WIDTH / 2;
+ taskdialog_du_to_px(dialog_info, &size.cx, NULL);
+ taskdialog_get_button_size(dialog_info->verification_box, size.cx, &size);
+ SetWindowPos(dialog_info->verification_box, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ expando_right = max(expando_right, x + size.cx);
+ expando_bottom = y + size.cy;
+ }
+
+ /* Common and custom buttons */
+ button_layout_infos = Alloc(dialog_info->button_count * sizeof(*button_layout_infos));
+ line_widths = Alloc(dialog_info->button_count * sizeof(*line_widths));
+
+ button_min_width = DIALOG_BUTTON_WIDTH;
+ button_height = DIALOG_BUTTON_HEIGHT;
+ taskdialog_du_to_px(dialog_info, &button_min_width, &button_height);
+ for (i = 0; i < dialog_info->button_count; i++)
+ {
+ taskdialog_get_button_size(dialog_info->buttons[i], dialog_width - expando_right - h_spacing * 2, &size);
+ button_layout_infos[i].width = max(size.cx, button_min_width);
+ }
/* Separate buttons into lines */
- location_x = DIALOG_SPACING;
- for (i = 0, line_count = 0; i < count; i++)
+ x = expando_right + h_spacing;
+ for (i = 0, line_count = 0; i < dialog_info->button_count; i++)
{
- if (location_x + buttons[i].width + DIALOG_SPACING > desc->dialog_width)
+ button_layout_infos[i].line = line_count;
+ x += button_layout_infos[i].width + h_spacing;
+ line_widths[line_count] += button_layout_infos[i].width + h_spacing;
+
+ if ((i + 1 < dialog_info->button_count) && (x + button_layout_infos[i + 1].width + h_spacing >= dialog_width))
{
- location_x = DIALOG_SPACING;
+ x = expando_right + h_spacing;
line_count++;
}
-
- buttons[i].line = line_count;
-
- location_x += buttons[i].width + DIALOG_SPACING;
- line_widths[line_count] += buttons[i].width + DIALOG_SPACING;
}
line_count++;
unsigned int j, last_button = 0;
int diff_changed;
- for (j = 0; j < count; j++)
- if (buttons[j].line == i - 1)
- last_button = j;
+ for (j = 0; j < dialog_info->button_count; j++)
+ if (button_layout_infos[j].line == i - 1) last_button = j;
/* Difference in length of both lines if we wrapped the last button from the last line into this one */
- diff_changed = abs(2 * buttons[last_button].width + line_widths[i] - line_widths[i - 1]);
+ diff_changed = abs(2 * button_layout_infos[last_button].width + line_widths[i] - line_widths[i - 1]);
if (diff_changed < diff_now)
{
- buttons[last_button].line = i;
- line_widths[i] += buttons[last_button].width;
- line_widths[i - 1] -= buttons[last_button].width;
+ button_layout_infos[last_button].line = i;
+ line_widths[i] += button_layout_infos[last_button].width;
+ line_widths[i - 1] -= button_layout_infos[last_button].width;
}
}
/* Calculate left alignment so all lines are as far right as possible. */
+ align = dialog_width - h_spacing;
for (i = 0; i < line_count; i++)
{
- int new_alignment = desc->dialog_width - line_widths[i];
- if (new_alignment < alignment)
- alignment = new_alignment;
+ int new_alignment = dialog_width - line_widths[i];
+ if (new_alignment < align) align = new_alignment;
}
- /* Now that we got them all positioned, create all buttons */
- location_x = alignment;
- for (i = 0; i < count; i++)
+ /* Now that we got them all positioned, move all buttons */
+ x = align;
+ size.cy = button_height;
+ for (i = 0; i < dialog_info->button_count; i++)
{
- DWORD style = &buttons[i] == desc->default_button ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON;
-
- if (i > 0 && buttons[i].line != buttons[i - 1].line) /* New line */
+ /* New line */
+ if (i > 0 && button_layout_infos[i].line != button_layout_infos[i - 1].line)
{
- location_x = alignment;
- desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
+ x = align;
+ dialog_height += size.cy + v_spacing;
}
- size += taskdialog_add_control(desc, buttons[i].id, WC_BUTTONW, buttons[i].hinst, buttons[i].text, style,
- location_x, desc->dialog_height, buttons[i].width, DIALOG_BUTTON_HEIGHT);
-
- location_x += buttons[i].width + DIALOG_SPACING;
+ y = dialog_height + v_spacing;
+ size.cx = button_layout_infos[i].width;
+ SetWindowPos(dialog_info->buttons[i], 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ x += button_layout_infos[i].width + h_spacing;
}
- /* Add height for last row and spacing */
- desc->dialog_height += DIALOG_BUTTON_HEIGHT + DIALOG_SPACING;
+ /* Add height for last row button and spacing */
+ dialog_height += size.cy + v_spacing;
+ dialog_height = max(dialog_height, expando_bottom);
+ Free(button_layout_infos);
Free(line_widths);
- Free(buttons);
-
- return size;
-}
-
-static void taskdialog_clear_controls(struct list *controls)
-{
- struct taskdialog_control *control, *control2;
- LIST_FOR_EACH_ENTRY_SAFE(control, control2, controls, struct taskdialog_control, entry)
+ /* Footer icon */
+ footer_icon_right = 0;
+ footer_icon_bottom = dialog_height;
+ if (dialog_info->footer_icon)
{
- list_remove(&control->entry);
- Free(control->template);
- Free(control);
+ x = h_spacing;
+ y = dialog_height + v_spacing;
+ size.cx = GetSystemMetrics(SM_CXSMICON);
+ size.cy = GetSystemMetrics(SM_CYSMICON);
+ SetWindowPos(dialog_info->footer_icon, 0, x, y, size.cx, size.cy, SWP_NOZORDER);
+ footer_icon_right = x + size.cx;
+ footer_icon_bottom = y + size.cy;
}
-}
-
-static unsigned int taskdialog_get_reference_rect(const struct taskdialog_template_desc *desc, RECT *ret)
-{
- HMONITOR monitor = MonitorFromWindow(desc->taskconfig->hwndParent ? desc->taskconfig->hwndParent : GetActiveWindow(),
- MONITOR_DEFAULTTOPRIMARY);
- MONITORINFO info;
-
- info.cbSize = sizeof(info);
- GetMonitorInfoW(monitor, &info);
- if (desc->taskconfig->dwFlags & TDF_POSITION_RELATIVE_TO_WINDOW && desc->taskconfig->hwndParent)
- GetWindowRect(desc->taskconfig->hwndParent, ret);
- else
- *ret = info.rcWork;
+ /* Footer text */
+ taskdialog_label_layout(dialog_info, dialog_info->footer_text, footer_icon_right, dialog_width, &dialog_height,
+ syslink);
+ dialog_height = max(dialog_height, footer_icon_bottom);
- pixels_to_dialogunits(desc, &ret->left, &ret->top);
- pixels_to_dialogunits(desc, &ret->right, &ret->bottom);
+ /* Expanded information */
+ if ((taskconfig->dwFlags & TDF_EXPAND_FOOTER_AREA) && dialog_info->expanded)
+ taskdialog_label_layout(dialog_info, dialog_info->expanded_info, 0, dialog_width, &dialog_height, syslink);
- pixels_to_dialogunits(desc, &info.rcWork.left, &info.rcWork.top);
- pixels_to_dialogunits(desc, &info.rcWork.right, &info.rcWork.bottom);
- return info.rcWork.right - info.rcWork.left;
-}
+ /* Add height for spacing, title height and frame height */
+ dialog_height += v_spacing;
+ dialog_height += GetSystemMetrics(SM_CYCAPTION);
+ dialog_height += GetSystemMetrics(SM_CXDLGFRAME);
-static WCHAR *taskdialog_get_exe_name(const TASKDIALOGCONFIG *taskconfig, WCHAR *name, DWORD length)
-{
- DWORD len = GetModuleFileNameW(NULL, name, length);
- if (len && len < length)
+ if (first_time)
{
- WCHAR *p;
- if ((p = strrchrW(name, '/'))) name = p + 1;
- if ((p = strrchrW(name, '\\'))) name = p + 1;
- return name;
+ x = (ref_rect.left + ref_rect.right - dialog_width) / 2;
+ y = (ref_rect.top + ref_rect.bottom - dialog_height) / 2;
+ SetWindowPos(dialog_info->hwnd, 0, x, y, dialog_width, dialog_height, SWP_NOZORDER);
+ first_time = FALSE;
}
else
- return NULL;
+ SetWindowPos(dialog_info->hwnd, 0, 0, 0, dialog_width, dialog_height, SWP_NOMOVE | SWP_NOZORDER);
}
-static DLGTEMPLATE *create_taskdialog_template(const TASKDIALOGCONFIG *taskconfig)
+static void taskdialog_draw_expando_control(struct taskdialog_info *dialog_info, LPDRAWITEMSTRUCT dis)
{
- struct taskdialog_control *control, *control2;
- unsigned int size, title_size, screen_width;
- struct taskdialog_template_desc desc;
- static const WORD fontsize = 0x7fff;
- static const WCHAR emptyW[] = { 0 };
- const WCHAR *titleW = NULL;
- DLGTEMPLATE *template;
- NONCLIENTMETRICSW ncm;
- WCHAR pathW[MAX_PATH];
- RECT ref_rect;
- char *ptr;
+ HWND hwnd;
HDC hdc;
+ RECT rect = {0};
+ WCHAR *text;
+ LONG icon_width, icon_height, text_offset;
+ UINT style = DFCS_FLAT;
+ BOOL draw_focus;
+
+ hdc = dis->hDC;
+ hwnd = dis->hwndItem;
+
+ SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0);
+
+ icon_width = DIALOG_EXPANDO_ICON_WIDTH;
+ icon_height = DIALOG_EXPANDO_ICON_HEIGHT;
+ taskdialog_du_to_px(dialog_info, &icon_width, &icon_height);
+ rect.right = icon_width;
+ rect.bottom = icon_height;
+ style |= dialog_info->expanded ? DFCS_SCROLLUP : DFCS_SCROLLDOWN;
+ DrawFrameControl(hdc, &rect, DFC_SCROLL, style);
+
+ GetCharWidthW(hdc, '0', '0', &text_offset);
+ text_offset /= 2;
+
+ rect = dis->rcItem;
+ rect.left += icon_width + text_offset;
+ text = dialog_info->expanded ? dialog_info->expanded_text : dialog_info->collapsed_text;
+ DrawTextW(hdc, text, -1, &rect, DT_WORDBREAK | DT_END_ELLIPSIS | DT_EXPANDTABS);
+
+ draw_focus = (dis->itemState & ODS_FOCUS) && !(dis->itemState & ODS_NOFOCUSRECT);
+ if(draw_focus) DrawFocusRect(hdc, &rect);
+}
- /* Window title */
- if (!taskconfig->pszWindowTitle)
- titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
- else if (IS_INTRESOURCE(taskconfig->pszWindowTitle))
- {
- if (!LoadStringW(taskconfig->hInstance, LOWORD(taskconfig->pszWindowTitle), (WCHAR *)&titleW, 0))
- titleW = taskdialog_get_exe_name(taskconfig, pathW, ARRAY_SIZE(pathW));
- }
- else
- titleW = taskconfig->pszWindowTitle;
- if (!titleW)
- titleW = emptyW;
- title_size = (strlenW(titleW) + 1) * sizeof(WCHAR);
-
- size = sizeof(DLGTEMPLATE) + 2 * sizeof(WORD);
- size += title_size;
- size += 2; /* font size */
-
- list_init(&desc.controls);
- desc.taskconfig = taskconfig;
- desc.control_count = 0;
+static void taskdialog_init(struct taskdialog_info *dialog_info, HWND hwnd)
+{
+ const TASKDIALOGCONFIG *taskconfig = dialog_info->taskconfig;
+ NONCLIENTMETRICSW ncm;
+ HDC hdc;
+ INT id;
ncm.cbSize = sizeof(ncm);
SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, ncm.cbSize, &ncm, 0);
- desc.font = CreateFontIndirectW(&ncm.lfMessageFont);
-
- hdc = GetDC(0);
- SelectObject(hdc, desc.font);
- desc.x_baseunit = GdiGetCharDimensions(hdc, NULL, &desc.y_baseunit);
- ReleaseDC(0, hdc);
- screen_width = taskdialog_get_reference_rect(&desc, &ref_rect);
+ memset(dialog_info, 0, sizeof(*dialog_info));
+ dialog_info->taskconfig = taskconfig;
+ dialog_info->hwnd = hwnd;
+ dialog_info->font = CreateFontIndirectW(&ncm.lfMessageFont);
- desc.dialog_height = 0;
- desc.dialog_width = max(taskconfig->cxWidth, DIALOG_MIN_WIDTH);
- desc.dialog_width = min(desc.dialog_width, screen_width);
- desc.default_button = NULL;
+ hdc = GetDC(dialog_info->hwnd);
+ SelectObject(hdc, dialog_info->font);
+ dialog_info->m.x_baseunit = GdiGetCharDimensions(hdc, NULL, &dialog_info->m.y_baseunit);
+ ReleaseDC(dialog_info->hwnd, hdc);
- size += taskdialog_add_main_instruction(&desc);
- size += taskdialog_add_content(&desc);
- size += taskdialog_add_buttons(&desc);
+ dialog_info->m.h_spacing = DIALOG_SPACING;
+ dialog_info->m.v_spacing = DIALOG_SPACING;
+ taskdialog_du_to_px(dialog_info, &dialog_info->m.h_spacing, &dialog_info->m.v_spacing);
- template = Alloc(size);
- if (!template)
+ if (taskconfig->dwFlags & TDF_CALLBACK_TIMER)
{
- taskdialog_clear_controls(&desc.controls);
- DeleteObject(desc.font);
- return NULL;
+ SetTimer(hwnd, ID_TIMER, DIALOG_TIMER_MS, NULL);
+ dialog_info->last_timer_tick = GetTickCount();
}
- template->style = DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_SYSMENU;
- template->cdit = desc.control_count;
- template->x = (ref_rect.left + ref_rect.right + desc.dialog_width) / 2;
- template->y = (ref_rect.top + ref_rect.bottom + desc.dialog_height) / 2;
- template->cx = desc.dialog_width;
- template->cy = desc.dialog_height;
-
- ptr = (char *)(template + 1);
- ptr += 2; /* menu */
- ptr += 2; /* class */
- template_write_data(&ptr, titleW, title_size);
- template_write_data(&ptr, &fontsize, sizeof(fontsize));
-
- /* write control entries */
- LIST_FOR_EACH_ENTRY_SAFE(control, control2, &desc.controls, struct taskdialog_control, entry)
- {
- ALIGN_POINTER(ptr, 3);
-
- template_write_data(&ptr, control->template, control->template_size);
-
- /* list item won't be needed later */
- list_remove(&control->entry);
- Free(control->template);
- Free(control);
- }
-
- DeleteObject(desc.font);
- return template;
+ taskdialog_add_main_icon(dialog_info);
+ taskdialog_add_main_instruction(dialog_info);
+ taskdialog_add_content(dialog_info);
+ taskdialog_add_expanded_info(dialog_info);
+ taskdialog_add_progress_bar(dialog_info);
+ taskdialog_add_radio_buttons(dialog_info);
+ taskdialog_add_command_links(dialog_info);
+ taskdialog_add_expando_button(dialog_info);
+ taskdialog_add_verification_box(dialog_info);
+ taskdialog_add_buttons(dialog_info);
+ taskdialog_add_footer_icon(dialog_info);
+ taskdialog_add_footer_text(dialog_info);
+
+ /* Set default button */
+ if (!dialog_info->default_button && dialog_info->command_links)
+ dialog_info->default_button = dialog_info->command_links[0];
+ if (!dialog_info->default_button) dialog_info->default_button = dialog_info->buttons[0];
+ SendMessageW(dialog_info->hwnd, WM_NEXTDLGCTL, (WPARAM)dialog_info->default_button, TRUE);
+ id = GetWindowLongW(dialog_info->default_button, GWLP_ID);
+ SendMessageW(dialog_info->hwnd, DM_SETDEFID, id, 0);
+
+ dialog_info->has_cancel =
+ (taskconfig->dwFlags & TDF_ALLOW_DIALOG_CANCELLATION)
+ || taskdialog_find_button(dialog_info->command_links, dialog_info->command_link_count, IDCANCEL)
+ || taskdialog_find_button(dialog_info->buttons, dialog_info->button_count, IDCANCEL);
+
+ if (!dialog_info->has_cancel) DeleteMenu(GetSystemMenu(hwnd, FALSE), SC_CLOSE, MF_BYCOMMAND);
+
+ taskdialog_layout(dialog_info);
}
-static HRESULT taskdialog_notify(struct taskdialog_info *dialog_info, UINT notification, WPARAM wparam, LPARAM lparam)
+static BOOL CALLBACK takdialog_destroy_control(HWND hwnd, LPARAM lParam)
{
- return dialog_info->callback ? dialog_info->callback(dialog_info->hwnd, notification, wparam, lparam,
- dialog_info->callback_data) : S_OK;
+ DestroyWindow(hwnd);
+ return TRUE;
}
-static void taskdialog_on_button_click(struct taskdialog_info *dialog_info, WORD command_id)
+static void taskdialog_destroy(struct taskdialog_info *dialog_info)
{
- if (taskdialog_notify(dialog_info, TDN_BUTTON_CLICKED, command_id, 0) == S_OK)
- EndDialog(dialog_info->hwnd, command_id);
+ EnumChildWindows(dialog_info->hwnd, takdialog_destroy_control, 0);
+
+ if (dialog_info->taskconfig->dwFlags & TDF_CALLBACK_TIMER) KillTimer(dialog_info->hwnd, ID_TIMER);
+ if (dialog_info->font) DeleteObject(dialog_info->font);
+ if (dialog_info->main_instruction_font) DeleteObject(dialog_info->main_instruction_font);
+ Free(dialog_info->buttons);
+ Free(dialog_info->radio_buttons);
+ Free(dialog_info->command_links);
+ Free(dialog_info->expanded_text);
+ Free(dialog_info->collapsed_text);
}
static INT_PTR CALLBACK taskdialog_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static const WCHAR taskdialog_info_propnameW[] = {'T','a','s','k','D','i','a','l','o','g','I','n','f','o',0};
struct taskdialog_info *dialog_info;
+ LRESULT result;
TRACE("hwnd=%p msg=0x%04x wparam=%lx lparam=%lx\n", hwnd, msg, wParam, lParam);
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;
}
/***********************************************************************
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,
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;
}
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);
-}
#include "winreg.h"
#include "wingdi.h"
#include "winuser.h"
-#include "wine/unicode.h"
#include "winnls.h"
#include "commctrl.h"
#include "comctl32.h"
static UINT
-TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr)
+TOOLBAR_TranslateState(const TBUTTON_INFO *btnPtr, BOOL captured)
{
UINT retstate = 0;
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;
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;
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);
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)
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)
{
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));
{
if (lpText)
{
- ret = strlenW (lpText);
- if (lpStr) strcpyW (lpStr, lpText);
+ ret = lstrlenW (lpText);
+ if (lpStr) lstrcpyW (lpStr, lpText);
}
}
else
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)
lpszStr += 2;
continue;
}
- if (!strncmpiW(lpszStr, wszAccel, iLen))
+ if (!wcsnicmp(lpszStr, wszAccel, iLen))
{
*pIDButton = btnPtr->idCommand;
return TRUE;
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)
{
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))
{
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;
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);
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);
{
WCHAR wszBuffer[INFOTIPSIZE+1];
NMTBGETINFOTIPW tbgit;
- unsigned int len; /* in chars */
wszBuffer[0] = '\0';
wszBuffer[INFOTIPSIZE] = '\0';
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
{
CHAR szBuffer[INFOTIPSIZE+1];
NMTBGETINFOTIPA tbgit;
- unsigned int len; /* in chars */
szBuffer[0] = '\0';
szBuffer[INFOTIPSIZE] = '\0';
!(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));
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;
}
#include <stdarg.h>
#include <string.h>
+#include <stdlib.h>
#include "windef.h"
#include "winbase.h"
-#include "wine/unicode.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
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)
{
if (!(GetWindowLongW(infoPtr->hwndSelf, GWL_STYLE) & TTS_NOPREFIX)) {
WCHAR *ptrW;
- if ((ptrW = strchrW(buffer, '\t')))
+ if ((ptrW = wcschr(buffer, '\t')))
*ptrW = 0;
}
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,
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;
}
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);
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)
{
/* 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 */
{
if (isW)
{
- size = (strlenW(pszTitle)+1)*sizeof(WCHAR);
+ size = (lstrlenW(pszTitle)+1)*sizeof(WCHAR);
infoPtr->pszTitle = Alloc(size);
if (!infoPtr->pszTitle)
return FALSE;
return TRUE;
}
-
static LRESULT
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;
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 */
TTTOOL_INFO *toolPtr;
UINT i;
- /* free tools */
- if (infoPtr->tools) {
- for (i = 0; i < infoPtr->uNumTools; i++) {
- toolPtr = &infoPtr->tools[i];
- if (toolPtr->lpszText) {
- if ( (toolPtr->lpszText != LPSTR_TEXTCALLBACKW) &&
- !IS_INTRESOURCE(toolPtr->lpszText) )
- {
- Free (toolPtr->lpszText);
- toolPtr->lpszText = NULL;
- }
- }
-
- TOOLTIPS_ResetSubclass (toolPtr);
- }
+ for (i = 0; i < infoPtr->uNumTools; i++)
+ {
+ toolPtr = &infoPtr->tools[i];
- Free (infoPtr->tools);
+ TOOLTIPS_FreeToolText (toolPtr);
+ TOOLTIPS_ResetSubclass (toolPtr);
}
+ Free (infoPtr->tools);
+
/* free title string */
Free (infoPtr->pszTitle);
/* free title icon if not a standard one */
static inline LRESULT
TOOLTIPS_GetTextLength(const TOOLTIPS_INFO *infoPtr)
{
- return strlenW(infoPtr->szTipText);
+ return lstrlenW(infoPtr->szTipText);
}
/******************************************************************
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;
HWND hwndBuddyLA;
HWND hwndBuddyRB;
INT fLocation;
- INT flags;
+ DWORD flags;
BOOL bUnicode;
- BOOL bFocussed;
RECT rcChannel;
RECT rcSelection;
RECT rcThumb;
/* 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
}
/* draw focus rectangle */
- if (infoPtr->bFocussed) {
+ if (infoPtr->flags & TB_IS_FOCUSED) {
DrawFocusRect(hdc, &rcClient);
}
}
-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;
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;
}
return 0;
}
-
static inline LRESULT
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)
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);
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);
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);
TRACKBAR_KillFocus (TRACKBAR_INFO *infoPtr)
{
TRACE("\n");
- infoPtr->bFocussed = FALSE;
+ infoPtr->flags &= ~TB_IS_FOCUSED;
TRACKBAR_InvalidateAll(infoPtr);
return 0;
TRACKBAR_SetFocus (TRACKBAR_INFO *infoPtr)
{
TRACE("\n");
- infoPtr->bFocussed = TRUE;
+ infoPtr->flags |= TB_IS_FOCUSED;
TRACKBAR_InvalidateAll(infoPtr);
return 0;
* Scroll (instead of repaint) as much as possible.
*/
-#include "config.h"
-#include "wine/port.h"
-
#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#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"
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);
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 */
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)
infoPtr->maxVisibleOrder = order;
- for (item = start; item != NULL;
+ for (item = infoPtr->root->firstChild; item != NULL;
item = TREEVIEW_GetNextListItem(infoPtr, item))
{
TREEVIEW_ComputeItemRect(infoPtr, 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)
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)
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))
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);
}
}
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,
hOldFont = SelectObject(hdc, hFont);
}
- if (GetTextExtentPoint32W(hdc, buffer, strlenW(buffer), &sz))
+ if (GetTextExtentPoint32W(hdc, buffer, lstrlenW(buffer), &sz))
{
TEXTMETRICW textMetric;
/* 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);
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);
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;
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
{
{
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)
{
editedItem->pszText = ptr;
editedItem->cchTextMax = iLength + 1;
- strcpyW(editedItem->pszText, newText);
+ lstrcpyW(editedItem->pszText, newText);
TREEVIEW_ComputeTextWidth(infoPtr, editedItem, 0);
}
}
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);
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);
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;
}
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);
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;
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "uxtheme.h"
#include "vssym32.h"
#include "wine/heap.h"
-#include "wine/unicode.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(updown);
* 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);
/*
/*
* 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;
}
}
*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;
}
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
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)
#include "v6util.h"
#include "msg.h"
+#ifdef __REACTOS__
+#define WM_CTLCOLOR 0x0019
+#endif
+
#define EDITBOX_SEQ_INDEX 0
#define NUM_MSG_SEQUENCES 1
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
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);
UnregisterClassA(ComboExTestClass, GetModuleHandleA(NULL));
DestroyWindow(hMainWnd);
+ DeleteObject(brush_red);
}
static void test_comboex_subclass(void)
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;
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);
}
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)
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);
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);
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)
{
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);
}
}
+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;
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);
{
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");
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
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);
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);
#include "v6util.h"
#include "resources.h"
-#ifdef __REACTOS__
-#include <ole2.h>
-#endif
-
#define IMAGELIST_MAGIC (('L' << 8) | 'I')
#include "pshpack2.h"
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
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);
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);
}
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");
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);