[PATCH v2 3/4] shell32/autocomplete: Use an owner-drawn listbox for the dropdown
Gabriel Ivăncescu
gabrielopcode at gmail.com
Thu Mar 28 07:24:31 CDT 2019
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
dlls/shell32/autocomplete.c | 72 ++++++++++++++++++++++++++++++++++---
1 file changed, 68 insertions(+), 4 deletions(-)
diff --git a/dlls/shell32/autocomplete.c b/dlls/shell32/autocomplete.c
index 0ad8a9d..1a88a1d 100644
--- a/dlls/shell32/autocomplete.c
+++ b/dlls/shell32/autocomplete.c
@@ -61,6 +61,7 @@ typedef struct
BOOL enabled;
UINT enum_strs_num;
WCHAR **enum_strs;
+ WCHAR **listbox_strs;
HWND hwndEdit;
HWND hwndListBox;
HWND hwndListBoxOwner;
@@ -296,7 +297,6 @@ static void show_listbox(IAutoCompleteImpl *ac)
UINT cnt, width, height;
GetWindowRect(ac->hwndEdit, &r);
- SendMessageW(ac->hwndListBox, LB_CARETOFF, 0, 0);
/* Windows XP displays 7 lines at most, then it uses a scroll bar */
cnt = SendMessageW(ac->hwndListBox, LB_GETCOUNT, 0, 0);
@@ -306,6 +306,60 @@ static void show_listbox(IAutoCompleteImpl *ac)
SetWindowPos(ac->hwndListBoxOwner, HWND_TOP, r.left, r.bottom + 1, width, height, SWP_SHOWWINDOW);
}
+static void set_listbox_font(IAutoCompleteImpl *ac, HFONT font)
+{
+ /* We have to calculate the item height manually due to owner-drawn */
+ HFONT old_font = NULL;
+ UINT height = 16;
+ HDC hdc;
+
+ if ((hdc = GetDCEx(ac->hwndListBox, 0, DCX_CACHE)))
+ {
+ TEXTMETRICW metrics;
+ if (font) old_font = SelectObject(hdc, font);
+ if (GetTextMetricsW(hdc, &metrics))
+ height = metrics.tmHeight;
+ if (old_font) SelectObject(hdc, old_font);
+ ReleaseDC(ac->hwndListBox, hdc);
+ }
+ SendMessageW(ac->hwndListBox, WM_SETFONT, (WPARAM)font, FALSE);
+ SendMessageW(ac->hwndListBox, LB_SETITEMHEIGHT, 0, height);
+}
+
+static BOOL draw_listbox_item(IAutoCompleteImpl *ac, DRAWITEMSTRUCT *info, UINT id)
+{
+ COLORREF old_text, old_bk;
+ HDC hdc = info->hDC;
+ UINT state;
+ WCHAR *str;
+
+ if (info->CtlType != ODT_LISTBOX || info->CtlID != id ||
+ id != (UINT)GetWindowLongPtrW(ac->hwndListBox, GWLP_ID))
+ return FALSE;
+
+ if ((INT)info->itemID < 0 || info->itemAction == ODA_FOCUS)
+ return TRUE;
+
+ state = info->itemState;
+ if (state & ODS_SELECTED)
+ {
+ old_bk = SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
+ old_text = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+
+ str = ac->listbox_strs[info->itemID];
+ ExtTextOutW(hdc, info->rcItem.left + 1, info->rcItem.top,
+ ETO_OPAQUE | ETO_CLIPPED, &info->rcItem, str,
+ strlenW(str), NULL);
+
+ if (state & ODS_SELECTED)
+ {
+ SetBkColor(hdc, old_bk);
+ SetTextColor(hdc, old_text);
+ }
+ return TRUE;
+}
+
static size_t format_quick_complete(WCHAR *dst, const WCHAR *qc, const WCHAR *str, size_t str_len)
{
/* Replace the first %s directly without using snprintf, to avoid
@@ -540,6 +594,8 @@ static BOOL display_matching_strs(IAutoCompleteImpl *ac, WCHAR *text, UINT len,
SendMessageW(ac->hwndListBox, WM_SETREDRAW, FALSE, 0);
SendMessageW(ac->hwndListBox, LB_RESETCONTENT, 0, 0);
+
+ ac->listbox_strs = str + start;
SendMessageW(ac->hwndListBox, LB_INITSTORAGE, end - start, 0);
for (; start < end; start++)
SendMessageW(ac->hwndListBox, LB_INSERTSTRING, -1, (LPARAM)str[start]);
@@ -581,7 +637,11 @@ static void autocomplete_text(IAutoCompleteImpl *ac, HWND hwnd, enum autoappend_
size = len + 1;
if (!(text = heap_alloc(size * sizeof(WCHAR))))
+ {
+ /* Reset the listbox to prevent potential crash from ResetEnumerator */
+ SendMessageW(ac->hwndListBox, LB_RESETCONTENT, 0, 0);
return;
+ }
len = SendMessageW(hwnd, WM_GETTEXT, size, (LPARAM)text);
if (len + 1 != size)
text = heap_realloc(text, (len + 1) * sizeof(WCHAR));
@@ -780,7 +840,7 @@ static LRESULT APIENTRY ACEditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
break;
case WM_SETFONT:
if (This->hwndListBox)
- SendMessageW(This->hwndListBox, WM_SETFONT, wParam, lParam);
+ set_listbox_font(This, (HFONT)wParam);
break;
case WM_DESTROY:
{
@@ -828,6 +888,10 @@ static LRESULT APIENTRY ACLBoxOwnerSubclassProc(HWND hwnd, UINT uMsg, WPARAM wPa
switch (uMsg)
{
+ case WM_DRAWITEM:
+ if (draw_listbox_item(This, (DRAWITEMSTRUCT*)lParam, wParam))
+ return TRUE;
+ break;
case WM_SIZE:
SetWindowPos(This->hwndListBox, NULL, 0, 0, LOWORD(lParam), HIWORD(lParam),
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_DEFERERASE);
@@ -848,7 +912,7 @@ static void create_listbox(IAutoCompleteImpl *This)
/* FIXME : The listbox should be resizable with the mouse. WS_THICKFRAME looks ugly */
This->hwndListBox = CreateWindowExW(0, WC_LISTBOXW, NULL,
- WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT,
+ WS_CHILD | WS_VISIBLE | WS_VSCROLL | LBS_HASSTRINGS | LBS_OWNERDRAWFIXED | LBS_NOINTEGRALHEIGHT,
0, 0, 0, 0, This->hwndListBoxOwner, NULL, shell32_hInstance, NULL);
if (This->hwndListBox) {
@@ -865,7 +929,7 @@ static void create_listbox(IAutoCompleteImpl *This)
/* Use the same font as the edit control, as it gets destroyed before it anyway */
edit_font = (HFONT)SendMessageW(This->hwndEdit, WM_GETFONT, 0, 0);
if (edit_font)
- SendMessageW(This->hwndListBox, WM_SETFONT, (WPARAM)edit_font, FALSE);
+ set_listbox_font(This, edit_font);
return;
}
--
2.20.1
More information about the wine-devel
mailing list