From: Vladislav Timonin timoninvlad@yandex.ru
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50338
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51855 --- dlls/comdlg32/Makefile.in | 2 +- dlls/comdlg32/comdlg32.rc | 3 + dlls/comdlg32/navbar.c | 323 +++++++++++++++++++++++++++++++++++++- dlls/comdlg32/navbar.h | 3 + 4 files changed, 325 insertions(+), 6 deletions(-)
diff --git a/dlls/comdlg32/Makefile.in b/dlls/comdlg32/Makefile.in index 953796cec07..0457664bdd3 100644 --- a/dlls/comdlg32/Makefile.in +++ b/dlls/comdlg32/Makefile.in @@ -1,7 +1,7 @@ EXTRADEFS = -D_COMDLG32_ MODULE = comdlg32.dll IMPORTLIB = comdlg32 -IMPORTS = uuid shell32 shlwapi comctl32 winspool user32 gdi32 advapi32 +IMPORTS = uuid shell32 shlwapi comctl32 winspool user32 gdi32 advapi32 kernelbase DELAYIMPORTS = ole32
C_SRCS = \ diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc index 6782f03799d..4523d57598c 100644 --- a/dlls/comdlg32/comdlg32.rc +++ b/dlls/comdlg32/comdlg32.rc @@ -153,6 +153,9 @@ STRINGTABLE { IDS_BACK "Back" IDS_FORWARD "Forward" + IDS_COPY_ADDRESS_AS_TEXT "Copy address as text" + IDS_EDIT_ADDRESS "Edit address" + IDS_WINE_CANT_FIND_ADDRESS "Wine can't find '%s'." }
/* diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 85798397685..60bcca0de8c 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -26,6 +26,7 @@ #include "filedlgbrowser.h" #include "shlwapi.h" #include "commoncontrols.h" +#include "pathcch.h"
#include "wine/debug.h" #include "wine/list.h" @@ -62,6 +63,8 @@ typedef struct {
HWND overflow_hwnd; HMENU overflow_menu; + + HWND pathedit_hwnd; } NAVBAR_INFO;
struct crumb { @@ -101,6 +104,280 @@ static void set_title_and_add_tooltip(NAVBAR_INFO *info, HWND window, UINT strin SendMessageW(info->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo); }
+static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp); + +static void NAVBAR_PATHEDIT_SetCurrentPath(NAVBAR_INFO *info) +{ + struct crumb *crumb; + WCHAR cur_path[MAX_PATH]; + + crumb = LIST_ENTRY(list_tail(&info->crumbs), struct crumb, entry); + SHGetPathFromIDListW(crumb->pidl, cur_path); + SetWindowTextW(info->pathedit_hwnd, cur_path); + SendMessageW(info->pathedit_hwnd, EM_SETSEL, 0, -1); /* select all */ +} + +static void NAVBAR_PATHEDIT_Edit(NAVBAR_INFO *info) +{ + struct crumb *crumb; + HDWP hdwp; + + NAVBAR_PATHEDIT_SetCurrentPath(info); + + /* show path edit, hide overflow and crumbs */ + hdwp = BeginDeferWindowPos(2 + info->crumbs_total_n + 1); + hdwp = DeferWindowPos(hdwp, info->pathedit_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + hdwp = DeferWindowPos(hdwp, info->overflow_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + LIST_FOR_EACH_ENTRY(crumb, &info->crumbs, struct crumb, entry) + hdwp = DeferWindowPos(hdwp, crumb->hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + hdwp = DeferWindowPos(hdwp, info->background_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + EndDeferWindowPos(hdwp); + + SetFocus(info->pathedit_hwnd); +} + +static void NAVBAR_PATHEDIT_Copy(NAVBAR_INFO *info) +{ + NAVBAR_PATHEDIT_SetCurrentPath(info); + SendMessageW(info->pathedit_hwnd, WM_COPY, 0, 0); +} + +static void NAVBAR_PATHEDIT_Dismiss(NAVBAR_INFO *info) +{ + HDWP hdwp; + + if (!IsWindowVisible(info->pathedit_hwnd)) + return; + + /* hide path edit, show overflow and crumbs */ + hdwp = BeginDeferWindowPos(2 + info->crumbs_total_n + 1); + hdwp = DeferWindowPos(hdwp, info->pathedit_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); + hdwp = DeferWindowPos(hdwp, info->overflow_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + hdwp = NAVBAR_DoLayout(info, hdwp); + EndDeferWindowPos(hdwp); +} + +static LRESULT NAVBAR_PATHEDIT_KillFocus(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + NAVBAR_PATHEDIT_Dismiss(info); + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static void NAVBAR_PATHEDIT_GoTo(NAVBAR_INFO *info) +{ + WCHAR *text = NULL, *expanded = NULL, *canonicalized = NULL; + INT text_len = 0; + DWORD expanded_len; + ITEMIDLIST *pidl = NULL; + HRESULT hr; + + text_len = GetWindowTextLengthW(info->pathedit_hwnd); + if (!text_len) + goto exit; + + text = HeapAlloc(GetProcessHeap(), 0, (text_len + 1) * sizeof(WCHAR)); + if (!text) + goto exit; + + if (!GetWindowTextW(info->pathedit_hwnd, text, text_len + 1)) + goto cleanup; + + TRACE("text %s\n", debugstr_w(text)); + + StrTrimW(text, L" "); + + expanded_len = ExpandEnvironmentStringsW(text, NULL, 0); + if (!expanded_len) + goto cleanup; + + expanded = HeapAlloc(GetProcessHeap(), 0, expanded_len * sizeof(WCHAR)); + if (!expanded) + goto cleanup; + + expanded_len = ExpandEnvironmentStringsW(text, expanded, expanded_len); + if (!expanded_len) + goto cleanup; + + TRACE("expanded %s\n", debugstr_w(expanded)); + + hr = PathAllocCanonicalize(expanded, PATHCCH_ALLOW_LONG_PATHS, &canonicalized); + if (FAILED(hr)) + goto cleanup; + + TRACE("canonicalized %s\n", debugstr_w(canonicalized)); + + /* check if we have a valid path */ + if (GetFileAttributesW(canonicalized) == INVALID_FILE_ATTRIBUTES) + { + WCHAR spec[MAX_PATH + 128] = {0}, + caption[MAX_PATH + 128] = {0}; + LoadStringW(COMDLG32_hInstance, IDS_WINE_CANT_FIND_ADDRESS, spec, ARRAY_SIZE(spec)); + wsprintfW(caption, spec, text); + MessageBoxW(info->parent_hwnd, caption, NULL, MB_ICONERROR | MB_OK); + goto cleanup; + } + + hr = SHILCreateFromPath(canonicalized, &pidl, 0); + if (FAILED(hr)) + goto cleanup; + + TRACE("pidl %p\n", pidl); + + SendMessageW(info->parent_hwnd, NBN_NAVPIDL, 0, (LPARAM)pidl); + ILFree(pidl); +cleanup: + if (text) + HeapFree(GetProcessHeap(), 0, text); + if (expanded) + HeapFree(GetProcessHeap(), 0, expanded); + if (canonicalized) + HeapFree(GetProcessHeap(), 0, canonicalized); +exit: + NAVBAR_PATHEDIT_Dismiss(info); +} + +static LRESULT NAVBAR_PATHEDIT_GetDlgCode(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (wparam) + { + case VK_ESCAPE: + NAVBAR_PATHEDIT_Dismiss(info); + SetFocus(info->parent_hwnd); + return DLGC_WANTMESSAGE; /* prevent dialog from closing */ + case VK_RETURN: + NAVBAR_PATHEDIT_GoTo(info); + break; + } + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static INT NAVBAR_PATHEDIT_NCOffset(HWND hwnd) +{ + RECT hwnd_rc; + HDC hdc; + HGDIOBJ old_font, gui_font = GetStockObject(DEFAULT_GUI_FONT); + TEXTMETRICW tm; + INT cy_edge = GetSystemMetrics(SM_CYEDGE), + cy_border = GetSystemMetrics(SM_CYBORDER); + + GetWindowRect(hwnd, &hwnd_rc); + + /* get font height */ + hdc = GetDC(0); + old_font = SelectObject(hdc, gui_font); + GetTextMetricsW(hdc, &tm); + SelectObject(hdc, old_font); + ReleaseDC(0, hdc); + + /* offset to vertically center the text */ + return (hwnd_rc.bottom - hwnd_rc.top - tm.tmHeight - (cy_edge * 2) - (cy_border * 2)) / 2; +} + +static LRESULT NAVBAR_PATHEDIT_NCCalcSize(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + NCCALCSIZE_PARAMS *ncp = (NCCALCSIZE_PARAMS *)lparam; + + if (!wparam) + goto exit; + + /* vertically center the text */ + ncp->rgrc[0].top += NAVBAR_PATHEDIT_NCOffset(hwnd); + +exit: + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static LRESULT NAVBAR_PATHEDIT_NCPaint(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + RECT hwnd_rc; + HDC hdc = GetWindowDC(hwnd); + + GetClientRect(hwnd, &hwnd_rc); + hwnd_rc.right += GetSystemMetrics(SM_CXEDGE); + hwnd_rc.top += GetSystemMetrics(SM_CYEDGE); + hwnd_rc.bottom = hwnd_rc.top + NAVBAR_PATHEDIT_NCOffset(hwnd); + + /* depending on theme, e.g. classic theme, */ + /* NC background color might differ from window background, */ + /* paint over the area that we shifted in WM_NCCALCSIZE */ + FillRect(hdc, &hwnd_rc, GetSysColorBrush(COLOR_WINDOW)); + ReleaseDC(hwnd, hdc); + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static LRESULT NAVBAR_PATHEDIT_NCHitTest(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + POINT screen_pt = { .x = LOWORD(lparam), + .y = HIWORD(lparam) }; + POINT client_pt = screen_pt; + + ScreenToClient(hwnd, &client_pt); + if (client_pt.y < 0) + { + /* what we shifted in WM_NCCALCSIZE is now not interactable, fix that */ + screen_pt.y += NAVBAR_PATHEDIT_NCOffset(hwnd); + lparam = MAKELPARAM(screen_pt.x, screen_pt.y); + } + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static LRESULT CALLBACK NAVBAR_PATHEDIT_SubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id_subclass, DWORD_PTR ref_data) +{ + NAVBAR_INFO *info = (NAVBAR_INFO *)ref_data; + + switch (msg) + { + case WM_GETDLGCODE: return NAVBAR_PATHEDIT_GetDlgCode(hwnd, info, msg, wparam, lparam); + case WM_KILLFOCUS: return NAVBAR_PATHEDIT_KillFocus(hwnd, info, msg, wparam, lparam); + case WM_NCCALCSIZE: return NAVBAR_PATHEDIT_NCCalcSize(hwnd, msg, wparam, lparam); + case WM_NCPAINT: return NAVBAR_PATHEDIT_NCPaint(hwnd, msg, wparam, lparam); + case WM_NCHITTEST: return NAVBAR_PATHEDIT_NCHitTest(hwnd, msg, wparam, lparam); + } + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static LRESULT CALLBACK NAVBAR_RMBMENU_SubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id_subclass, DWORD_PTR ref_data) +{ + NAVBAR_INFO *info = (NAVBAR_INFO *)ref_data; + + switch (msg) + { + case WM_RBUTTONUP: + { + POINT pt = { .x = LOWORD(lparam), + .y = HIWORD(lparam) }; + HMENU menu = CreatePopupMenu(); + WCHAR buffer[128] = {0}; + static const INT IDC_RMBMENUCOPY = 200; + static const INT IDC_RMBMENUEDIT = 201; + + LoadStringW(COMDLG32_hInstance, IDS_COPY_ADDRESS_AS_TEXT, buffer, ARRAY_SIZE(buffer)); + AppendMenuW(menu, MF_STRING, IDC_RMBMENUCOPY, buffer); + + LoadStringW(COMDLG32_hInstance, IDS_EDIT_ADDRESS, buffer, ARRAY_SIZE(buffer)); + AppendMenuW(menu, MF_STRING, IDC_RMBMENUEDIT, buffer); + + ClientToScreen(hwnd, &pt); + switch (TrackPopupMenu(menu, TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL)) + { + case IDC_RMBMENUCOPY: + NAVBAR_PATHEDIT_Copy(info); + break; + case IDC_RMBMENUEDIT: + NAVBAR_PATHEDIT_Edit(info); + break; + } + } + } + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + static void NAVBAR_OVERFLOW_Insert(NAVBAR_INFO *info, ITEMIDLIST* pidl, WCHAR *display_name) { MENUITEMINFOW menu_item = {0}; @@ -184,6 +461,8 @@ static LRESULT NAVBAR_OVERFLOW_DrawIcon(HWND hwnd, NAVBAR_INFO *info, UINT msg,
static LRESULT CALLBACK NAVBAR_BACKGROUND_SubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id_subclass, DWORD_PTR ref_data) { + NAVBAR_INFO *info = (NAVBAR_INFO *)ref_data; + switch (msg) { case WM_PAINT: @@ -199,6 +478,9 @@ static LRESULT CALLBACK NAVBAR_BACKGROUND_SubclassProc(HWND hwnd, UINT msg, WPAR
return FALSE; /* processed */ } + case WM_LBUTTONUP: + NAVBAR_PATHEDIT_Edit(info); + break; }
return DefSubclassProc(hwnd, msg, wparam, lparam); @@ -209,7 +491,7 @@ static void NAVBAR_CalcLayout(NAVBAR_INFO *info) RECT container_rc, overflow_rc; struct crumb *crumb1, *crumb2; INT padding, container_w, - buttons_w, max_crumbs_w, + buttons_w, overflow_w, max_crumbs_w, w = 0, crumbs_visible_n = 0, prev_x;
if (!GetWindowRect(info->container_hwnd, &container_rc)) @@ -224,12 +506,20 @@ static void NAVBAR_CalcLayout(NAVBAR_INFO *info)
if (!GetWindowRect(info->overflow_hwnd, &overflow_rc)) return; + overflow_w = overflow_rc.right - overflow_rc.left;
buttons_w = overflow_rc.right - padding; max_crumbs_w = container_w - buttons_w; if (max_crumbs_w < 0) return;
+ SetWindowPos(info->pathedit_hwnd, NULL, 0, 0, overflow_w + max_crumbs_w, info->container_h, SWP_NOMOVE); + + /* reserve some space on the right side for click-to-edit area */ + max_crumbs_w -= MulDiv(64, info->dpi_x, USER_DEFAULT_SCREEN_DPI); + if (max_crumbs_w < 0) + return; + LIST_FOR_EACH_ENTRY_REV(crumb1, &info->crumbs, struct crumb, entry) { INT next_w = w + crumb1->full_w; @@ -310,6 +600,11 @@ static void NAVBAR_CalcLayout(NAVBAR_INFO *info) else style |= BS_CENTER;
+ /* when resizing path edit, crumbs can briefly show through, apply WS_CLIPSIBLINGS to avoid that. */ + /* when crumb reappears during resizing, it can fail to repaint itself if WS_CLIPSIBLINGS was applied. */ + if (IsWindowVisible(info->pathedit_hwnd)) + style |= WS_CLIPSIBLINGS; + SetWindowLongPtrW(crumb1->hwnd, GWL_STYLE, style);
crumb1->x = prev_x - crumb1->current_w; @@ -431,16 +726,19 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
x += cs->cy + 1; info->background_hwnd = CreateWindowExW(0, WC_STATICW, NULL, - WS_CHILD | WS_VISIBLE, + WS_CHILD | WS_VISIBLE | SS_NOTIFY, x, 0, 0, cs->cy, hwnd, 0, COMDLG32_hInstance, NULL); SetWindowSubclass(info->background_hwnd, NAVBAR_BACKGROUND_SubclassProc, 0, (DWORD_PTR)info); + SetWindowSubclass(info->background_hwnd, NAVBAR_RMBMENU_SubclassProc, 1, (DWORD_PTR)info);
info->overflow_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, WS_CHILD | WS_VISIBLE | BS_ICON | BS_SPLITBUTTON, x, 0, cs->cy + MulDiv(6, info->dpi_x, USER_DEFAULT_SCREEN_DPI), cs->cy, hwnd, (HMENU)IDC_OVERFLOW, COMDLG32_hInstance, NULL); SendMessageW(info->overflow_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + set_title_and_add_tooltip(info, info->overflow_hwnd, IDS_EDIT_ADDRESS); + SetWindowSubclass(info->overflow_hwnd, NAVBAR_RMBMENU_SubclassProc, 0, (DWORD_PTR)info);
info->overflow_menu = CreatePopupMenu(); menu_info.cbSize = sizeof(MENUINFO); @@ -448,6 +746,14 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) menu_info.dwStyle = MNS_NOCHECK | MNS_NOTIFYBYPOS; SetMenuInfo(info->overflow_menu, &menu_info);
+ info->pathedit_hwnd = CreateWindowExW(WS_EX_CLIENTEDGE, + WC_EDITW, NULL, + WS_CHILD | ES_AUTOHSCROLL, + x, 0, 0, cs->cy, + hwnd, 0, COMDLG32_hInstance, NULL); + SendMessageW(info->pathedit_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + SetWindowSubclass(info->pathedit_hwnd, NAVBAR_PATHEDIT_SubclassProc, 0, (DWORD_PTR)info); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -482,9 +788,12 @@ static LRESULT NAVBAR_Size(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam
NAVBAR_CalcLayout(info);
- hdwp = BeginDeferWindowPos(info->crumbs_total_n + 1); - hdwp = NAVBAR_DoLayout(info, hdwp); - EndDeferWindowPos(hdwp); + if (!IsWindowVisible(info->pathedit_hwnd)) + { + hdwp = BeginDeferWindowPos(info->crumbs_total_n + 1); + hdwp = NAVBAR_DoLayout(info, hdwp); + EndDeferWindowPos(hdwp); + }
return TRUE; } @@ -509,6 +818,9 @@ static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa SendMessageW(info->parent_hwnd, NBN_NAVPIDL, 0, (LPARAM)pidl); break; } + case IDC_OVERFLOW: + NAVBAR_PATHEDIT_Edit(info); + break; }
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -583,6 +895,7 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa SendMessageW(crumb1->hwnd, WM_SETFONT, (LPARAM)gui_font, FALSE); SendMessageW(crumb1->hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&full_size); SetWindowLongPtrW(crumb1->hwnd, GWLP_USERDATA, (LPARAM)crumb1->pidl); + SetWindowSubclass(crumb1->hwnd, NAVBAR_RMBMENU_SubclassProc, 0, (DWORD_PTR)info);
crumb1->full_w = full_size.cx + padding; crumb1->current_w = crumb1->full_w; diff --git a/dlls/comdlg32/navbar.h b/dlls/comdlg32/navbar.h index a077cf58095..aa89a352cf5 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -37,6 +37,9 @@ /* strings */ #define IDS_BACK 2000 #define IDS_FORWARD 2001 +#define IDS_COPY_ADDRESS_AS_TEXT 2004 +#define IDS_EDIT_ADDRESS 2005 +#define IDS_WINE_CANT_FIND_ADDRESS 2006
/* bitmaps */ #define IDB_NAVBAR 3000