Implements navigation bar featuring: - back/forward buttons - go up button - address crumbs - hides crumbs when there's not enough space to display all of them - always shows at least 2 crumbs - truncating them if needed - first crumb is at least 56px - aligns label to the left if truncated - right clicking on the crumb, or free space, opens a menu with `Copy address as text` and `Edit address` options - reuses existing crumbs to avoid some flickering - overflow menu - ~~first crumb is always moved here~~ - removes the split, if there are no items in the menu - hidden crumbs are moved here - left ~~or right~~ click on the button starts address editing - address edit - accepts an existing path - expands environment variables, e.g. `%APPDATA%\Microsoft` -> `C:\users%USERNAME%\AppData\Roaming\Microsoft` - errors with a `MessageBox` on nonexistent paths - refresh button - changes into go to button when editing address - DPI scaling
Screenshots:
![navbar-crumbs-classic](/uploads/8c9a806b57f8e456fcbfd3cbc1ef3bb7/navbar-crumbs-classic.png)
![navbar-crumbs-light](/uploads/9c8a7acf9b55ae5ac182219edd45b65d/navbar-crumbs-light.png)
![navbar-edit-classic](/uploads/f68309bbc9200a8d8d4e3cd692179409/navbar-edit-classic.png)
![navbar-edit-light](/uploads/6798e79e6d2498a052b0ecbc2545c6e4/navbar-edit-light.png)
![navbar-120dpi-classic](/uploads/72abee382ab6171ff9c1b99a9065c845/navbar-120dpi-classic.png)
![navbar-120dpi-light](/uploads/538a030bc2028f8654d6c35d6f2825dd/navbar-120dpi-light.png)
Remaining issues: - crumbs not lighting up when hovered sometimes - can be observed in current file dialog, hovering over dropdown arrow of a file type fixes it for a moment - crumbs flickering on address change sometimes - mostly when there's not enough space to display all of them - `DeferWindowPos` doesn't seem to be atomic in Wine, presumably is in native Windows? - unexpected address conversion for some PIDLs - e.g. `Desktop` -> `C:\users%USERNAME%\Desktop`, which is an entirely different location - or `My Computer` into an empty string - known folders are not handled by address edit, e.g. `Desktop`, `My Computer` - icons are not DPI scaled - while writing this: ``` 0024:fixme:bitmap:NtGdiCreateBitmap planes = 0 0024:err:system:user_check_not_lock BUG: holding USER lock wine/dlls/win32u/sysparams.c:396: user_check_not_lock: Assertion `0' failed. wine: Assertion failed at address F7FF8549 (thread 0024), starting debugger... ``` - can't reproduce, happened when clicking on an item in overflow menu - race condition? cosmic ray?
Addresses following bugs: - https://bugs.winehq.org/show_bug.cgi?id=29912 - https://bugs.winehq.org/show_bug.cgi?id=50338 - https://bugs.winehq.org/show_bug.cgi?id=51855 (partially? no search box)
-- v13: comdlg32: Add Ctrl+Backspace delete word to IFileDialog address edit. comdlg32: Use path word break in IFileDialog address edit. comctl32: Fix PathWordBreakProc. comdlg32: Keep IExplorerBrowser in IFileDialog focused after Backspace. fixup: comdlg32: Add address edit to IFileDialog navigation bar. comdlg32: Add refresh/go to button to IFileDialog navigation bar. comdlg32: Add address edit to IFileDialog navigation bar. comdlg32: Add breadcrumb overflow menu to IFileDialog navigation bar. comdlg32: Add address breadcrumbs to IFileDialog navigation bar. comdlg32: Add go up button to IFileDialog navigation bar. comdlg32: Add IFileDialog navigation bar. comdlg32: Enable visual styles when showing IFileDialog.
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/cdlg.h | 1 + dlls/comdlg32/cdlg32.c | 16 ++++++- dlls/comdlg32/comdlg32.manifest | 16 +++++++ dlls/comdlg32/comdlg32.rc | 3 ++ dlls/comdlg32/itemdlg.c | 84 +++++++++++++++++++++++++++++++++ 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 dlls/comdlg32/comdlg32.manifest
diff --git a/dlls/comdlg32/cdlg.h b/dlls/comdlg32/cdlg.h index 1ddd157b880..e47d685fac3 100644 --- a/dlls/comdlg32/cdlg.h +++ b/dlls/comdlg32/cdlg.h @@ -27,6 +27,7 @@ #define COMDLG32_Atom MAKEINTATOM(0xa000) /* MS uses this one to identify props */
extern HINSTANCE COMDLG32_hInstance DECLSPEC_HIDDEN; +extern HANDLE COMDLG32_hActCtx DECLSPEC_HIDDEN;
void COMDLG32_SetCommDlgExtendedError(DWORD err) DECLSPEC_HIDDEN; LPVOID COMDLG32_AllocMem(int size) __WINE_ALLOC_SIZE(1) DECLSPEC_HIDDEN; diff --git a/dlls/comdlg32/cdlg32.c b/dlls/comdlg32/cdlg32.c index f9619cc6a49..aebb3475674 100644 --- a/dlls/comdlg32/cdlg32.c +++ b/dlls/comdlg32/cdlg32.c @@ -40,6 +40,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
DECLSPEC_HIDDEN HINSTANCE COMDLG32_hInstance = 0; +HANDLE COMDLG32_hActCtx = INVALID_HANDLE_VALUE;
static DWORD COMDLG32_TlsIndex = TLS_OUT_OF_INDEXES;
@@ -72,6 +73,9 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved) switch(Reason) { case DLL_PROCESS_ATTACH: + { + ACTCTXW actctx = {0}; + COMDLG32_hInstance = hInstance; DisableThreadLibraryCalls(hInstance);
@@ -79,11 +83,21 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved)
/* SHELL */ GPA(COMDLG32_SHSimpleIDListFromPathAW, SHELL32_hInstance, (LPCSTR)162); - break;
+ actctx.cbSize = sizeof(actctx); + actctx.hModule = COMDLG32_hInstance; + actctx.lpResourceName = MAKEINTRESOURCEW(123); + actctx.dwFlags = ACTCTX_FLAG_HMODULE_VALID | ACTCTX_FLAG_RESOURCE_NAME_VALID; + COMDLG32_hActCtx = CreateActCtxW(&actctx); + if (COMDLG32_hActCtx == INVALID_HANDLE_VALUE) + ERR("failed to create activation context, last error %lu\n", GetLastError()); + + break; + } case DLL_PROCESS_DETACH: if (Reserved) break; if (COMDLG32_TlsIndex != TLS_OUT_OF_INDEXES) TlsFree(COMDLG32_TlsIndex); + ReleaseActCtx(&COMDLG32_hActCtx); break; } return TRUE; diff --git a/dlls/comdlg32/comdlg32.manifest b/dlls/comdlg32/comdlg32.manifest new file mode 100644 index 00000000000..f9e7b7cc115 --- /dev/null +++ b/dlls/comdlg32/comdlg32.manifest @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> +<assemblyIdentity type="win32" name="Microsoft.Windows.Shell.comdlg32" version="0.0.0.0"/> +<dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> +</dependency> +</assembly> diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc index a5d09faa563..474bafd8760 100644 --- a/dlls/comdlg32/comdlg32.rc +++ b/dlls/comdlg32/comdlg32.rc @@ -602,3 +602,6 @@ NETWORK ICON network.ico
/* @makedep: fontpics.bmp */ 38 BITMAP fontpics.bmp + +/* @makedep: comdlg32.manifest */ +123 RT_MANIFEST comdlg32.manifest diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 95778c2f358..1bb77fa430c 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -145,6 +145,8 @@ typedef struct FileDialogImpl { DWORD opendropdown_selection;
GUID client_guid; + + HANDLE user_actctx; } FileDialogImpl;
/************************************************************************** @@ -152,10 +154,14 @@ typedef struct FileDialogImpl { */ static HRESULT events_OnFileOk(FileDialogImpl *This) { + ULONG_PTR ctx_cookie; events_client *cursor; HRESULT hr = S_OK; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { TRACE("Notifying %p\n", cursor); @@ -164,6 +170,9 @@ static HRESULT events_OnFileOk(FileDialogImpl *This) break; }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + if(hr == E_NOTIMPL) hr = S_OK;
@@ -172,10 +181,14 @@ static HRESULT events_OnFileOk(FileDialogImpl *This)
static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder) { + ULONG_PTR ctx_cookie; events_client *cursor; HRESULT hr = S_OK; TRACE("%p (%p)\n", This, folder);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { TRACE("Notifying %p\n", cursor); @@ -184,6 +197,9 @@ static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder) break; }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + if(hr == E_NOTIMPL) hr = S_OK;
@@ -192,47 +208,72 @@ static HRESULT events_OnFolderChanging(FileDialogImpl *This, IShellItem *folder)
static void events_OnFolderChange(FileDialogImpl *This) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { TRACE("Notifying %p\n", cursor); IFileDialogEvents_OnFolderChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface); } + + if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); }
static void events_OnSelectionChange(FileDialogImpl *This) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { TRACE("Notifying %p\n", cursor); IFileDialogEvents_OnSelectionChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface); } + + if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); }
static void events_OnTypeChange(FileDialogImpl *This) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { TRACE("Notifying %p\n", cursor); IFileDialogEvents_OnTypeChange(cursor->pfde, (IFileDialog*)&This->IFileDialog2_iface); } + + if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); }
static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem) { + ULONG_PTR ctx_cookie; events_client *cursor; HRESULT hr = S_OK; FDE_OVERWRITE_RESPONSE response = FDEOR_DEFAULT; TRACE("%p %p\n", This, shellitem);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { TRACE("Notifying %p\n", cursor); @@ -242,6 +283,9 @@ static HRESULT events_OnOverwrite(FileDialogImpl *This, IShellItem *shellitem) break; }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + if(hr == E_NOTIMPL) hr = S_OK;
@@ -274,9 +318,13 @@ static inline HRESULT get_cctrl_event(IFileDialogEvents *pfde, IFileDialogContro
static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { IFileDialogControlEvents *pfdce; @@ -288,14 +336,21 @@ static HRESULT cctrl_event_OnButtonClicked(FileDialogImpl *This, DWORD ctl_id) } }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + return S_OK; }
static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DWORD item_id) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p %li %li\n", This, ctl_id, item_id);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { IFileDialogControlEvents *pfdce; @@ -307,14 +362,21 @@ static HRESULT cctrl_event_OnItemSelected(FileDialogImpl *This, DWORD ctl_id, DW } }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + return S_OK; }
static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_id, BOOL checked) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { IFileDialogControlEvents *pfdce; @@ -326,15 +388,22 @@ static HRESULT cctrl_event_OnCheckButtonToggled(FileDialogImpl *This, DWORD ctl_ } }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + return S_OK; }
static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This, DWORD ctl_id) { + ULONG_PTR ctx_cookie; events_client *cursor; TRACE("%p\n", This);
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + ActivateActCtx(This->user_actctx, &ctx_cookie); + LIST_FOR_EACH_ENTRY(cursor, &This->events_clients, events_client, entry) { IFileDialogControlEvents *pfdce; @@ -346,6 +415,9 @@ static HRESULT cctrl_event_OnControlActivating(FileDialogImpl *This, } }
+ if (This->user_actctx != INVALID_HANDLE_VALUE) + DeactivateActCtx(0, ctx_cookie); + return S_OK; }
@@ -2243,12 +2315,22 @@ static INT_PTR CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, static HRESULT create_dialog(FileDialogImpl *This, HWND parent) { INT_PTR res; + ULONG_PTR actctx_cookie = 0; + + GetCurrentActCtx(&This->user_actctx); + if (!ActivateActCtx(COMDLG32_hActCtx, &actctx_cookie)) + ERR("failed to activate activation context, last error %lx\n", GetLastError());
SetLastError(0); res = DialogBoxParamW(COMDLG32_hInstance, MAKEINTRESOURCEW(NEWFILEOPENV3ORD), parent, itemdlg_dlgproc, (LPARAM)This); This->dlg_hwnd = NULL; + + if (actctx_cookie != 0) + if (!DeactivateActCtx(0, actctx_cookie)) + ERR("failed to deactivate activation context, last error %lx\n", GetLastError()); + if(res == -1) { ERR("Failed to show dialog (LastError: %ld)\n", GetLastError()); @@ -4642,6 +4724,8 @@ static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **p return E_FAIL; }
+ fdimpl->user_actctx = INVALID_HANDLE_VALUE; + hr = IFileDialog2_QueryInterface(&fdimpl->IFileDialog2_iface, riid, ppv); IFileDialog2_Release(&fdimpl->IFileDialog2_iface); return hr;
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/Makefile.in | 2 + dlls/comdlg32/comdlg32.rc | 10 + dlls/comdlg32/itemdlg.c | 74 ++--- dlls/comdlg32/navbar.bmp | Bin 0 -> 9354 bytes dlls/comdlg32/navbar.c | 172 ++++++++++ dlls/comdlg32/navbar.h | 47 +++ dlls/comdlg32/navbar.svg | 676 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 937 insertions(+), 44 deletions(-) create mode 100644 dlls/comdlg32/navbar.bmp create mode 100644 dlls/comdlg32/navbar.c create mode 100644 dlls/comdlg32/navbar.h create mode 100644 dlls/comdlg32/navbar.svg
diff --git a/dlls/comdlg32/Makefile.in b/dlls/comdlg32/Makefile.in index 91a12f8a7e4..953796cec07 100644 --- a/dlls/comdlg32/Makefile.in +++ b/dlls/comdlg32/Makefile.in @@ -13,11 +13,13 @@ C_SRCS = \ finddlg.c \ fontdlg.c \ itemdlg.c \ + navbar.c \ printdlg.c
RC_SRCS = comdlg32.rc
SVG_SRCS = \ + navbar.svg \ pd32_collate.svg \ pd32_landscape.svg \ pd32_nocollate.svg \ diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc index 474bafd8760..6782f03799d 100644 --- a/dlls/comdlg32/comdlg32.rc +++ b/dlls/comdlg32/comdlg32.rc @@ -22,6 +22,7 @@ #include "cdlg.h" #include "colordlg.h" #include "filedlgbrowser.h" +#include "navbar.h"
#pragma makedep po
@@ -148,6 +149,12 @@ STRINGTABLE IDS_FONT_SIZE_INPUT "Font size has to be a number." }
+STRINGTABLE +{ + IDS_BACK "Back" + IDS_FORWARD "Forward" +} + /* * WARNING: DO NOT CHANGE THE SIZE OF THE STANDARD DIALOG TEMPLATES. */ @@ -603,5 +610,8 @@ NETWORK ICON network.ico /* @makedep: fontpics.bmp */ 38 BITMAP fontpics.bmp
+/* @makedep: navbar.bmp */ +IDB_NAVBAR BITMAP navbar.bmp + /* @makedep: comdlg32.manifest */ 123 RT_MANIFEST comdlg32.manifest diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 1bb77fa430c..1d7d3b5dab5 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -33,13 +33,13 @@ #include "commdlg.h" #include "cdlg.h" #include "filedlgbrowser.h" +#include "navbar.h"
#include "wine/debug.h" #include "wine/list.h"
-#define IDC_NAV_TOOLBAR 200 -#define IDC_NAVBACK 201 -#define IDC_NAVFORWARD 202 +#define IDC_NAVBAR 200 +#define NAVBAR_HEIGHT 30
#include <initguid.h> /* This seems to be another version of IID_IFileDialogCustomize. If @@ -1639,6 +1639,13 @@ static HRESULT init_custom_controls(FileDialogImpl *This) ERR("Failed to register RadioButtonList window class.\n"); }
+ if (!GetClassInfoW(COMDLG32_hInstance, WC_NAVBARW, &wc) || + wc.hInstance != COMDLG32_hInstance) + { + if (!NAVBAR_Register()) + ERR("Failed to register NavBar window class.\n"); + } + return S_OK; }
@@ -1710,7 +1717,7 @@ static void update_layout(FileDialogImpl *This) RECT dialog_rc; RECT cancel_rc, dropdown_rc, open_rc; RECT filetype_rc, filename_rc, filenamelabel_rc; - RECT toolbar_rc, ebrowser_rc, customctrls_rc; + RECT navbar_rc, ebrowser_rc, customctrls_rc; static const UINT vspacing = 4, hspacing = 4; static const UINT min_width = 320, min_height = 200; BOOL show_dropdown; @@ -1837,12 +1844,11 @@ static void update_layout(FileDialogImpl *This) filename_rc.bottom = filename_rc.top + filename_height; }
- hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR); - if(hwnd) - { - GetWindowRect(hwnd, &toolbar_rc); - MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2); - } + /* Navigation bar */ + navbar_rc.top = hspacing; + navbar_rc.bottom = navbar_rc.top + MulDiv(NAVBAR_HEIGHT, This->dpi_y, USER_DEFAULT_SCREEN_DPI); + navbar_rc.left = dialog_rc.left + hspacing; + navbar_rc.right = dialog_rc.right - hspacing;
/* The custom controls */ customctrls_rc.left = dialog_rc.left + hspacing; @@ -1853,7 +1859,7 @@ static void update_layout(FileDialogImpl *This)
/* The ExplorerBrowser control. */ ebrowser_rc.left = dialog_rc.left + hspacing; - ebrowser_rc.top = toolbar_rc.bottom + vspacing; + ebrowser_rc.top = navbar_rc.bottom + vspacing; ebrowser_rc.right = dialog_rc.right - hspacing; ebrowser_rc.bottom = customctrls_rc.top - vspacing;
@@ -1862,7 +1868,7 @@ static void update_layout(FileDialogImpl *This) */
/* FIXME: The Save Dialog uses a slightly different layout. */ - hdwp = BeginDeferWindowPos(7); + hdwp = BeginDeferWindowPos(8);
if(hdwp && This->peb) IExplorerBrowser_SetRect(This->peb, &hdwp, ebrowser_rc); @@ -1899,6 +1905,11 @@ static void update_layout(FileDialogImpl *This) DeferWindowPos(hdwp, hwnd, NULL, cancel_rc.left, cancel_rc.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE);
+ if (hdwp && (hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAVBAR))) + DeferWindowPos(hdwp, hwnd, NULL, navbar_rc.left, navbar_rc.top, + navbar_rc.right - navbar_rc.left, navbar_rc.bottom - navbar_rc.top, + SWP_NOZORDER | SWP_NOACTIVATE); + if(hdwp) EndDeferWindowPos(hdwp); else @@ -1965,35 +1976,10 @@ static HRESULT init_explorerbrowser(FileDialogImpl *This)
static void init_toolbar(FileDialogImpl *This, HWND hwnd) { - HWND htoolbar; - TBADDBITMAP tbab; - TBBUTTON button[2]; - - htoolbar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, TBSTYLE_FLAT | WS_CHILD | WS_VISIBLE, - 0, 0, 0, 0, - hwnd, (HMENU)IDC_NAV_TOOLBAR, NULL, NULL); - - tbab.hInst = HINST_COMMCTRL; - tbab.nID = IDB_HIST_LARGE_COLOR; - SendMessageW(htoolbar, TB_ADDBITMAP, 0, (LPARAM)&tbab); - - button[0].iBitmap = HIST_BACK; - button[0].idCommand = IDC_NAVBACK; - button[0].fsState = TBSTATE_ENABLED; - button[0].fsStyle = BTNS_BUTTON; - button[0].dwData = 0; - button[0].iString = 0; - - button[1].iBitmap = HIST_FORWARD; - button[1].idCommand = IDC_NAVFORWARD; - button[1].fsState = TBSTATE_ENABLED; - button[1].fsStyle = BTNS_BUTTON; - button[1].dwData = 0; - button[1].iString = 0; - - SendMessageW(htoolbar, TB_ADDBUTTONSW, 2, (LPARAM)button); - SendMessageW(htoolbar, TB_SETBUTTONSIZE, 0, MAKELPARAM(24,24)); - SendMessageW(htoolbar, TB_AUTOSIZE, 0, 0); + CreateWindowExW(0, WC_NAVBARW, NULL, WS_CHILD | WS_VISIBLE, + 0, 0, + 0, MulDiv(NAVBAR_HEIGHT, This->dpi_y, USER_DEFAULT_SCREEN_DPI), + hwnd, (HMENU)IDC_NAVBAR, NULL, NULL); }
static void update_control_text(FileDialogImpl *This) @@ -2134,8 +2120,8 @@ static LRESULT on_wm_initdialog(HWND hwnd, LPARAM lParam) }
ctrl_container_reparent(This, This->dlg_hwnd); - init_explorerbrowser(This); init_toolbar(This, hwnd); + init_explorerbrowser(This); update_control_text(This); update_layout(This);
@@ -2288,8 +2274,6 @@ static LRESULT on_wm_command(FileDialogImpl *This, WPARAM wparam, LPARAM lparam) case IDOK: return on_idok(This); case IDCANCEL: return on_idcancel(This); case psh1: return on_command_opendropdown(This, wparam, lparam); - case IDC_NAVBACK: return on_browse_back(This); - case IDC_NAVFORWARD: return on_browse_forward(This); case IDC_FILETYPE: return on_command_filetype(This, wparam, lparam); default: TRACE("Unknown command.\n"); } @@ -2307,6 +2291,8 @@ static INT_PTR CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, case WM_SIZE: return on_wm_size(This); case WM_GETMINMAXINFO: return on_wm_getminmaxinfo(This, lparam); case WM_DESTROY: return on_wm_destroy(This); + case NBN_NAVBACK: return on_browse_back(This); + case NBN_NAVFORWARD: return on_browse_forward(This); }
return FALSE; diff --git a/dlls/comdlg32/navbar.bmp b/dlls/comdlg32/navbar.bmp new file mode 100644 index 0000000000000000000000000000000000000000..b021235312d85a933d391af5eedb4ea8e06686da GIT binary patch literal 9354 zcmeHLd013O5+5^z%Aw$mF&aFguIwg}Q-<MGqiZ}iBA}=QLGGJ5Kx7p`G#Wq9RZ&m` zgy9%&262?r15l7C3KA7evR*_sX59!X8fvQ^Om>t(a5wwyADjLtUcc_DUw8kiy1L(3 zFWWtX#gyInKMchQMH^)ZONYhAC#$!gs{bm$V)gt{6Id@=$qzr<G22NtWt>h_`w&Zg z{W0NgZZj?2+=eE_#i{btn%Ohf#pwOZp2gxQzWi4IH5hmYK~zwNp$tb+?S0ZawNw9p zQsnqU5ri;FsQznUBMdV?!uqxGhae_}vYZ>@MN#DZkPhh%wvfIWiWbT^l!++sp}dcx zgEF=EN$=E7{bNxyQOJ(jUif@dp*fT8NR&}1WKUy_?-i<38}*S6>5|Q0%YO@>wl~F~ z1`6>r28HHKsfBo$)GJh{HtHiC(j^<R?Jq^Jci7A^#(vU&7lnA9h4KfKxvDBE#!lQR zuG?*N_aFM)pg7K1zx?2OJxTbA*&!CQN1LFI4hq?kJ&mQz4>Y!-Z%Ka&3XL@b<pZ{z zt}7>q+s+y}jNYk_bjTmH2D0sM{@BIz^~E2?nogMSIQ-rdbv{5LY#MX0_(M>a_K!LW z&4XfNB8Sg?iUMyG0rx5D(i+HyYzc#~2vc5&D~%L4qq`eS-sfVS=19C7e4;yH^#Mn{ z(llR-ABwiDd~$Y=BUJ5kf||XKaAubs$TlzT%J4F6KjN%cYCK(2A8lw)&{(hgfMSx` z$UhVxw0~%vIVc~Y=yBG6wC}x$WzU8Rc-^cqsz$wiq(i!7Blj&~y<YmqS5cX(A~1#b zBUe0UJLw%nLprl)Od5MI_=^0~KBx6*6Yptv&cXJZxe$2^r8OV6-|msfmOQ47bWoS( zPBvt#jDPaS$ZY;6k2CzO@0!jSJ?{^bHO%8S7?<R2TJ~dI0v{TVi=h69H`E?<hKql3 zgbVxD!G)N$C=O5^Z4af}mj0aLZgkh)c+xz?qJ1|4Ma~oDc#2EPC$uk#&zUHUk6FVx zW^6ZuqFH-ZKMSot4#S0JFIif{EwSIASBG@ThHMFguoTaoH{)|SJmkMS^s5w@6XWoV z?O|BT(%@Ji#$fSLl_?abltYL^P2x8FZWT-{^MX;8?lAhi7mTU#LGgys=R6sI^ztu{ zd$LuQ7s$VA#o>0}->yvSPWR@u=7fCuupuMz=WFR(pg!Ik>JGU<ZLBL?it|K2_`szj zUQm0$1^wU%HT%{=^&Weuj9LL=ccYs#v^6MS&{-tkW0W&!&J@427pJ294rMNDisnL& zuW{qNc=zYR%gL~<ISctWf63Auwh;Ttmveh{$%bqRgRm6yQ2+RZ!QogD{}EqjgRn^q z+(hp#l>n1lES=Gd5oa*?sMe<pVd5Y8pLveY@IB_DFO0h!2;=Gl7(Z<PCZEYoXsH-0 zeSqW*#oO%L+OC|2*76kiYfTQ^I(ZPTB=H&k8&ZPdN@^&S?poI&3AAp{@Zmnr_2d4W z>&t~)f3p{bg89!Xcl-&8g6BU;bk=i{WA;9k#wVLQ=~Fh}<og9_f){~T5<#cL2f}Y< zgKDVN3tC$rp<LSI+kWGpBQSZ=#}2WEip*a|wB&+Lbsz+_q`~r{@E0mV-a|HT$}*he zK>O>p<@CJD`8TWygHdNaVax>|7<<Vd#@6}4m}+k(7oko!YRltLDUQ6cc-!g+cWVpa z+cT-qaw-mP$qvHpvSV<w_yC-Yw0o51W!kXE{=<!Pr)p6y7_U1{UAt_P_5Q|`O?R$k z?QTFnbjrf;Jn)`(UXH2od?i1#ZD!jH7n%LTUK-i$dNCHppV$bRr(GcYb}mHSkwVnH zBIZf?lyYkXvoA0&>Ay>PRG!xq;F&fJ;$%x*3L{&;5<~da4A^!(3nD2;BNlP=UV{+4 zzDb33XJfimL(Dtawz>`oG|>5?I2Kc<u~ISrFS@t`##VTs4?LJ0M0se`84sW{;BEQ8 zS0_d84?}B7JlwgE3k{N;PqY0kuBUn#8upn0HURpz@raM4y}4@ZU#oMSvwh8eO!qKu zQ>Ke;lqb|wJPiFtgj#nx$-V^p%H5!uwGKuUIx_sz8fYI=91&NPS2a&L!<ME@K!3ep z+3M06-e2L!`5zj;9EK%+25RYcppmx@Miy@Xtuoi19F4f+YkXmCV=Q>o9)eL}3pzR8 zM!^V5`${pdy)q7B-zR*zs08!6j(>Xw7FKPCIh6v?EZGQJrLHJFu@rG52S`V*&pZXD z4_*OPc%k^ef2oM!zvXls+^dyBS+x6Ov8R!(f?A*Ijoc{5hfQUF{Gp|GGWtrG=3)F( zAElm#AsmtEj`873I(#Y*z>?yvpqgld9NB|L?phdm!iiY}aZd3wssg#kJu$NAbNKVM zj2`~OE$IyFx6m3X{)zvgM^}Jq$|_KotO1Su^^6Y)?-!0_&ak*D3IgjAKs$0-2U}oz zm}P6L?0jU#;{=*4p817cXHs(sq_v)bl-3GJYApxLiY>sc*bLey-I=+L!1<1lIx_oi zTcd>8gPZD-VRGd1PPTx%qQA)#|7}-G;k)x0@ZGs|=K0|ADa<`<+SA?V(p^Dm`M$1b z+N0J>bhjEJb~pZj&R}JV9HHr{sk>Ko1Xaa@ac&@tNU#B|L|Ygwc7Rb5jM3bleL#Mo zc%>)p3yqw05OK9P{s+PT_WBGM7Qce=!LWosF+Nbsum|-V2N+qf9>z*Hz<8+(j4#*# zqq7~sq*w&}nxio7OWO{PfGblrcX^wN$kfB|FV3BDO~tUPY$sUD!oc)Y2<XcMpj+$@ zTDj{%J=+1)v(|uG=4#N+-T;Ey1kl~PwgY2BB*#(ql=xdcsyVv(QT35ck1ix^ZohCW zteu|aF`hrBxf%Uk>@t_~{u_(L#bmVD)hJq-5?jPQHuZ~DPlQ!R!J<F}6OtWZQp$SJ z5pRH*+3uj5?*qS+`oITLZ<r?agz1HzFdOT7(jeI1Bn8!A^B1f^@PDvT3Lj_rz>Ktw zFeUYKn2a(t(*<VbdV+3&56mj?hUt0kFeS?cw9_2Hyif!*r)j%vI@m&9g0i_Qwy}k# zhZY_S?2Kv1hXp4>!LT3@KFs%pS$Uo?88Owxc8t$QrLF;=>O;Vd^LUE5VnqyPb+MD- z$I0GCW5mve)07EuOLk&+!>~*r)B8F61uZ#2B=cKx_&t&<SkNNz=9VhMr#6fM{)F%) zo&2+Lu(s?=m@Y<M(%s<qnV#@*whtKQ2f(~S0hktvfF}(GGpPv73WRXD>BOM<kH3Br z%<_ed@bU$~&Eqrs%^=60@e7SbW8xxV)#)7&eEuj*j9A)<vA?~)dB4&g&Uy#;a*2Ib z`17rGsW2CPO?WzK&M+}$9cbd7Rz3O|IF(1kBC+rV=G;rHa9#Sw>bFQR@1=PdJ-l)t zP`5hTW6~Xot!lWXrP8yAbS1vL@~c_9yS_V@hG+E=XgwVdt)&TY7tieM0PB{12f_}F zpCy``=l{dE{9w1=`NPco^|Sq89_A$C%d#LC=1Vuh0%<5LEZWS-(dJWw=0EZ4l3(ly zgRlsbuxSh$i}r{2xmXCOh-J=SjL$W1nsc8%FG~%MkbBd)X!lE61H}>T<*`YtLF4F3 zusIb4b_EgL91-t6i*HGHwl|hUFGKBAR|BYy*%(tF7Zf1-^Jh`XE(Y{%qO>GBSq_)@ znAbPu#k_3J`vSic1aLVq2)-##f^2`QH}X$_DnY!DR7K|Pu0?xZQVdxahA<o*xh{jG z8>NtZqYP4RmNSyt`&0|I7*zftJGl+jk0TagCN!UB#;__31{%{tw)Z7s{-DC=D!tn& zUASVS$n;%WgI`%}w_V9jCZB2@vxP-PVc=R44Qe44kB0^E=-Xnza@^y?@fkj5-MN7a z9-oVLxlk0k{PJ-ZgVp^RrfoEykmbuOtvVF+Q|sv?a3xv9@K4_hSpin9{n@?J9%Mcl z&xBhxnPFX&ODSe3cGW|zo-=nfg}WZ}F5q@z{#Uvu<+!N#lQP?(BD1b!^ufwg+abCk z57uT!cBzPXEv#T3{Ra1M7Zu?=J>1J30?emY7(=wb{LUN#t+0h1oFJ3=g9WC0e8HyW zZL+BK&vX2(yJTBdLb``Zy~Os_ymj17e~QG{Y=1$>(g)Y`qkq03-3wP!H$y{82wcH$ zWXeSne~ZEKPm}A}$EBYf3w(OG@dSu(R)BiQz;Qm<buoIFFNX?EyT!LEfW}yy%zuWt zzKRvBPj|NexUhvLOSQLs+9||ygzk0|B0qhi!smItEpS$Vb>;1|5;%Jx;8|AS{1+KM zyq9>Ff1KuSRF>s$k&_iPzp4C7mxo`=;=8}A%7j}d4nSk(HfWSYGWnmrnQ!A6An~)j z{Z?ZmKbTAR(!DO}`sx1~|H;jzz>RgMJNR*yo4&Gd=(n1-qB723G;P<aCv=C!^X-6Q z-(c(MZu5*wb~*j+`)if(;Ia&EmL$S;X)H7s#X@U^81B~>!@as9xP!k9(Ko`kXVc(L zbvCq=CP887(uYM`SN-r!Wy;(0-#bAS0ncyNPP-2L{j+<Zd*)!*#ON{c&x6^!S3l(l zO$2Xc`xeS{zovW9o4(-&g3I=|+*x&q|G2H86xyzoFn+lI)k)-C3iR6${U$+jueK2G zUd)G@gwUTP{^kuS&IYqmJxv^BQEMJ$`I)_4{3}d~9b~!$^LN`od0xR};40l05tA)4 zT|Th#|6T+0yH8=*XKmN=_jldBbOIjWZ=&=a{NO7Y+__i)*NYE#SM2lpTb937Q>vHo zD#aORcym25XAM68D;ob+|AhgGo4#hIm$?A%@)z=gm$c+>Uh!>?U{MS1j~e{;ib!=g Zp7V?HYZbi5{_(Y@-(G)X;Qx|=e*#-Ru^IpX
literal 0 HcmV?d00001
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c new file mode 100644 index 00000000000..a9cf4ecc2ca --- /dev/null +++ b/dlls/comdlg32/navbar.c @@ -0,0 +1,172 @@ +/* + * Navigation bar control + * + * Copyright 2022 Vladislav Timonin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include "navbar.h" +#include "commdlg.h" +#include "cdlg.h" + +/* private control ids */ +#define IDC_NAVBACK 201 +#define IDC_NAVFORWARD 202 + +typedef struct { + HWND parent_hwnd; + HWND container_hwnd; + + HIMAGELIST icons; + HWND tooltip; + + HWND back_btn_hwnd; + HWND fwd_btn_hwnd; +} NAVBAR_INFO; + +static void set_icon(HIMAGELIST icons, INT icon_id, HWND window) +{ + HICON icon; + + icon = ImageList_GetIcon(icons, icon_id, ILD_NORMAL); + SendMessageW(window, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); +} + +static void set_title_and_add_tooltip(NAVBAR_INFO *info, HWND window, UINT string_id) +{ + WCHAR buffer[128] = {0}; + TOOLINFOW toolinfo = {0}; + + LoadStringW(COMDLG32_hInstance, string_id, buffer, ARRAY_SIZE(buffer)); + + SetWindowTextW(window, buffer); + + toolinfo.cbSize = sizeof(toolinfo); + toolinfo.uFlags = TTF_IDISHWND | TTF_SUBCLASS; + toolinfo.hwnd = info->container_hwnd; + toolinfo.lpszText = buffer; + toolinfo.uId = (UINT_PTR)window; + + SendMessageW(info->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo); +} + +static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + CREATESTRUCTW *cs = (CREATESTRUCTW*)lparam; + NAVBAR_INFO *info; + INT x; + HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); + + info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NAVBAR_INFO)); + info->parent_hwnd = GetParent(hwnd); + info->container_hwnd = hwnd; + + info->icons = ImageList_LoadImageW(COMDLG32_hInstance, MAKEINTRESOURCEW(IDB_NAVBAR), 24, 0, + CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION); + + info->tooltip = CreateWindowW(TOOLTIPS_CLASSW, NULL, WS_POPUP | TTS_ALWAYSTIP, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + hwnd, 0, COMDLG32_hInstance, NULL); + + x = 0; + info->back_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, + WS_CHILD | WS_VISIBLE | BS_ICON | BS_BITMAP, + x, 0, cs->cy, cs->cy, + hwnd, (HMENU)IDC_NAVBACK, COMDLG32_hInstance, NULL); + SendMessageW(info->back_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + set_icon(info->icons, ILI_BACK, info->back_btn_hwnd); + set_title_and_add_tooltip(info, info->back_btn_hwnd, IDS_BACK); + + x += cs->cy + 1; + info->fwd_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, + WS_CHILD | WS_VISIBLE | BS_ICON | BS_BITMAP, + x, 0, cs->cy, cs->cy, + hwnd, (HMENU)IDC_NAVFORWARD, COMDLG32_hInstance, NULL); + SendMessageW(info->fwd_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + set_icon(info->icons, ILI_FORWARD, info->fwd_btn_hwnd); + set_title_and_add_tooltip(info, info->fwd_btn_hwnd, IDS_FORWARD); + + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info); + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + SetWindowLongPtrW(hwnd, 0, 0); + ImageList_Destroy(info->icons); + + HeapFree(GetProcessHeap(), 0, info); + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + switch (LOWORD(wparam)) + { + case IDC_NAVBACK: + SendMessageW(info->parent_hwnd, NBN_NAVBACK, 0, 0); + break; + case IDC_NAVFORWARD: + SendMessageW(info->parent_hwnd, NBN_NAVFORWARD, 0, 0); + break; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + NAVBAR_INFO *info = (NAVBAR_INFO *)GetWindowLongPtrW(hwnd, 0); + + if (!info && (msg != WM_NCCREATE)) + goto exit; + + switch (msg) + { + case WM_NCCREATE: return NAVBAR_Create(hwnd, msg, wparam, lparam); + case WM_DESTROY: return NAVBAR_Destroy(hwnd, info, msg, wparam, lparam); + case WM_COMMAND: return NAVBAR_Command(hwnd, info, msg, wparam, lparam); + } + +exit: + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +ATOM NAVBAR_Register(void) +{ + WNDCLASSW wndClass; + + ZeroMemory(&wndClass, sizeof(WNDCLASSW)); + wndClass.style = CS_HREDRAW; + wndClass.lpfnWndProc = NAVBAR_WindowProc; + wndClass.cbWndExtra = sizeof(NAVBAR_INFO *); + wndClass.hInstance = COMDLG32_hInstance; + wndClass.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)(COLOR_BTNSHADOW); + wndClass.lpszClassName = WC_NAVBARW; + + return RegisterClassW(&wndClass); +} + +BOOL NAVBAR_Unregister(void) +{ + return UnregisterClassW(WC_NAVBARW, COMDLG32_hInstance); +} diff --git a/dlls/comdlg32/navbar.h b/dlls/comdlg32/navbar.h new file mode 100644 index 00000000000..617967b829f --- /dev/null +++ b/dlls/comdlg32/navbar.h @@ -0,0 +1,47 @@ +/* + * Navigation bar control + * + * Copyright 2022 Vladislav Timonin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef _WINE_NAVBAR +#define _WINE_NAVBAR + +#include "windows.h" + +#define WC_NAVBARW L"NavBar" + +/* navbar notifications */ +#define NBN_NAVBACK WM_USER + 102 +#define NBN_NAVFORWARD WM_USER + 103 + +/* strings */ +#define IDS_BACK 2000 +#define IDS_FORWARD 2001 + +/* bitmaps */ +#define IDB_NAVBAR 3000 +/* icons in IDB_NAVBAR */ +#define ILI_BACK 0 +#define ILI_FORWARD 1 +#define ILI_UP 2 +#define ILI_REFRESH 3 + +ATOM NAVBAR_Register(void); +BOOL NAVBAR_Unregister(void); + +#endif /* _WINE_NAVBAR */ diff --git a/dlls/comdlg32/navbar.svg b/dlls/comdlg32/navbar.svg new file mode 100644 index 00000000000..d4a9c943738 --- /dev/null +++ b/dlls/comdlg32/navbar.svg @@ -0,0 +1,676 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + id="bitmap:120-32" + height="24" + width="96" + version="1.1" + sodipodi:docname="navbar.svg" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg%22%3E + <defs + id="defs2916"> + <radialGradient + id="radialGradient1361" + gradientUnits="userSpaceOnUse" + cy="36.421" + cx="24.837" + gradientTransform="matrix(1,0,0,0.536723,0,16.87306)" + r="15.645"> + <stop + id="stop8664" + stop-color="#000" + offset="0" /> + <stop + id="stop8666" + stop-color="#000" + stop-opacity="0" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient3241" + gradientUnits="userSpaceOnUse" + cx="15.987" + cy="1.535" + r="17.171" + gradientTransform="matrix(-0.99935,0,0,0.783428,33.4962,1.6234577)"> + <stop + id="stop8652-1" + stop-color="#FFF" + offset="0" /> + <stop + id="stop8654-1" + stop-color="#FFF" + stop-opacity="0" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient3244" + gradientUnits="userSpaceOnUse" + cx="17.481" + cy="16.118" + r="16.956" + gradientTransform="matrix(-0.411558,0.00716573,-0.00521275,-0.299389,21.69622,19.724318)"> + <stop + id="stop2593-6" + stop-color="#73d216" + offset="0" /> + <stop + id="stop2595-2" + stop-color="#4e9a06" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient3263" + gradientUnits="userSpaceOnUse" + cx="15.987" + cy="1.535" + r="17.171" + gradientTransform="matrix(0.99731932,0,0,0.783428,14.565151,1.62346)"> + <stop + id="stop8652" + stop-color="#FFF" + offset="0" /> + <stop + id="stop8654" + stop-color="#FFF" + stop-opacity="0" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient3266" + gradientUnits="userSpaceOnUse" + cx="27.547" + cy="15.843" + r="16.956" + gradientTransform="matrix(0.41071672,0.0074091,0.00521275,-0.288964,26.347891,19.75179)"> + <stop + id="stop2593" + stop-color="#73d216" + offset="0" /> + <stop + id="stop2595" + stop-color="#4e9a06" + offset="1" /> + </radialGradient> + <linearGradient + id="linearGradient3278" + y2="4" + gradientUnits="userSpaceOnUse" + y1="15" + gradientTransform="translate(96,-1)" + x2="4" + x1="4"> + <stop + id="stop10718" + stop-color="#d3d7cf" + offset="0" /> + <stop + id="stop10720" + stop-color="#babdb6" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3281" + y2="5" + gradientUnits="userSpaceOnUse" + y1="2" + gradientTransform="translate(96,0)" + x2="4" + x1="4"> + <stop + id="stop10746" + stop-color="#729fcf" + offset="0" /> + <stop + id="stop10748" + stop-color="#3465a4" + offset="1" /> + </linearGradient> + <radialGradient + id="radialGradient1361-2" + gradientUnits="userSpaceOnUse" + cy="36.421001" + cx="24.837" + gradientTransform="matrix(1,0,0,0.536723,0,16.87306)" + r="15.645"> + <stop + id="stop8664-9" + stop-color="#000" + offset="0" /> + <stop + id="stop8666-3" + stop-color="#000" + stop-opacity="0" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient5915" + gradientUnits="userSpaceOnUse" + cy="20.839001" + cx="13.175" + gradientTransform="matrix(0,-0.41162,-0.513112,0,215.50763,22.608323)" + r="16.955999"> + <stop + id="stop2593-1" + stop-color="#73d216" + offset="0" /> + <stop + id="stop2595-9" + stop-color="#4e9a06" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient5912" + gradientUnits="userSpaceOnUse" + cy="1.535" + cx="15.987" + gradientTransform="matrix(0,-0.99935,0.783428,0,193.62392,34.495966)" + r="17.171"> + <stop + id="stop8652-4" + stop-color="#FFF" + offset="0" /> + <stop + id="stop8654-7" + stop-color="#FFF" + stop-opacity="0" + offset="1" /> + </radialGradient> + <radialGradient + id="radialGradient1992" + gradientUnits="userSpaceOnUse" + cy="36.421001" + cx="24.837" + gradientTransform="matrix(1,0,0,0.536723,0,16.87306)" + r="15.645"> + <stop + id="stop8664-6" + stop-color="#000" + offset="0" /> + <stop + id="stop8666-1" + stop-color="#000" + stop-opacity="0" + offset="1" /> + </radialGradient> + <linearGradient + id="linearGradient3681" + y2="19.115" + gradientUnits="userSpaceOnUse" + x2="15.419" + gradientTransform="matrix(0.666667,0,0,0.646309,96.83003,1019.302)" + y1="10.612" + x1="13.479"> + <stop + id="stop2833" + stop-color="#3465a4" + offset="0" /> + <stop + id="stop2855" + stop-color="#5b86be" + offset="0.33333" /> + <stop + id="stop2835" + stop-color="#83a8d8" + stop-opacity="0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3683" + y2="26.194" + gradientUnits="userSpaceOnUse" + x2="37.064999" + gradientTransform="matrix(-0.666667,0,0,-0.646309,128.20505,1049.113)" + y1="29.73" + x1="37.127998"> + <stop + id="stop2849" + stop-color="#3465a4" + offset="0" /> + <stop + id="stop2851" + stop-color="#3465a4" + stop-opacity="0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3685" + y2="20.608999" + xlink:href="#linearGradient2380" + gradientUnits="userSpaceOnUse" + x2="15.985" + y1="36.061001" + x1="62.514" /> + <linearGradient + id="linearGradient2380"> + <stop + id="stop2382-9" + stop-color="#b9cfe7" + offset="0" /> + <stop + id="stop2384" + stop-color="#729fcf" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3687" + y2="50.939999" + xlink:href="#linearGradient2871" + gradientUnits="userSpaceOnUse" + x2="45.380001" + y1="45.264" + x1="46.834999" /> + <linearGradient + id="linearGradient2871"> + <stop + id="stop2873" + stop-color="#3465a4" + offset="0" /> + <stop + id="stop2875" + stop-color="#3465a4" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3691" + y2="24.841999" + gradientUnits="userSpaceOnUse" + x2="37.124001" + gradientTransform="matrix(0.666667,0,0,0.647455,96.519755,1019.8536)" + y1="30.749001" + x1="32.647999"> + <stop + id="stop2692" + stop-color="#c4d7eb" + offset="0" /> + <stop + id="stop2694" + stop-color="#c4d7eb" + stop-opacity="0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3693" + y2="24.841999" + gradientUnits="userSpaceOnUse" + x2="37.124001" + gradientTransform="matrix(0.666667,0,0,0.647455,96.519755,1019.8536)" + y1="31.455999" + x1="36.714001"> + <stop + id="stop2684" + stop-color="#3977c3" + offset="0" /> + <stop + id="stop2686" + stop-color="#89aedc" + stop-opacity="0" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3695" + y2="26.649" + xlink:href="#linearGradient2402" + gradientUnits="userSpaceOnUse" + x2="53.589001" + y1="23.667999" + x1="18.936001" /> + <linearGradient + id="linearGradient2402"> + <stop + id="stop2404" + stop-color="#729fcf" + offset="0" /> + <stop + id="stop2406" + stop-color="#528ac5" + offset="1" /> + </linearGradient> + <linearGradient + id="linearGradient3703" + y2="26.048" + xlink:href="#linearGradient2797" + gradientUnits="userSpaceOnUse" + x2="52.854" + y1="26.048" + x1="5.9649" /> + <linearGradient + id="linearGradient2797"> + <stop + id="stop2799" + stop-color="#FFF" + offset="0" /> + <stop + id="stop2801" + stop-color="#FFF" + stop-opacity="0" + offset="1" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2380" + id="linearGradient3228" + gradientUnits="userSpaceOnUse" + x1="62.514" + y1="36.061001" + x2="15.985" + y2="20.608999" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2871" + id="linearGradient3230" + gradientUnits="userSpaceOnUse" + x1="46.834999" + y1="45.264" + x2="45.380001" + y2="50.939999" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2402" + id="linearGradient3232" + gradientUnits="userSpaceOnUse" + x1="18.936001" + y1="23.667999" + x2="53.589001" + y2="26.649" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2871" + id="linearGradient3234" + gradientUnits="userSpaceOnUse" + x1="46.834999" + y1="45.264" + x2="45.380001" + y2="50.939999" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient2797" + id="linearGradient3236" + gradientUnits="userSpaceOnUse" + x1="5.9649" + y1="26.048" + x2="52.854" + y2="26.048" /> + </defs> + <g + id="g3268" + sodipodi:insensitive="true"> + <path + id="path8660" + opacity="0.321569" + style="color:#000000" + d="m 40.482,36.421 a 15.645,8.3969 0 1 1 -31.289,0 15.645,8.3969 0 1 1 31.289,0 z" + fill-rule="evenodd" + transform="matrix(0.703097,0,0,0.655005,18.537026,-6.3560112)" + fill="url(#radialGradient1361)" /> + <path + id="path8643" + stroke-linejoin="round" + style="color:#000000;stroke-dasharray:none" + d="m 28.501,16.5 v -9 h 5.9878 v -4 l 10.011,8.5554 -10.011,8.445 v -4 H 28.501 Z" + fill-rule="evenodd" + stroke-dashoffset="0" + stroke="#3a7304" + stroke-linecap="round" + stroke-miterlimit="10" + stroke-width="1" + fill="url(#radialGradient3266)" /> + <path + id="path8645" + opacity="0.508021" + style="color:#000000" + d="M 34.988,4.5 V 8 H 29 l 0.03167,4.5 c 5.9878,-3 8.9817,2 14.471,-0.5 l -8.515,-7.5 z" + fill-rule="evenodd" + fill="url(#radialGradient3263)" /> + <path + id="path8658" + opacity="0.481283" + stroke-linejoin="miter" + style="color:#000000;stroke-dasharray:none" + d="m 29.499,15.5 v -7 h 5.9878 v -3 l 7.4848,6.5 -7.4848,6.5 v -3 z" + stroke-dashoffset="0" + stroke="#ffffff" + stroke-linecap="butt" + stroke-miterlimit="10" + stroke-width="1" + fill="none" /> + </g> + <g + id="g3246" + sodipodi:insensitive="true"> + <path + id="path8660-4" + opacity="0.321569" + style="color:#000000" + d="m 40.482,36.421 a 15.645,8.3969 0 1 1 -31.289,0 15.645,8.3969 0 1 1 31.289,0 z" + fill-rule="evenodd" + transform="matrix(0.703097,0,0,0.655005,-5.4629086,-6.3560133)" + fill="url(#radialGradient1361)" /> + <path + id="path8643-2" + stroke-linejoin="round" + style="color:#000000;stroke-dasharray:none" + d="m 19.532,16.5 v -9 h -6 v -4 L 3.5,12.0554 13.532,20.5 v -4 z" + fill-rule="evenodd" + stroke-dashoffset="0" + stroke="#3a7304" + stroke-linecap="round" + stroke-miterlimit="10" + stroke-width="1" + fill="url(#radialGradient3244)" /> + <path + id="path8645-4" + opacity="0.508021" + style="color:#000000" + d="M 13.032,4.5 V 8 h 6 l -0.03173,4.5 c -6,-3 -9,2 -14.5,-0.5 l 8.5317,-7.5 z" + fill-rule="evenodd" + fill="url(#radialGradient3241)" /> + <path + id="path8658-0" + opacity="0.481283" + stroke-linejoin="miter" + style="color:#000000;stroke-dasharray:none" + d="m 18.532,15.5 v -7 h -6 v -3 l -7.5,6.5 7.5,6.5 v -3 z" + stroke-dashoffset="0" + stroke="#ffffff" + stroke-linecap="butt" + stroke-miterlimit="10" + stroke-width="1" + fill="none" /> + </g> + <g + id="g5917" + transform="translate(-143.99954,-2.7328773e-5)" + sodipodi:insensitive="true"> + <path + id="path8660-8" + opacity="0.204545" + style="color:#000000;fill:url(#radialGradient1361-2)" + d="m 40.482,36.421 a 15.645,8.3969 0 1 1 -31.289,0 15.645,8.3969 0 1 1 31.289,0 z" + fill-rule="evenodd" + transform="matrix(0.703097,0,0,0.357277,186.53732,7.0193119)" + fill="url(#radialGradient1361)" /> + <path + id="path8643-4" + stroke-linejoin="round" + style="color:#000000;fill:url(#radialGradient5915);stroke-dasharray:none" + d="m 208.5,20.532 h -9 v -6 h -4 l 8.56,-10.032 8.4446,10.032 h -4 v 6 z" + fill-rule="evenodd" + stroke-dashoffset="0" + stroke="#3a7304" + stroke-linecap="round" + stroke-miterlimit="10" + stroke-width="1" + fill="url(#radialGradient5915)" /> + <path + id="path8645-5" + opacity="0.508021" + style="color:#000000;fill:url(#radialGradient5912)" + d="m 196.5,14.032 h 3.5 v 6 h 1 c 0.5,-10 9,-7.5 3,-14.5 z" + fill-rule="evenodd" + fill="url(#radialGradient5912)" /> + <path + id="path8658-03" + opacity="0.481283" + stroke-linejoin="miter" + style="color:#000000;stroke-dasharray:none" + d="m 207.5,19.532 h -7 v -6 h -3 l 6.5,-7.5 6.5,7.5 h -3 z" + stroke-dashoffset="0" + stroke="#ffffff" + stroke-linecap="butt" + stroke-miterlimit="10" + stroke-width="1" + fill="none" /> + </g> + <g + id="g3876" + style="display:inline" + transform="matrix(0.75039848,0,0,0.75039848,0.02870684,-765.02849)"> + <path + id="path8660-0" + opacity="0.383333" + style="color:#000000;fill:url(#radialGradient1992)" + d="m 40.482,36.421 a 15.645,8.3969 0 1 1 -31.289,0 15.645,8.3969 0 1 1 31.289,0 z" + fill-rule="evenodd" + transform="matrix(-0.993158,0,0,-0.667502,137.20489,1070.0684)" + fill="url(#radialGradient1992)" /> + <g + id="g3667" + transform="translate(-0.41059,1.42455)"> + <path + id="path2865" + stroke-linejoin="miter" + style="color:#000000;fill:url(#linearGradient3681);stroke:url(#linearGradient3683);stroke-dasharray:none" + d="m 109.96,1026.2 c 0,0 -5.9583,-0.4039 -4.125,6.3823 h -5.125 c 0,0 0.33334,-7.6749 9.25,-6.3823 z" + fill-rule="nonzero" + stroke-dashoffset="0" + display="block" + stroke="url(#linearGradient3683)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="1" + fill="url(#linearGradient3681)" /> + <g + id="g1878" + transform="matrix(-0.386034,-0.316192,-0.326152,0.374245,134.46368,1028.1234)" + stroke="#3465a4" + stroke-width="1.52344" + fill="url(#linearGradient3685)" + style="fill:url(#linearGradient3685)"> + <path + id="path1880" + stroke-linejoin="miter" + style="color:#000000;fill:url(#linearGradient3228);stroke:url(#linearGradient3230);stroke-dasharray:none" + d="M 44.307,50.23 C 62.821,35.819 49.665,13.412 22.462,12.498 l -0.348,-9.3465 -14.49,17.346 15.09,12.722 c 0,0 -0.25192,-9.8812 -0.25192,-9.8812 18.83,0.99898 32.982,14.072 21.844,26.892 z" + fill-rule="nonzero" + stroke-dashoffset="0" + display="block" + stroke="url(#linearGradient3687)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="2.00968" + fill="url(#linearGradient3685)" /> + </g> + <path + id="path2839" + stroke-linejoin="miter" + style="color:#000000;fill:url(#linearGradient3691);stroke:url(#linearGradient3693);stroke-dasharray:none" + d="m 115.44,1041.5 c 0,0 5.9583,0.4046 4.125,-6.3936 h 5.1839 c 0,0.9728 -0.39226,7.6885 -9.3089,6.3936 z" + fill-rule="nonzero" + stroke-dashoffset="0" + display="block" + stroke="url(#linearGradient3693)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="1" + fill="url(#linearGradient3691)" /> + <g + id="g2779" + stroke-linejoin="miter" + style="color:#000000;fill:url(#linearGradient3695);stroke:url(#linearGradient3687);stroke-dasharray:none" + fill-rule="nonzero" + display="block" + stroke-dashoffset="0" + transform="matrix(0.386034,0.316753,0.326152,-0.374909,91.23907,1039.6243)" + stroke="url(#linearGradient3687)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="2.0079" + fill="url(#linearGradient3695)"> + <path + id="path2781" + stroke-linejoin="miter" + style="color:#000000;fill:url(#linearGradient3232);stroke:url(#linearGradient3234);stroke-dasharray:none" + d="M 44.307,50.23 C 62.821,35.819 49.665,13.412 22.462,12.498 L 22.399,3.069 7.794,20.424 22.462,33.006 v -9.6684 c 18.83,0.99898 32.982,14.072 21.844,26.892 z" + fill-rule="nonzero" + stroke-dashoffset="0" + display="block" + stroke="url(#linearGradient3687)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="2.0079" + fill="url(#linearGradient3695)" /> + </g> + <path + id="path2791" + opacity="0.272222" + style="color:#000000" + d="m 101.42,1044.2 -0.005,-9.2715 8.1406,-0.04 -2.8009,3.2437 2.7032,1.7397 c -2,1.4568 -2.9393,1.3182 -3.606,2.9773 l -1.9248,-1.4913 -2.5069,2.8418 z" + fill-rule="nonzero" + fill="#ffffff" /> + <g + id="g2793" + opacity="0.5" + transform="matrix(0.328713,0.262901,0.277723,-0.311169,94.451191,1039.6682)" + stroke="#ffffff" + stroke-width="1.59006" + fill="none"> + <path + id="path2795" + stroke-linejoin="miter" + style="color:#000000;stroke:url(#linearGradient3236);stroke-dasharray:none" + d="M 51.77,46.731 C 60.89,31.511 46.465,11.426 18.287,12.242 l -0.061,-7.0152 -11.692,14.07 11.502,9.847 c 0,0 0.05562,-9.0069 0.05562,-9.0069 23.572,0.79434 37.206,12.015 33.678,26.594 z" + stroke-dashoffset="0" + display="block" + stroke="url(#linearGradient3703)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="2.38842" + fill="none" /> + </g> + <g + id="g2805" + opacity="0.5" + transform="matrix(-0.339024,-0.277687,-0.286434,0.328671,131.88578,1028.1196)" + stroke="#ffffff" + stroke-width="1.52344" + fill="none"> + <path + id="path2807" + stroke-linejoin="miter" + style="color:#000000;stroke:url(#linearGradient3703);stroke-dasharray:none" + d="M 51.39,46.506 C 60.51,31.286 46.287,13.118 18.865,12.756 l -0.203,-6.8084 -11.22,13.22 11.585,9.795 c 0,0 -0.31396,-8.424 -0.31396,-8.424 22.406,0.47504 35.698,11.698 32.677,25.967 z" + stroke-dashoffset="0" + display="block" + stroke="url(#linearGradient3703)" + stroke-linecap="butt" + stroke-miterlimit="4" + stroke-width="2.28835" + fill="none" /> + </g> + <path + id="path2811" + opacity="0.272222" + style="color:#000000" + d="m 101.06,1030.1 c 1.8331,-6.967 11.056,-4.1901 13.552,-3.1271 2.7835,0.1366 3.6269,-1.4573 5.8438,-1.564 -9.3665,-6.3273 -18.708,-4.0447 -19.396,4.6911 z" + fill-rule="nonzero" + fill="#ffffff" /> + </g> + </g> +</svg>
From: Vladislav Timonin timoninvlad@yandex.ru
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=29912
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51855 --- dlls/comdlg32/itemdlg.c | 8 ++++++++ dlls/comdlg32/navbar.c | 15 +++++++++++++++ dlls/comdlg32/navbar.h | 1 + 3 files changed, 24 insertions(+)
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 1d7d3b5dab5..7778c0cb0db 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -2218,6 +2218,13 @@ static LRESULT on_browse_forward(FileDialogImpl *This) return FALSE; }
+static LRESULT on_browse_up(FileDialogImpl *This) +{ + TRACE("%p\n", This); + IExplorerBrowser_BrowseToIDList(This->peb, NULL, SBSP_PARENT); + return FALSE; +} + static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam) { if(HIWORD(wparam) == CBN_SELCHANGE) @@ -2293,6 +2300,7 @@ static INT_PTR CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, case WM_DESTROY: return on_wm_destroy(This); case NBN_NAVBACK: return on_browse_back(This); case NBN_NAVFORWARD: return on_browse_forward(This); + case NBN_NAVUP: return on_browse_up(This); }
return FALSE; diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index a9cf4ecc2ca..186b05e6acc 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -23,10 +23,12 @@ #include "navbar.h" #include "commdlg.h" #include "cdlg.h" +#include "filedlgbrowser.h"
/* private control ids */ #define IDC_NAVBACK 201 #define IDC_NAVFORWARD 202 +#define IDC_NAVUP 203
typedef struct { HWND parent_hwnd; @@ -37,6 +39,7 @@ typedef struct {
HWND back_btn_hwnd; HWND fwd_btn_hwnd; + HWND up_btn_hwnd; } NAVBAR_INFO;
static void set_icon(HIMAGELIST icons, INT icon_id, HWND window) @@ -102,6 +105,15 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) set_icon(info->icons, ILI_FORWARD, info->fwd_btn_hwnd); set_title_and_add_tooltip(info, info->fwd_btn_hwnd, IDS_FORWARD);
+ x += cs->cy + 1; + info->up_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, + WS_CHILD | WS_VISIBLE | BS_ICON | BS_BITMAP, + x, 0, cs->cy, cs->cy, + hwnd, (HMENU)IDC_NAVUP, COMDLG32_hInstance, NULL); + SendMessageW(info->up_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + set_icon(info->icons, ILI_UP, info->up_btn_hwnd); + set_title_and_add_tooltip(info, info->up_btn_hwnd, IDS_UPFOLDER); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -127,6 +139,9 @@ static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa case IDC_NAVFORWARD: SendMessageW(info->parent_hwnd, NBN_NAVFORWARD, 0, 0); break; + case IDC_NAVUP: + SendMessageW(info->parent_hwnd, NBN_NAVUP, 0, 0); + break; }
return DefWindowProcW(hwnd, msg, wparam, lparam); diff --git a/dlls/comdlg32/navbar.h b/dlls/comdlg32/navbar.h index 617967b829f..367dc2f1b37 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -28,6 +28,7 @@ /* navbar notifications */ #define NBN_NAVBACK WM_USER + 102 #define NBN_NAVFORWARD WM_USER + 103 +#define NBN_NAVUP WM_USER + 104
/* strings */ #define IDS_BACK 2000
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/itemdlg.c | 19 +++ dlls/comdlg32/navbar.c | 353 ++++++++++++++++++++++++++++++++++++++++ dlls/comdlg32/navbar.h | 4 + 3 files changed, 376 insertions(+)
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 7778c0cb0db..e38415821a1 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -2225,6 +2225,15 @@ static LRESULT on_browse_up(FileDialogImpl *This) return FALSE; }
+static LRESULT on_browse_pidl(FileDialogImpl *This, LPARAM lparam) +{ + ITEMIDLIST* pidl = (ITEMIDLIST*)lparam; + + TRACE("%p - pidl %p\n", This, pidl); + IExplorerBrowser_BrowseToIDList(This->peb, pidl, SBSP_ABSOLUTE); + return FALSE; +} + static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam) { if(HIWORD(wparam) == CBN_SELCHANGE) @@ -2301,6 +2310,7 @@ static INT_PTR CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, case NBN_NAVBACK: return on_browse_back(This); case NBN_NAVFORWARD: return on_browse_forward(This); case NBN_NAVUP: return on_browse_up(This); + case NBN_NAVPIDL: return on_browse_pidl(This, lparam); }
return FALSE; @@ -3430,6 +3440,7 @@ static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBro { FileDialogImpl *This = impl_from_IExplorerBrowserEvents(iface); HRESULT hr; + HWND hwnd; TRACE("%p (%p)\n", This, pidlFolder);
if(This->psi_folder) @@ -3441,6 +3452,14 @@ static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBro ERR("Failed to get the current folder.\n"); This->psi_folder = NULL; } + else + { + hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAVBAR); + if (!hwnd) + ERR("Failed to update navbar.\n"); + else + SendMessageW(hwnd, NBM_SETPIDL, 0, (LPARAM)pidlFolder); + }
events_OnFolderChange(This);
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 186b05e6acc..635bc9d2e51 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -24,15 +24,24 @@ #include "commdlg.h" #include "cdlg.h" #include "filedlgbrowser.h" +#include "shlwapi.h" + +#include "wine/debug.h" +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(commdlg);
/* private control ids */ #define IDC_NAVBACK 201 #define IDC_NAVFORWARD 202 #define IDC_NAVUP 203 +#define IDC_NAVCRUMB 204
typedef struct { HWND parent_hwnd; HWND container_hwnd; + INT container_h; + INT dpi_x;
HIMAGELIST icons; HWND tooltip; @@ -40,8 +49,26 @@ typedef struct { HWND back_btn_hwnd; HWND fwd_btn_hwnd; HWND up_btn_hwnd; + + HWND background_hwnd; + INT background_x; + INT background_w; + + struct list crumbs; + INT crumbs_total_n; + INT crumbs_visible_n; } NAVBAR_INFO;
+struct crumb { + struct list entry; + ITEMIDLIST *pidl; + WCHAR display_name[MAX_PATH]; + HWND hwnd; + INT full_w; + INT current_w; + INT x; +}; + static void set_icon(HIMAGELIST icons, INT icon_id, HWND window) { HICON icon; @@ -69,16 +96,199 @@ static void set_title_and_add_tooltip(NAVBAR_INFO *info, HWND window, UINT strin SendMessageW(info->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo); }
+static LRESULT CALLBACK NAVBAR_BACKGROUND_SubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id_subclass, DWORD_PTR ref_data) +{ + switch (msg) + { + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + RECT rc; + + GetClientRect(hwnd, &rc); + + /* draw a frame without left border */ + DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_TOP | BF_BOTTOM | BF_RIGHT | BF_FLAT); + + return FALSE; /* processed */ + } + } + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + +static void NAVBAR_CalcLayout(NAVBAR_INFO *info) +{ + RECT container_rc, up_button_rc; + struct crumb *crumb1, *crumb2; + INT padding, container_w, + buttons_w, max_crumbs_w, + w = 0, crumbs_visible_n = 0, prev_x; + + if (!GetWindowRect(info->container_hwnd, &container_rc)) + return; + padding = container_rc.left; + + if (!GetClientRect(info->container_hwnd, &container_rc)) + return; + container_w = container_rc.right - container_rc.left; + if (!container_w) + return; + + if (!GetWindowRect(info->up_btn_hwnd, &up_button_rc)) + return; + + buttons_w = up_button_rc.right + 1 - padding; + max_crumbs_w = container_w - buttons_w; + if (max_crumbs_w < 0) + return; + + LIST_FOR_EACH_ENTRY_REV(crumb1, &info->crumbs, struct crumb, entry) + { + INT next_w = w + crumb1->full_w; + crumb1->current_w = crumb1->full_w; + if (next_w > max_crumbs_w) + break; + + w = next_w; + crumbs_visible_n += 1; + } + + /* always show at least 2 crumbs */ + if (crumbs_visible_n < 2) + { + w = 0; + + crumb1 = (struct crumb *)list_tail(&info->crumbs); + if (&crumb1->entry == &info->crumbs) + { + ERR("no crumbs to layout\n"); + return; + } + + crumb2 = (struct crumb *)list_tail(&crumb1->entry); + if (&crumb2->entry == &info->crumbs) /* only 1 crumb in the list */ + { + if (crumbs_visible_n == 0) /* that doesn't fit on screen */ + { + crumb1->current_w = max_crumbs_w; + crumbs_visible_n = 1; + } + + prev_x = buttons_w + crumb1->current_w; + } + else + { + /* at least 2 crumbs in the list, 1 or 0 crumbs on screen */ + /* last crumb should take as much space as possible */ + INT min_crumb_w = MulDiv(56, info->dpi_x, USER_DEFAULT_SCREEN_DPI); + INT crumb2_size = crumb2->full_w; + + if (crumb2_size > min_crumb_w) + crumb2_size = min_crumb_w; + + /* calculate how much extra space is required to fit both crumbs */ + w = (crumb1->full_w + crumb2_size) - max_crumbs_w; + if (w > 0) + { + /* not enough space for last crumb, truncate it */ + crumb1->current_w = max(0, crumb1->full_w - w); + crumb2->current_w = crumb2_size; + } + else + /* enough space for last crumb, let the previous crumb take leftover space */ + crumb2->current_w = max_crumbs_w - crumb1->full_w; + + crumbs_visible_n = 2; + prev_x = buttons_w + crumb1->current_w + crumb2->current_w; + } + } + else + prev_x = buttons_w + w; + + info->background_x = prev_x; + info->background_w = container_rc.right - prev_x; + + info->crumbs_visible_n = crumbs_visible_n; + + LIST_FOR_EACH_ENTRY_REV(crumb1, &info->crumbs, struct crumb, entry) + { + if (crumbs_visible_n > 0) + { + LONG_PTR style = WS_CHILD | WS_VISIBLE; + + /* if label doesn't fully fit, align it to the left */ + if (crumb1->current_w < crumb1->full_w) + style |= BS_LEFT; + else + style |= BS_CENTER; + + SetWindowLongPtrW(crumb1->hwnd, GWL_STYLE, style); + + crumb1->x = prev_x - crumb1->current_w; + prev_x = crumb1->x; + } + else + break; + + crumbs_visible_n -= 1; + } +} + +static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) +{ + struct crumb *crumb; + INT can_fit_n = info->crumbs_visible_n; + UINT background_flags = 0; + + LIST_FOR_EACH_ENTRY_REV(crumb, &info->crumbs, struct crumb, entry) + { + UINT flags = 0; + + if (can_fit_n > 0) + flags |= SWP_SHOWWINDOW | SWP_NOCOPYBITS; + else + flags |= SWP_HIDEWINDOW; + + hdwp = DeferWindowPos(hdwp, crumb->hwnd, HWND_TOP, + crumb->x, 0, + crumb->current_w, info->container_h, + flags); + + can_fit_n -= 1; + } + + if (info->background_w > 0) + background_flags |= SWP_SHOWWINDOW | SWP_NOCOPYBITS; + else + background_flags |= SWP_HIDEWINDOW; + + hdwp = DeferWindowPos(hdwp, info->background_hwnd, HWND_TOP, + info->background_x, 0, + info->background_w, info->container_h, + background_flags); + + return hdwp; +} + static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { CREATESTRUCTW *cs = (CREATESTRUCTW*)lparam; NAVBAR_INFO *info; + HDC hdc; INT x; HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT);
info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NAVBAR_INFO)); + list_init(&info->crumbs); info->parent_hwnd = GetParent(hwnd); info->container_hwnd = hwnd; + info->container_h = cs->cy; + + hdc = GetDC(0); + info->dpi_x = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(0, hdc);
info->icons = ImageList_LoadImageW(COMDLG32_hInstance, MAKEINTRESOURCEW(IDB_NAVBAR), 24, 0, CLR_NONE, IMAGE_BITMAP, LR_CREATEDIBSECTION); @@ -114,6 +324,13 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) set_icon(info->icons, ILI_UP, info->up_btn_hwnd); set_title_and_add_tooltip(info, info->up_btn_hwnd, IDS_UPFOLDER);
+ x += cs->cy + 1; + info->background_hwnd = CreateWindowExW(0, WC_STATICW, NULL, + WS_CHILD | WS_VISIBLE, + x, 0, 0, cs->cy, + hwnd, 0, COMDLG32_hInstance, NULL); + SetWindowSubclass(info->background_hwnd, NAVBAR_BACKGROUND_SubclassProc, 0, (DWORD_PTR)info); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -121,6 +338,18 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) { + struct crumb *crumb1, *crumb2; + + LIST_FOR_EACH_ENTRY_SAFE(crumb1, crumb2, &info->crumbs, struct crumb, entry) + { + ITEMIDLIST *pidl = NULL; + pidl = (ITEMIDLIST*)GetWindowLongPtrW(crumb1->hwnd, GWLP_USERDATA); + ILFree(pidl); + + list_remove(&crumb1->entry); + HeapFree(GetProcessHeap(), 0, crumb1); + } + SetWindowLongPtrW(hwnd, 0, 0); ImageList_Destroy(info->icons);
@@ -129,6 +358,19 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa return DefWindowProcW(hwnd, msg, wparam, lparam); }
+static LRESULT NAVBAR_Size(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + HDWP hdwp; + + NAVBAR_CalcLayout(info); + + hdwp = BeginDeferWindowPos(info->crumbs_total_n + 1); + hdwp = NAVBAR_DoLayout(info, hdwp); + EndDeferWindowPos(hdwp); + + return TRUE; +} + static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) { switch (LOWORD(wparam)) @@ -142,11 +384,120 @@ static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa case IDC_NAVUP: SendMessageW(info->parent_hwnd, NBN_NAVUP, 0, 0); break; + case IDC_NAVCRUMB: + { + HWND crumb = (HWND)lparam; + ITEMIDLIST *pidl = (ITEMIDLIST*)GetWindowLongPtrW(crumb, GWLP_USERDATA); + SendMessageW(info->parent_hwnd, NBN_NAVPIDL, 0, (LPARAM)pidl); + break; + } }
return DefWindowProcW(hwnd, msg, wparam, lparam); }
+static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + ITEMIDLIST *pidl = (ITEMIDLIST *)lparam; + IShellFolder *desktop; + HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); + HRESULT hr; + INT padding = MulDiv(10, info->dpi_x, USER_DEFAULT_SCREEN_DPI); + struct list new_crumbs; + struct crumb *crumb1, *crumb2; + HDWP hdwp; + + TRACE("info %p pidl %p\n", info, pidl); + + hr = SHGetDesktopFolder(&desktop); + if (FAILED(hr)) + { + ERR("failed to get desktop folder\n"); + goto exit; + } + + list_init(&new_crumbs); + info->crumbs_total_n = 0; + pidl = ILClone(pidl); + do + { + STRRET strret; + SIZE full_size; + + crumb1 = HeapAlloc(GetProcessHeap(), 0, sizeof(*crumb1)); + + crumb1->pidl = ILClone(pidl); + IShellFolder_GetDisplayNameOf(desktop, pidl, SHGDN_FORADDRESSBAR, &strret); + StrRetToBufW(&strret, pidl, crumb1->display_name, MAX_PATH); + + crumb1->hwnd = CreateWindowExW(0, WC_BUTTONW, crumb1->display_name, WS_CHILD, + 0, 0, 0, 0, + info->container_hwnd, (HMENU)IDC_NAVCRUMB, COMDLG32_hInstance, NULL); + 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); + + crumb1->full_w = full_size.cx + padding; + crumb1->current_w = crumb1->full_w; + + /* PIDL is iterated from right-to-left, prepend the crumb to store them in left-to-right order */ + list_add_head(&new_crumbs, &crumb1->entry); + info->crumbs_total_n += 1; + } + while (ILRemoveLastID(pidl)); + ILFree(pidl); + + /* reuse existing crumbs */ + crumb1 = LIST_ENTRY((&info->crumbs)->next, struct crumb, entry); + crumb2 = LIST_ENTRY((&new_crumbs)->next, struct crumb, entry); + for (;;) + { + if (&crumb1->entry == &info->crumbs || + &crumb2->entry == &new_crumbs || + !ILIsEqual(crumb2->pidl, crumb1->pidl)) + break; + + DestroyWindow(crumb2->hwnd); + ILFree(crumb2->pidl); + crumb2->pidl = crumb1->pidl; + crumb2->hwnd = crumb1->hwnd; + + crumb1 = LIST_ENTRY(crumb1->entry.next, struct crumb, entry); + crumb2 = LIST_ENTRY(crumb2->entry.next, struct crumb, entry); + } + + /* cleanup unused existing crumbs */ + for (;;) + { + if (&crumb1->entry == &info->crumbs) + break; + + DestroyWindow(crumb1->hwnd); + ILFree(crumb1->pidl); + + crumb1 = LIST_ENTRY(crumb1->entry.next, struct crumb, entry); + } + + LIST_FOR_EACH_ENTRY_SAFE(crumb1, crumb2, &info->crumbs, struct crumb, entry) + { + list_remove(&crumb1->entry); + HeapFree(GetProcessHeap(), 0, crumb1); + } + + new_crumbs.next->prev = &info->crumbs; + new_crumbs.prev->next = &info->crumbs; + info->crumbs = new_crumbs; + + NAVBAR_CalcLayout(info); + + hdwp = BeginDeferWindowPos(info->crumbs_total_n + 1); + hdwp = NAVBAR_DoLayout(info, hdwp); + EndDeferWindowPos(hdwp); + +exit: + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { NAVBAR_INFO *info = (NAVBAR_INFO *)GetWindowLongPtrW(hwnd, 0); @@ -158,7 +509,9 @@ static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LP { case WM_NCCREATE: return NAVBAR_Create(hwnd, msg, wparam, lparam); case WM_DESTROY: return NAVBAR_Destroy(hwnd, info, msg, wparam, lparam); + case WM_SIZE: return NAVBAR_Size(hwnd, info, msg, wparam, lparam); case WM_COMMAND: return NAVBAR_Command(hwnd, info, msg, wparam, lparam); + case NBM_SETPIDL: return NAVBAR_SetPIDL(hwnd, info, msg, wparam, lparam); }
exit: diff --git a/dlls/comdlg32/navbar.h b/dlls/comdlg32/navbar.h index 367dc2f1b37..a077cf58095 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -25,10 +25,14 @@
#define WC_NAVBARW L"NavBar"
+/* navbar messages */ +#define NBM_SETPIDL WM_USER + 101 + /* navbar notifications */ #define NBN_NAVBACK WM_USER + 102 #define NBN_NAVFORWARD WM_USER + 103 #define NBN_NAVUP WM_USER + 104 +#define NBN_NAVPIDL WM_USER + 105
/* strings */ #define IDS_BACK 2000
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/navbar.c | 189 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 181 insertions(+), 8 deletions(-)
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 635bc9d2e51..85798397685 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -25,6 +25,7 @@ #include "cdlg.h" #include "filedlgbrowser.h" #include "shlwapi.h" +#include "commoncontrols.h"
#include "wine/debug.h" #include "wine/list.h" @@ -36,6 +37,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg); #define IDC_NAVFORWARD 202 #define IDC_NAVUP 203 #define IDC_NAVCRUMB 204 +#define IDC_OVERFLOW 205
typedef struct { HWND parent_hwnd; @@ -57,12 +59,15 @@ typedef struct { struct list crumbs; INT crumbs_total_n; INT crumbs_visible_n; + + HWND overflow_hwnd; + HMENU overflow_menu; } NAVBAR_INFO;
struct crumb { struct list entry; ITEMIDLIST *pidl; - WCHAR display_name[MAX_PATH]; + WCHAR *display_name; HWND hwnd; INT full_w; INT current_w; @@ -96,6 +101,87 @@ static void set_title_and_add_tooltip(NAVBAR_INFO *info, HWND window, UINT strin SendMessageW(info->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo); }
+static void NAVBAR_OVERFLOW_Insert(NAVBAR_INFO *info, ITEMIDLIST* pidl, WCHAR *display_name) +{ + MENUITEMINFOW menu_item = {0}; + + TRACE("info %p pidl %p display_name %s\n", info, pidl, debugstr_w(display_name)); + + menu_item.cbSize = sizeof(MENUITEMINFOW); + menu_item.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STRING | MIIM_BITMAP; + menu_item.fType = MFT_STRING; + menu_item.dwItemData = (ULONG_PTR)pidl; + menu_item.dwTypeData = display_name; /* copied by InsertMenuItemW */ + menu_item.cch = lstrlenW(display_name); + menu_item.hbmpItem = HBMMENU_CALLBACK; /* see NAVBAR_OVERFLOW_DrawIcon */ + + InsertMenuItemW(info->overflow_menu, -1, TRUE, &menu_item); +} + +static void NAVBAR_OVERFLOW_Clear(NAVBAR_INFO *info) +{ + INT i, menu_item_count = GetMenuItemCount(info->overflow_menu); + MENUITEMINFOW menu_item = {0}; + + TRACE("info %p menu_item_count %i\n", info, menu_item_count); + + menu_item.cbSize = sizeof(MENUITEMINFOW); + menu_item.fMask = MIIM_DATA; + + for (i = menu_item_count - 1; i >= 0; i--) + { + GetMenuItemInfoW(info->overflow_menu, i, TRUE, &menu_item); + ILFree((ITEMIDLIST *)menu_item.dwItemData); + DeleteMenu(info->overflow_menu, i, MF_BYPOSITION); + } +} + +static LRESULT NAVBAR_OVERFLOW_MeasureIcon(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + MEASUREITEMSTRUCT *mis = (MEASUREITEMSTRUCT *)lparam; + ITEMIDLIST *pidl = (ITEMIDLIST *)mis->itemData; + SHFILEINFOW file_info = {0}; + IImageList *icon_list; + + icon_list = (IImageList *)SHGetFileInfoW((const WCHAR *)pidl, 0, &file_info, sizeof(file_info), + SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SHELLICONSIZE | SHGFI_SYSICONINDEX); + if (icon_list) + { + IImageList_GetIconSize(icon_list, (int *)&mis->itemWidth, (int *)&mis->itemHeight); + IImageList_Release(icon_list); + DestroyIcon(file_info.hIcon); + + /* add some padding to the right side so that the text isn't so close to the icon */ + /* 4px is the default left side padding for MNS_NOCHECK */ + mis->itemWidth += 4; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static LRESULT NAVBAR_OVERFLOW_DrawIcon(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam; + ITEMIDLIST *pidl = (ITEMIDLIST *)dis->itemData; + SHFILEINFOW file_info = {0}; + IImageList *icon_list; + + icon_list = (IImageList *)SHGetFileInfoW((const WCHAR *)pidl, 0, &file_info, sizeof(file_info), + SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SHELLICONSIZE | SHGFI_SYSICONINDEX); + if (icon_list) + { + int icon_width, icon_height; + + IImageList_GetIconSize(icon_list, &icon_width, &icon_height); + DrawIconEx(dis->hDC, dis->rcItem.left, dis->rcItem.top, + file_info.hIcon, icon_width, icon_height, 0, NULL, DI_NORMAL); + IImageList_Release(icon_list); + DestroyIcon(file_info.hIcon); + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static LRESULT CALLBACK NAVBAR_BACKGROUND_SubclassProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id_subclass, DWORD_PTR ref_data) { switch (msg) @@ -120,7 +206,7 @@ static LRESULT CALLBACK NAVBAR_BACKGROUND_SubclassProc(HWND hwnd, UINT msg, WPAR
static void NAVBAR_CalcLayout(NAVBAR_INFO *info) { - RECT container_rc, up_button_rc; + RECT container_rc, overflow_rc; struct crumb *crumb1, *crumb2; INT padding, container_w, buttons_w, max_crumbs_w, @@ -136,10 +222,10 @@ static void NAVBAR_CalcLayout(NAVBAR_INFO *info) if (!container_w) return;
- if (!GetWindowRect(info->up_btn_hwnd, &up_button_rc)) + if (!GetWindowRect(info->overflow_hwnd, &overflow_rc)) return;
- buttons_w = up_button_rc.right + 1 - padding; + buttons_w = overflow_rc.right - padding; max_crumbs_w = container_w - buttons_w; if (max_crumbs_w < 0) return; @@ -241,6 +327,9 @@ static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) struct crumb *crumb; INT can_fit_n = info->crumbs_visible_n; UINT background_flags = 0; + BUTTON_SPLITINFO split_info = {0}; + + NAVBAR_OVERFLOW_Clear(info);
LIST_FOR_EACH_ENTRY_REV(crumb, &info->crumbs, struct crumb, entry) { @@ -249,7 +338,10 @@ static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) if (can_fit_n > 0) flags |= SWP_SHOWWINDOW | SWP_NOCOPYBITS; else + { flags |= SWP_HIDEWINDOW; + NAVBAR_OVERFLOW_Insert(info, ILClone(crumb->pidl), crumb->display_name); + }
hdwp = DeferWindowPos(hdwp, crumb->hwnd, HWND_TOP, crumb->x, 0, @@ -259,6 +351,18 @@ static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) can_fit_n -= 1; }
+ split_info.mask = BCSIF_STYLE; + + if (GetMenuItemCount(info->overflow_menu) > 0) + /* reset split style to re-enable split and dropdown arrow if they were disabled */ + SendMessageW(info->overflow_hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&split_info); + else + { + /* remove the split and dropdown arrow when there are no items in the overflow */ + split_info.uSplitStyle = BCSS_NOSPLIT | BCSS_IMAGE; + SendMessageW(info->overflow_hwnd, BCM_SETSPLITINFO, 0, (LPARAM)&split_info); + } + if (info->background_w > 0) background_flags |= SWP_SHOWWINDOW | SWP_NOCOPYBITS; else @@ -279,6 +383,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) HDC hdc; INT x; HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); + MENUINFO menu_info = {0};
info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NAVBAR_INFO)); list_init(&info->crumbs); @@ -331,6 +436,18 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) hwnd, 0, COMDLG32_hInstance, NULL); SetWindowSubclass(info->background_hwnd, NAVBAR_BACKGROUND_SubclassProc, 0, (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); + + info->overflow_menu = CreatePopupMenu(); + menu_info.cbSize = sizeof(MENUINFO); + menu_info.fMask = MIM_STYLE; + menu_info.dwStyle = MNS_NOCHECK | MNS_NOTIFYBYPOS; + SetMenuInfo(info->overflow_menu, &menu_info); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -342,9 +459,8 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa
LIST_FOR_EACH_ENTRY_SAFE(crumb1, crumb2, &info->crumbs, struct crumb, entry) { - ITEMIDLIST *pidl = NULL; - pidl = (ITEMIDLIST*)GetWindowLongPtrW(crumb1->hwnd, GWLP_USERDATA); - ILFree(pidl); + ILFree(crumb1->pidl); + CoTaskMemFree(crumb1->display_name);
list_remove(&crumb1->entry); HeapFree(GetProcessHeap(), 0, crumb1); @@ -352,6 +468,8 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa
SetWindowLongPtrW(hwnd, 0, 0); ImageList_Destroy(info->icons); + NAVBAR_OVERFLOW_Clear(info); + DestroyMenu(info->overflow_menu);
HeapFree(GetProcessHeap(), 0, info);
@@ -396,12 +514,32 @@ static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa return DefWindowProcW(hwnd, msg, wparam, lparam); }
+static LRESULT NAVBAR_MenuCommand(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + UINT pos = wparam; + HMENU menu = (HMENU)lparam; + MENUITEMINFOW menu_item = {0}; + + TRACE("info %p pos %i menu %p\n", info, pos, menu); + + menu_item.cbSize = sizeof(MENUITEMINFOW); + menu_item.fMask = MIIM_DATA; + + if (GetMenuItemInfoW(menu, pos, TRUE, &menu_item)) + SendMessageW(info->parent_hwnd, NBN_NAVPIDL, 0, (LPARAM)menu_item.dwItemData); + else + ERR("failed to get menu item info\n"); + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) { ITEMIDLIST *pidl = (ITEMIDLIST *)lparam; IShellFolder *desktop; HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); HRESULT hr; + SHFILEINFOW file_info; INT padding = MulDiv(10, info->dpi_x, USER_DEFAULT_SCREEN_DPI); struct list new_crumbs; struct crumb *crumb1, *crumb2; @@ -416,6 +554,15 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa goto exit; }
+ if (SHGetFileInfoW((const WCHAR *)pidl, 0, &file_info, sizeof(file_info), + SHGFI_PIDL | SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SHELLICONSIZE)) + { + SendMessageW(info->overflow_hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM)file_info.hIcon); + DestroyIcon(file_info.hIcon); + } + else + WARN("failed to get file info for pidl %p\n", pidl); + list_init(&new_crumbs); info->crumbs_total_n = 0; pidl = ILClone(pidl); @@ -428,7 +575,7 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa
crumb1->pidl = ILClone(pidl); IShellFolder_GetDisplayNameOf(desktop, pidl, SHGDN_FORADDRESSBAR, &strret); - StrRetToBufW(&strret, pidl, crumb1->display_name, MAX_PATH); + StrRetToStrW(&strret, pidl, &crumb1->display_name);
crumb1->hwnd = CreateWindowExW(0, WC_BUTTONW, crumb1->display_name, WS_CHILD, 0, 0, 0, 0, @@ -446,6 +593,7 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa } while (ILRemoveLastID(pidl)); ILFree(pidl); + IShellFolder_Release(desktop);
/* reuse existing crumbs */ crumb1 = LIST_ENTRY((&info->crumbs)->next, struct crumb, entry); @@ -459,7 +607,9 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa
DestroyWindow(crumb2->hwnd); ILFree(crumb2->pidl); + CoTaskMemFree(crumb2->display_name); crumb2->pidl = crumb1->pidl; + crumb2->display_name = crumb1->display_name; crumb2->hwnd = crumb1->hwnd;
crumb1 = LIST_ENTRY(crumb1->entry.next, struct crumb, entry); @@ -474,6 +624,7 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa
DestroyWindow(crumb1->hwnd); ILFree(crumb1->pidl); + CoTaskMemFree(crumb1->display_name);
crumb1 = LIST_ENTRY(crumb1->entry.next, struct crumb, entry); } @@ -498,6 +649,24 @@ exit: return DefWindowProcW(hwnd, msg, wparam, lparam); }
+static LRESULT NAVBAR_Notify(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + NMHDR *nmhdr = (NMHDR *)lparam; + + if (nmhdr && nmhdr->hwndFrom == info->overflow_hwnd && nmhdr->code == BCN_DROPDOWN) + { + NMBCDROPDOWN* dropdown = (NMBCDROPDOWN *)lparam; + POINT pt = { .x = dropdown->rcButton.left, + .y = dropdown->rcButton.bottom, }; + + ClientToScreen(info->overflow_hwnd, &pt); + TrackPopupMenu(info->overflow_menu, TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, 0, hwnd, NULL); + return TRUE; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { NAVBAR_INFO *info = (NAVBAR_INFO *)GetWindowLongPtrW(hwnd, 0); @@ -511,6 +680,10 @@ static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LP case WM_DESTROY: return NAVBAR_Destroy(hwnd, info, msg, wparam, lparam); case WM_SIZE: return NAVBAR_Size(hwnd, info, msg, wparam, lparam); case WM_COMMAND: return NAVBAR_Command(hwnd, info, msg, wparam, lparam); + case WM_MENUCOMMAND: return NAVBAR_MenuCommand(hwnd, info, msg, wparam, lparam); + case WM_NOTIFY: return NAVBAR_Notify(hwnd, info, msg, wparam, lparam); + case WM_MEASUREITEM: return NAVBAR_OVERFLOW_MeasureIcon(hwnd, info, msg, wparam, lparam); + case WM_DRAWITEM: return NAVBAR_OVERFLOW_DrawIcon(hwnd, info, msg, wparam, lparam); case NBM_SETPIDL: return NAVBAR_SetPIDL(hwnd, info, msg, wparam, lparam); }
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
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/comdlg32.rc | 2 + dlls/comdlg32/itemdlg.c | 18 +++++++++ dlls/comdlg32/navbar.c | 78 ++++++++++++++++++++++++++++++++++++--- dlls/comdlg32/navbar.h | 3 ++ 4 files changed, 95 insertions(+), 6 deletions(-)
diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc index 4523d57598c..0add4c32694 100644 --- a/dlls/comdlg32/comdlg32.rc +++ b/dlls/comdlg32/comdlg32.rc @@ -156,6 +156,8 @@ STRINGTABLE IDS_COPY_ADDRESS_AS_TEXT "Copy address as text" IDS_EDIT_ADDRESS "Edit address" IDS_WINE_CANT_FIND_ADDRESS "Wine can't find '%s'." + IDS_REFRESH "Refresh" + IDS_GOTO "Go to" }
/* diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index e38415821a1..04418fb6eba 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -2234,6 +2234,23 @@ static LRESULT on_browse_pidl(FileDialogImpl *This, LPARAM lparam) return FALSE; }
+static LRESULT on_browse_refresh(FileDialogImpl *This) +{ + IShellView *psv; + HRESULT hr; + + TRACE("%p\n", This); + + hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&psv); + if (SUCCEEDED(hr)) + { + IShellView_Refresh(psv); + IShellView_Release(psv); + } + + return FALSE; +} + static LRESULT on_command_filetype(FileDialogImpl *This, WPARAM wparam, LPARAM lparam) { if(HIWORD(wparam) == CBN_SELCHANGE) @@ -2311,6 +2328,7 @@ static INT_PTR CALLBACK itemdlg_dlgproc(HWND hwnd, UINT umessage, WPARAM wparam, case NBN_NAVFORWARD: return on_browse_forward(This); case NBN_NAVUP: return on_browse_up(This); case NBN_NAVPIDL: return on_browse_pidl(This, lparam); + case NBN_NAVREFRESH: return on_browse_refresh(This); }
return FALSE; diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 60bcca0de8c..7a58366bdd8 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -39,6 +39,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg); #define IDC_NAVUP 203 #define IDC_NAVCRUMB 204 #define IDC_OVERFLOW 205 +#define IDC_NAVREFRESHGOTO 206
typedef struct { HWND parent_hwnd; @@ -52,6 +53,7 @@ typedef struct { HWND back_btn_hwnd; HWND fwd_btn_hwnd; HWND up_btn_hwnd; + HWND refresh_goto_btn_hwnd;
HWND background_hwnd; INT background_x; @@ -104,6 +106,18 @@ static void set_title_and_add_tooltip(NAVBAR_INFO *info, HWND window, UINT strin SendMessageW(info->tooltip, TTM_ADDTOOLW, 0, (LPARAM)&toolinfo); }
+static void remove_tooltip(NAVBAR_INFO *info, HWND window) +{ + TOOLINFOW toolinfo = {0}; + + toolinfo.cbSize = sizeof(toolinfo); + toolinfo.uFlags = TTF_IDISHWND; + toolinfo.hwnd = info->container_hwnd; + toolinfo.uId = (UINT_PTR)window; + + SendMessageW(info->tooltip, TTM_DELTOOLW, 0, (LPARAM)&toolinfo); +} + static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp);
static void NAVBAR_PATHEDIT_SetCurrentPath(NAVBAR_INFO *info) @@ -133,6 +147,13 @@ static void NAVBAR_PATHEDIT_Edit(NAVBAR_INFO *info) hdwp = DeferWindowPos(hdwp, info->background_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_HIDEWINDOW); EndDeferWindowPos(hdwp);
+ /* change Refresh button to Go to button */ + set_icon(info->icons, ILI_FORWARD, info->refresh_goto_btn_hwnd); + + /* update title and tooltip to Go to */ + remove_tooltip(info, info->refresh_goto_btn_hwnd); + set_title_and_add_tooltip(info, info->refresh_goto_btn_hwnd, IDS_GOTO); + SetFocus(info->pathedit_hwnd); }
@@ -155,11 +176,21 @@ static void NAVBAR_PATHEDIT_Dismiss(NAVBAR_INFO *info) hdwp = DeferWindowPos(hdwp, info->overflow_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); hdwp = NAVBAR_DoLayout(info, hdwp); EndDeferWindowPos(hdwp); + + /* change Go to button back to Refresh button */ + set_icon(info->icons, ILI_REFRESH, info->refresh_goto_btn_hwnd); + + /* update title and tooltip to Refresh */ + remove_tooltip(info, info->refresh_goto_btn_hwnd); + set_title_and_add_tooltip(info, info->refresh_goto_btn_hwnd, IDS_REFRESH); }
static LRESULT NAVBAR_PATHEDIT_KillFocus(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) { - NAVBAR_PATHEDIT_Dismiss(info); + /* if user clicked on Go to button, don't dismiss the path edit, */ + /* let either Go to focus handler or NAVBAR_Command dismiss it */ + if (GetFocus() != info->refresh_goto_btn_hwnd) + NAVBAR_PATHEDIT_Dismiss(info);
return DefSubclassProc(hwnd, msg, wparam, lparam); } @@ -341,6 +372,21 @@ static LRESULT CALLBACK NAVBAR_PATHEDIT_SubclassProc(HWND hwnd, UINT msg, WPARAM return DefSubclassProc(hwnd, msg, wparam, lparam); }
+static LRESULT CALLBACK NAVBAR_REFRESHGOTO_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_KILLFOCUS: + if (GetFocus() != info->pathedit_hwnd) + NAVBAR_PATHEDIT_Dismiss(info); + break; + } + + 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; @@ -488,10 +534,10 @@ static LRESULT CALLBACK NAVBAR_BACKGROUND_SubclassProc(HWND hwnd, UINT msg, WPAR
static void NAVBAR_CalcLayout(NAVBAR_INFO *info) { - RECT container_rc, overflow_rc; + RECT container_rc, overflow_rc, refresh_goto_btn_rc; struct crumb *crumb1, *crumb2; INT padding, container_w, - buttons_w, overflow_w, max_crumbs_w, + buttons_w, overflow_w, max_crumbs_w, refresh_goto_btn_w, w = 0, crumbs_visible_n = 0, prev_x;
if (!GetWindowRect(info->container_hwnd, &container_rc)) @@ -513,10 +559,15 @@ static void NAVBAR_CalcLayout(NAVBAR_INFO *info) if (max_crumbs_w < 0) return;
- SetWindowPos(info->pathedit_hwnd, NULL, 0, 0, overflow_w + max_crumbs_w, info->container_h, SWP_NOMOVE); + if (!GetClientRect(info->refresh_goto_btn_hwnd, &refresh_goto_btn_rc)) + return; + refresh_goto_btn_w = refresh_goto_btn_rc.right - refresh_goto_btn_rc.left; + + SetWindowPos(info->pathedit_hwnd, NULL, 0, 0, overflow_w + max_crumbs_w - refresh_goto_btn_w, info->container_h, SWP_NOMOVE); + SetWindowPos(info->refresh_goto_btn_hwnd, NULL, container_w - refresh_goto_btn_w, 0, 0, 0, SWP_NOSIZE);
/* reserve some space on the right side for click-to-edit area */ - max_crumbs_w -= MulDiv(64, info->dpi_x, USER_DEFAULT_SCREEN_DPI); + max_crumbs_w -= MulDiv(64, info->dpi_x, USER_DEFAULT_SCREEN_DPI) + refresh_goto_btn_w; if (max_crumbs_w < 0) return;
@@ -584,7 +635,7 @@ static void NAVBAR_CalcLayout(NAVBAR_INFO *info) prev_x = buttons_w + w;
info->background_x = prev_x; - info->background_w = container_rc.right - prev_x; + info->background_w = container_rc.right - refresh_goto_btn_w - prev_x;
info->crumbs_visible_n = crumbs_visible_n;
@@ -754,6 +805,15 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) SendMessageW(info->pathedit_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); SetWindowSubclass(info->pathedit_hwnd, NAVBAR_PATHEDIT_SubclassProc, 0, (DWORD_PTR)info);
+ info->refresh_goto_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, + WS_CHILD | WS_VISIBLE | BS_ICON | BS_BITMAP, + 0, 0, cs->cy, cs->cy, + hwnd, (HMENU)IDC_NAVREFRESHGOTO, COMDLG32_hInstance, NULL); + SendMessageW(info->refresh_goto_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + set_icon(info->icons, ILI_REFRESH, info->refresh_goto_btn_hwnd); + set_title_and_add_tooltip(info, info->refresh_goto_btn_hwnd, IDS_REFRESH); + SetWindowSubclass(info->refresh_goto_btn_hwnd, NAVBAR_REFRESHGOTO_SubclassProc, 0, (DWORD_PTR)info); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -821,6 +881,12 @@ static LRESULT NAVBAR_Command(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa case IDC_OVERFLOW: NAVBAR_PATHEDIT_Edit(info); break; + case IDC_NAVREFRESHGOTO: + if (IsWindowVisible(info->pathedit_hwnd)) + NAVBAR_PATHEDIT_GoTo(info); + else + SendMessageW(info->parent_hwnd, NBN_NAVREFRESH, 0, 0); + break; }
return DefWindowProcW(hwnd, msg, wparam, lparam); diff --git a/dlls/comdlg32/navbar.h b/dlls/comdlg32/navbar.h index aa89a352cf5..9633b303636 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -33,6 +33,7 @@ #define NBN_NAVFORWARD WM_USER + 103 #define NBN_NAVUP WM_USER + 104 #define NBN_NAVPIDL WM_USER + 105 +#define NBN_NAVREFRESH WM_USER + 106
/* strings */ #define IDS_BACK 2000 @@ -40,6 +41,8 @@ #define IDS_COPY_ADDRESS_AS_TEXT 2004 #define IDS_EDIT_ADDRESS 2005 #define IDS_WINE_CANT_FIND_ADDRESS 2006 +#define IDS_REFRESH 2007 +#define IDS_GOTO 2008
/* bitmaps */ #define IDB_NAVBAR 3000
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/navbar.c | 267 +++++++++++++++++++++++++++++++++++------ 1 file changed, 227 insertions(+), 40 deletions(-)
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 7a58366bdd8..ab8e96a8064 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -27,6 +27,7 @@ #include "shlwapi.h" #include "commoncontrols.h" #include "pathcch.h" +#include "knownfolders.h"
#include "wine/debug.h" #include "wine/list.h" @@ -41,6 +42,14 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg); #define IDC_OVERFLOW 205 #define IDC_NAVREFRESHGOTO 206
+static const KNOWNFOLDERID *ACCEPTED_KNOWN_FOLDERS[] = +{ + &FOLDERID_Desktop, + &FOLDERID_ComputerFolder, + &FOLDERID_RecycleBinFolder, + &FOLDERID_Documents, +}; + typedef struct { HWND parent_hwnd; HWND container_hwnd; @@ -118,17 +127,65 @@ static void remove_tooltip(NAVBAR_INFO *info, HWND window) SendMessageW(info->tooltip, TTM_DELTOOLW, 0, (LPARAM)&toolinfo); }
+static WCHAR *get_current_path(NAVBAR_INFO *info) +{ + struct crumb *crumb; + WCHAR *cur_path = NULL; + IShellFolder *desktop_sf; + SHGDNF shgdn = SHGDN_FORPARSING; + STRRET strret; + HRESULT hr; + INT i; + + crumb = LIST_ENTRY(list_tail(&info->crumbs), struct crumb, entry); + if (!crumb) + return NULL; + + hr = SHGetDesktopFolder(&desktop_sf); + if (FAILED(hr)) + return NULL; + + /* check if current path is one of the known folders */ + /* and return known folder name instead if it is */ + for (i = 0; i < ARRAY_SIZE(ACCEPTED_KNOWN_FOLDERS); i++) + { + ITEMIDLIST *known_folder_pidl = NULL; + BOOL is_known_folder = FALSE; + + hr = SHGetKnownFolderIDList(ACCEPTED_KNOWN_FOLDERS[i], 0, NULL, &known_folder_pidl); + if (FAILED(hr)) + continue; + + is_known_folder = ILIsEqual(crumb->pidl, known_folder_pidl); + ILFree(known_folder_pidl); + + if (is_known_folder) + { + shgdn = SHGDN_NORMAL; + break; + } + } + + hr = IShellFolder_GetDisplayNameOf(desktop_sf, crumb->pidl, shgdn, &strret); + if (SUCCEEDED(hr)) + hr = StrRetToStrW(&strret, crumb->pidl, &cur_path); + + IShellFolder_Release(desktop_sf); + + return cur_path; +} + 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]; + WCHAR *cur_path = get_current_path(info); + if (!cur_path) + return;
- 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 */ + CoTaskMemFree(cur_path); }
static void NAVBAR_PATHEDIT_Edit(NAVBAR_INFO *info) @@ -195,36 +252,98 @@ static LRESULT NAVBAR_PATHEDIT_KillFocus(HWND hwnd, NAVBAR_INFO *info, UINT msg, return DefSubclassProc(hwnd, msg, wparam, lparam); }
-static void NAVBAR_PATHEDIT_GoTo(NAVBAR_INFO *info) +static ITEMIDLIST *NAVBAR_PATHEDIT_ParseUnixPath(WCHAR *text) { - WCHAR *text = NULL, *expanded = NULL, *canonicalized = NULL; - INT text_len = 0; - DWORD expanded_len; + ITEMIDLIST *pidl = NULL; + WCHAR *dos_path = NULL; + CHAR *unix_path = NULL; + INT unix_path_len; + + unix_path_len = WideCharToMultiByte(CP_UNIXCP, 0, text, -1, NULL, 0, NULL, NULL); + unix_path = HeapAlloc(GetProcessHeap(), 0, unix_path_len * sizeof(WCHAR)); + if (!unix_path) + return NULL; + + unix_path_len = WideCharToMultiByte(CP_UNIXCP, 0, text, -1, unix_path, unix_path_len, NULL, NULL); + if (!unix_path_len) + return NULL; + + dos_path = wine_get_dos_file_name(unix_path); + HeapFree(GetProcessHeap(), 0, unix_path); + + TRACE("%s\n", debugstr_w(dos_path)); + + if (!PathFileExistsW(dos_path)) + return NULL; + + SHILCreateFromPath(dos_path, &pidl, 0); + + return pidl; +} + +static ITEMIDLIST *NAVBAR_PATHEDIT_ParseKnownFolder(WCHAR *text) +{ + IShellFolder *desktop = NULL; ITEMIDLIST *pidl = NULL; HRESULT hr; + INT i;
- text_len = GetWindowTextLengthW(info->pathedit_hwnd); - if (!text_len) - goto exit; + hr = SHGetDesktopFolder(&desktop); + if (FAILED(hr)) + return NULL;
- text = HeapAlloc(GetProcessHeap(), 0, (text_len + 1) * sizeof(WCHAR)); - if (!text) - goto exit; + for (i = 0; i < ARRAY_SIZE(ACCEPTED_KNOWN_FOLDERS); i++) + { + ITEMIDLIST *known_folder_pidl = NULL; + STRRET strret;
- if (!GetWindowTextW(info->pathedit_hwnd, text, text_len + 1)) - goto cleanup; + hr = SHGetKnownFolderIDList(ACCEPTED_KNOWN_FOLDERS[i], 0, NULL, &known_folder_pidl); + if (FAILED(hr)) + continue;
- TRACE("text %s\n", debugstr_w(text)); + hr = IShellFolder_GetDisplayNameOf(desktop, known_folder_pidl, SHGDN_NORMAL, &strret); + if (SUCCEEDED(hr)) + { + WCHAR *name;
- StrTrimW(text, L" "); + hr = StrRetToStrW(&strret, known_folder_pidl, &name); + if (SUCCEEDED(hr)) + { + BOOL is_known_folder = !lstrcmpiW(name, text); + CoTaskMemFree(name); + + if (is_known_folder) + { + TRACE("%s\n", debugstr_guid(ACCEPTED_KNOWN_FOLDERS[i])); + pidl = known_folder_pidl; + break; + } + } + } + + ILFree(known_folder_pidl); + } + + IShellFolder_Release(desktop); + + return pidl; +} + +static ITEMIDLIST *NAVBAR_PATHEDIT_ParseWindowsPath(NAVBAR_INFO *info, WCHAR *text) +{ + WCHAR *expanded = NULL, *full = NULL; + DWORD expanded_len; + ITEMIDLIST *pidl = NULL; + IShellFolder *desktop = NULL; + HRESULT hr = S_OK;
expanded_len = ExpandEnvironmentStringsW(text, NULL, 0); if (!expanded_len) - goto cleanup; + return NULL;
expanded = HeapAlloc(GetProcessHeap(), 0, expanded_len * sizeof(WCHAR)); if (!expanded) - goto cleanup; + return NULL;
expanded_len = ExpandEnvironmentStringsW(text, expanded, expanded_len); if (!expanded_len) @@ -232,38 +351,106 @@ static void NAVBAR_PATHEDIT_GoTo(NAVBAR_INFO *info)
TRACE("expanded %s\n", debugstr_w(expanded));
- hr = PathAllocCanonicalize(expanded, PATHCCH_ALLOW_LONG_PATHS, &canonicalized); - if (FAILED(hr)) + if (PathIsRelativeW(expanded)) + { + WCHAR *current = get_current_path(info); + + if (!PathIsRelativeW(current)) + { + hr = PathAllocCombine(current, expanded, PATHCCH_ALLOW_LONG_PATHS, &full); + TRACE("current %s relative %s combined %s\n", debugstr_w(current), debugstr_w(expanded), debugstr_w(full)); + } + + CoTaskMemFree(current); + if (FAILED(hr)) + goto cleanup; + } + else + { + hr = PathAllocCanonicalize(expanded, PATHCCH_ALLOW_LONG_PATHS, &full); + if (FAILED(hr)) + goto cleanup; + + TRACE("canonicalized %s\n", debugstr_w(full)); + } + + if (!PathFileExistsW(full)) + return NULL; + + SHILCreateFromPath(full, &pidl, 0); + +cleanup: + if (expanded) + HeapFree(GetProcessHeap(), 0, expanded); + if (full) + LocalFree(full); + if (desktop) + IShellFolder_Release(desktop); + + return pidl; +} + +static void NAVBAR_PATHEDIT_GoTo(NAVBAR_INFO *info) +{ + WCHAR *text = NULL; + INT text_len = 0; + ITEMIDLIST *pidl = NULL; + + 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("canonicalized %s\n", debugstr_w(canonicalized)); + StrTrimW(text, L" ");
- /* check if we have a valid path */ - if (GetFileAttributesW(canonicalized) == INVALID_FILE_ATTRIBUTES) + TRACE("text %s\n", debugstr_w(text)); + + if (StrCmpW(text, L"..") == 0 || + StrCmpW(text, L"../") == 0 || + StrCmpW(text, L"..\") == 0) + { + struct crumb *last_crumb = LIST_ENTRY(list_tail(&info->crumbs), struct crumb, entry), + *parent_crumb = NULL; + + if (last_crumb && last_crumb->entry.prev != &info->crumbs) + parent_crumb = LIST_ENTRY(last_crumb->entry.prev, struct crumb, entry); + + if (parent_crumb) + pidl = ILClone(parent_crumb->pidl); + } + else if (text[0] == '/') + pidl = NAVBAR_PATHEDIT_ParseUnixPath(text); + else + { + pidl = NAVBAR_PATHEDIT_ParseKnownFolder(text); + if (!pidl) + pidl = NAVBAR_PATHEDIT_ParseWindowsPath(info, text); + } + + TRACE("pidl %p\n", pidl); + + if (pidl) + { + SendMessageW(info->parent_hwnd, NBN_NAVPIDL, 0, (LPARAM)pidl); + ILFree(pidl); + } + else { 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); + HeapFree(GetProcessHeap(), 0, text); exit: NAVBAR_PATHEDIT_Dismiss(info); }
From: Vladislav Timonin timoninvlad@yandex.ru
To be able to Backspace multiple times.
On Windows, Back/Forward/Up/Refresh buttons don't take focus when clicked on, but we do, workaround this by sending Go Up command on Backspace ourselves. --- dlls/comdlg32/itemdlg.c | 12 ++++++++++++ dlls/comdlg32/navbar.c | 15 +++++++++++++++ 2 files changed, 27 insertions(+)
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 04418fb6eba..c5507a84945 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -3476,7 +3476,19 @@ static HRESULT WINAPI IExplorerBrowserEvents_fnOnNavigationComplete(IExplorerBro if (!hwnd) ERR("Failed to update navbar.\n"); else + { + IShellView *shellview; + HWND shellview_hwnd; + SendMessageW(hwnd, NBM_SETPIDL, 0, (LPARAM)pidlFolder); + + /* keep focus on the explorer browser to be able to Backspace multiple times */ + hr = IExplorerBrowser_GetCurrentView(This->peb, &IID_IShellView, (void**)&shellview); + if (SUCCEEDED(hr)) + hr = IShellView_GetWindow(shellview, &shellview_hwnd); + if (SUCCEEDED(hr)) + SetFocus(shellview_hwnd); + } }
events_OnFolderChange(This); diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index ab8e96a8064..3006c3c691e 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -692,6 +692,17 @@ static LRESULT NAVBAR_OVERFLOW_DrawIcon(HWND hwnd, NAVBAR_INFO *info, UINT msg, return DefWindowProcW(hwnd, msg, wparam, lparam); }
+static LRESULT CALLBACK NAVBAR_BackspaceProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT_PTR id_subclass, DWORD_PTR ref_data) +{ + NAVBAR_INFO *info = (NAVBAR_INFO *)ref_data; + + if (msg == WM_KEYDOWN && wparam == VK_BACK && !IsWindowVisible(info->pathedit_hwnd)) + /* match navigation direction with ShellView_OnNotify */ + SendMessageW(info->container_hwnd, WM_COMMAND, IDC_NAVUP, (LPARAM)hwnd); + + return DefSubclassProc(hwnd, msg, wparam, lparam); +} + 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; @@ -943,6 +954,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) SendMessageW(info->back_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); set_icon(info->icons, ILI_BACK, info->back_btn_hwnd); set_title_and_add_tooltip(info, info->back_btn_hwnd, IDS_BACK); + SetWindowSubclass(info->back_btn_hwnd, NAVBAR_BackspaceProc, 0, (DWORD_PTR)info);
x += cs->cy + 1; info->fwd_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, @@ -952,6 +964,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) SendMessageW(info->fwd_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); set_icon(info->icons, ILI_FORWARD, info->fwd_btn_hwnd); set_title_and_add_tooltip(info, info->fwd_btn_hwnd, IDS_FORWARD); + SetWindowSubclass(info->fwd_btn_hwnd, NAVBAR_BackspaceProc, 0, (DWORD_PTR)info);
x += cs->cy + 1; info->up_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, @@ -961,6 +974,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) SendMessageW(info->up_btn_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); set_icon(info->icons, ILI_UP, info->up_btn_hwnd); set_title_and_add_tooltip(info, info->up_btn_hwnd, IDS_UPFOLDER); + SetWindowSubclass(info->up_btn_hwnd, NAVBAR_BackspaceProc, 0, (DWORD_PTR)info);
x += cs->cy + 1; info->background_hwnd = CreateWindowExW(0, WC_STATICW, NULL, @@ -1000,6 +1014,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) set_icon(info->icons, ILI_REFRESH, info->refresh_goto_btn_hwnd); set_title_and_add_tooltip(info, info->refresh_goto_btn_hwnd, IDS_REFRESH); SetWindowSubclass(info->refresh_goto_btn_hwnd, NAVBAR_REFRESHGOTO_SubclassProc, 0, (DWORD_PTR)info); + SetWindowSubclass(info->refresh_goto_btn_hwnd, NAVBAR_BackspaceProc, 1, (DWORD_PTR)info);
SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comctl32/commctrl.c | 49 +++++++-- dlls/comctl32/tests/combo.c | 196 ++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 8 deletions(-)
diff --git a/dlls/comctl32/commctrl.c b/dlls/comctl32/commctrl.c index c5910b40869..3ae71fc3f14 100644 --- a/dlls/comctl32/commctrl.c +++ b/dlls/comctl32/commctrl.c @@ -1621,6 +1621,7 @@ static inline BOOL IsDelimiter(WCHAR c) case '\': case '.': case ' ': + case '\t': return TRUE; } return FALSE; @@ -1628,15 +1629,47 @@ static inline BOOL IsDelimiter(WCHAR c)
static int CALLBACK PathWordBreakProc(LPCWSTR lpch, int ichCurrent, int cch, int code) { - if (code == WB_ISDELIMITER) - return IsDelimiter(lpch[ichCurrent]); - else - { - int dir = (code == WB_LEFT) ? -1 : 1; - for(; 0 <= ichCurrent && ichCurrent < cch; ichCurrent += dir) - if (IsDelimiter(lpch[ichCurrent])) return ichCurrent; + INT ret = 0; + + switch (code) { + case WB_LEFT: + /* skip trailing delimiters */ + while (ichCurrent && IsDelimiter(lpch[ichCurrent - 1])) + ichCurrent--; + /* skip word */ + while (ichCurrent && !IsDelimiter(lpch[ichCurrent - 1])) + ichCurrent--; + /* special case: if there's a single leading delimiter, skip it */ + if (ichCurrent == 1 && IsDelimiter(lpch[ichCurrent - 1])) + ichCurrent--; + + if (ichCurrent && ichCurrent > cch) + ichCurrent--; + + ret = ichCurrent; + break; + case WB_RIGHT: + if (cch < 0) + cch = lstrlenW(lpch); + + /* skip word */ + while (ichCurrent < cch && !IsDelimiter(lpch[ichCurrent])) + ichCurrent++; + /* move caret after the delimiters */ + while (ichCurrent < cch && IsDelimiter(lpch[ichCurrent])) + ichCurrent++; + + ret = ichCurrent; + break; + case WB_ISDELIMITER: + ret = IsDelimiter(lpch[ichCurrent]); + break; + default: + ERR("unexpected action code\n"); + break; } - return ichCurrent; + + return ret; }
/*********************************************************************** diff --git a/dlls/comctl32/tests/combo.c b/dlls/comctl32/tests/combo.c index 3eceeb55bd5..603d2740641 100644 --- a/dlls/comctl32/tests/combo.c +++ b/dlls/comctl32/tests/combo.c @@ -50,6 +50,7 @@ static const char ComboExTestClass[] = "ComboExTestClass";
static HBRUSH brush_red;
+static LRESULT (WINAPI *pSetPathWordBreakProc)(HWND, BOOL); static BOOL (WINAPI *pSetWindowSubclass)(HWND, SUBCLASSPROC, UINT_PTR, DWORD_PTR);
#define MAX_CHARS 100 @@ -545,6 +546,7 @@ static void init_functions(void)
#define X(f) p##f = (void*)GetProcAddress(hComCtl32, #f); #define X2(f, ord) p##f = (void*)GetProcAddress(hComCtl32, (const char *)ord); + X2(SetPathWordBreakProc, 384); X2(SetWindowSubclass, 410); #undef X #undef X2 @@ -1514,6 +1516,199 @@ static void test_comboex_CBEN_GETDISPINFO(void) DestroyWindow(combo); }
+static BOOL hold_key(int vk) +{ + BYTE key_state[256]; + BOOL result; + + result = GetKeyboardState(key_state); + ok(result, "GetKeyboardState failed.\n"); + if (!result) return FALSE; + key_state[vk] |= 0x80; + result = SetKeyboardState(key_state); + ok(result, "SetKeyboardState failed.\n"); + return result != 0; +} + +static BOOL release_key(int vk) +{ + BYTE key_state[256]; + BOOL result; + + result = GetKeyboardState(key_state); + ok(result, "GetKeyboardState failed.\n"); + if (!result) return FALSE; + key_state[vk] &= ~0x80; + result = SetKeyboardState(key_state); + ok(result, "SetKeyboardState failed.\n"); + return result != 0; +} + +static LRESULT send_ctrl_key(HWND hwnd, UINT key) +{ + LRESULT result; + hold_key(VK_CONTROL); + result = SendMessageA(hwnd, WM_KEYDOWN, key, 1); + release_key(VK_CONTROL); + return result; +} + +static void move_word(INT line, HWND edit, UINT direction, INT expected_caret_position) +{ + INT selection_start = -1, selection_end = -1; + + send_ctrl_key(edit, direction); + SendMessageA(edit, EM_GETSEL, (WPARAM)&selection_start, (WPARAM)&selection_end); + ok_(__FILE__, line)(selection_start == expected_caret_position, "Unexpected caret position %i.\n", selection_start); +} + +static void test_comboex_CBES_EX_PATHWORDBREAKPROC(void) +{ + HWND combo, edit; + INT start, end; + WCHAR some_pathW[] = L"some/path"; + WCHAR delimitersW[] = L"a .\/\t"; + EDITWORDBREAKPROCW word_break_proc, proc2; + int ret; + + combo = createComboEx(WS_VISIBLE | WS_CHILD | CBS_DROPDOWN); + SendMessageA(combo, CBEM_SETEXTENDEDSTYLE, 0, CBES_EX_PATHWORDBREAKPROC); + edit = (HWND)SendMessageA(combo, CBEM_GETEDITCONTROL, 0, 0); + + /* test that proc matches with what SetPathWordBreakProc sets */ + word_break_proc = (EDITWORDBREAKPROCW)SendMessageA(edit, EM_GETWORDBREAKPROC, 0, 0); + ok(word_break_proc != NULL, "proc is NULL."); + + pSetPathWordBreakProc(edit, FALSE); + proc2 = (EDITWORDBREAKPROCW)SendMessageA(edit, EM_GETWORDBREAKPROC, 0, 0); + ok(proc2 == NULL, "proc is not NULL %p.", proc2); + + pSetPathWordBreakProc(edit, TRUE); + proc2 = (EDITWORDBREAKPROCW)SendMessageA(edit, EM_GETWORDBREAKPROC, 0, 0); + ok(word_break_proc == proc2, "proc did not match %p %p.", word_break_proc, proc2); + + /* multiple delimiters */ + /* |C:|aa |bb|cc.|dd/|ee↹|ff|gg/|hh/ ↹.| */ + SetWindowTextA(edit, "C:\aa bb\cc.dd/ee\tff\gg/hh\/ \t."); + /* from left to right */ + SendMessageA(edit, EM_GETSEL, (WPARAM)&start, (WPARAM)&end); + ok(start == 0, "Unexpected caret position %i.\n", start); /* |C:\aa bb\cc.dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_RIGHT, 3); /* C:|aa bb\cc.dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_RIGHT, 6); /* C:\aa |bb\cc.dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_RIGHT, 9); /* C:\aa bb|cc.dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_RIGHT, 12); /* C:\aa bb\cc.|dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_RIGHT, 15); /* C:\aa bb\cc.dd/|ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_RIGHT, 18); /* C:\aa bb\cc.dd/ee↹|ff\gg/hh/ .↹ */ + move_word(__LINE__, edit, VK_RIGHT, 21); /* C:\aa bb\cc.dd/ee↹ff|gg/hh/ .↹ */ + move_word(__LINE__, edit, VK_RIGHT, 24); /* C:\aa bb\cc.dd/ee↹ff\gg/|hh/ .↹ */ + move_word(__LINE__, edit, VK_RIGHT, 31); /* C:\aa bb\cc.dd/ee↹ff\gg/hh/ ↹.| */ + /* from right to left */ + SendMessageA(edit, EM_SETSEL, GetWindowTextLengthA(edit), GetWindowTextLengthA(edit)); + SendMessageA(edit, EM_GETSEL, (WPARAM)&start, (WPARAM)&end); + ok(start == 31, "Unexpected caret position %i.\n", start); /* C:\aa bb\cc.dd/ee↹ff\gg/hh/ ↹.| */ + move_word(__LINE__, edit, VK_LEFT, 24); /* C:\aa bb\cc.dd/ee↹ff\gg/|hh/ .↹ */ + move_word(__LINE__, edit, VK_LEFT, 21); /* C:\aa bb\cc.dd/ee↹ff|gg/hh/ .↹ */ + move_word(__LINE__, edit, VK_LEFT, 18); /* C:\aa bb\cc.dd/ee↹|ff\gg/hh/ .↹ */ + move_word(__LINE__, edit, VK_LEFT, 15); /* C:\aa bb\cc.dd/|ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_LEFT, 12); /* C:\aa bb\cc.|dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_LEFT, 9); /* C:\aa bb|cc.dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_LEFT, 6); /* C:\aa |bb\cc.dd/ee↹ff\gg/hh/ ↹. */ + move_word(__LINE__, edit, VK_LEFT, 3); /* C:|aa bb\cc.dd/ee↹ff\gg/hh/ ↹. */ + + /* no trailing delimiter and leading delimiter */ + SetWindowTextA(edit, "\\some\path"); + SendMessageA(edit, EM_SETSEL, GetWindowTextLengthA(edit), GetWindowTextLengthA(edit)); + SendMessageA(edit, EM_GETSEL, (WPARAM)&start, (WPARAM)&end); + ok(start == 11, "Unexpected caret position %i.\n", start); /* \some\path| */ + move_word(__LINE__, edit, VK_LEFT, 7); /* \some|path */ + move_word(__LINE__, edit, VK_LEFT, 2); /* \|some\path*/ + move_word(__LINE__, edit, VK_LEFT, 0); /* |\some\path*/ + + /* single leading delimiter */ + SetWindowTextA(edit, "/some/path"); + SendMessageA(edit, EM_SETSEL, GetWindowTextLengthA(edit), GetWindowTextLengthA(edit)); + SendMessageA(edit, EM_GETSEL, (WPARAM)&start, (WPARAM)&end); + ok(start == 10, "Unexpected caret position %i.\n", start); /* /some/path|*/ + move_word(__LINE__, edit, VK_LEFT, 6); /* /some/|path*/ + move_word(__LINE__, edit, VK_LEFT, 0); /* |/some/path*/ + + /* WB_LEFT */ + ret = word_break_proc(NULL, 0, 0, WB_LEFT); + ok(ret == 0, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, 3, -1, WB_LEFT); + ok(ret == 0, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, 3, 0, WB_LEFT); + ok(ret == 0, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, 6, -1, WB_LEFT); + ok(ret == 4, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, 6, 0, WB_LEFT); + ok(ret == 4, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, 6, 4, WB_LEFT); + ok(ret == 4, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, 6, 5, WB_LEFT); + ok(ret == 5, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW, lstrlenW(some_pathW), lstrlenW(some_pathW), WB_LEFT); + ok(ret == 5, "Unexpected return value %i.\n", ret); + if (0) + { + word_break_proc(NULL, 10, 10, WB_LEFT); /* crashes */ + word_break_proc(some_pathW, 15, 5, WB_LEFT); /* reads out of bounds */ + word_break_proc(some_pathW, -10, 0, WB_LEFT); /* hangs with negative index */ + } + + /* WB_RIGHT */ + ret = word_break_proc(some_pathW, lstrlenW(some_pathW), lstrlenW(some_pathW), WB_RIGHT); + ok(ret == lstrlenW(some_pathW), "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 5, 0, 0, WB_RIGHT); + ok(ret == 0, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 5, 0, 1, WB_RIGHT); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 5, 1, 1, WB_RIGHT); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 5, 2, 2, WB_RIGHT); + ok(ret == 2, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 5, 0, 3, WB_RIGHT); + ok(ret == 3, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 1, 0, -1, WB_RIGHT); + ok(ret == 4, "Unexpected return value %i.\n", ret); + ret = word_break_proc(some_pathW + 6, -6, 0, WB_RIGHT); + ok(ret == -1, "Unexpected return value %i.\n", ret); + if (0) + { + word_break_proc(NULL, 0, 0, WB_RIGHT); /* crashes */ + word_break_proc(NULL, 10, 10, WB_RIGHT); /* crashes */ + word_break_proc(some_pathW + 5, 0, -1, WB_RIGHT); /* reads out of bounds */ + word_break_proc(some_pathW + 5, 1, 0, WB_RIGHT); /* reads out of bounds */ + word_break_proc(some_pathW, 15, 5, WB_RIGHT); /* reads out of bounds */ + word_break_proc(some_pathW, lstrlenW(some_pathW), 0, WB_RIGHT); /* reads out of bounds */ + } + + /* WB_ISDELIMITER */ + ret = word_break_proc(delimitersW, 0, lstrlenW(delimitersW), WB_ISDELIMITER); + ok(ret == 0, "Unexpected return value %i.\n", ret); + ret = word_break_proc(delimitersW, 1, lstrlenW(delimitersW), WB_ISDELIMITER); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(delimitersW, 2, lstrlenW(delimitersW), WB_ISDELIMITER); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(delimitersW, 3, lstrlenW(delimitersW), WB_ISDELIMITER); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(delimitersW, 4, lstrlenW(delimitersW), WB_ISDELIMITER); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(delimitersW, 5, lstrlenW(delimitersW), WB_ISDELIMITER); + ok(ret == 1, "Unexpected return value %i.\n", ret); + ret = word_break_proc(delimitersW, 3, 0, WB_ISDELIMITER); + ok(ret == 1, "Unexpected return value %i.\n", ret); + if (0) + { + word_break_proc(NULL, 0, 0, WB_ISDELIMITER); /* crashes */ + word_break_proc(NULL, 10, 10, WB_ISDELIMITER); /* crashes */ + } + + /* invalid action code */ + ret = word_break_proc(NULL, 5, 5, 0xFFFF); + ok(ret == 0, "Unexpected return value %i.\n", ret); +} + START_TEST(combo) { ULONG_PTR ctx_cookie; @@ -1534,6 +1729,7 @@ START_TEST(combo) test_comboex_subclass(); test_comboex_get_set_item(); test_comboex_CBEN_GETDISPINFO(); + test_comboex_CBES_EX_PATHWORDBREAKPROC();
if (!load_v6_module(&ctx_cookie, &hCtx)) {
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/navbar.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 3006c3c691e..2a2aec022a7 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -88,6 +88,8 @@ struct crumb { INT x; };
+LRESULT WINAPI SetPathWordBreakProc(HWND hwnd, BOOL bSet); + static void set_icon(HIMAGELIST icons, INT icon_id, HWND window) { HICON icon; @@ -1005,6 +1007,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) 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); + SetPathWordBreakProc(info->pathedit_hwnd, TRUE);
info->refresh_goto_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, NULL, WS_CHILD | WS_VISIBLE | BS_ICON | BS_BITMAP,
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/navbar.c | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+)
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 2a2aec022a7..7f763327636 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -545,6 +545,65 @@ static LRESULT NAVBAR_PATHEDIT_NCHitTest(HWND hwnd, UINT msg, WPARAM wparam, LPA return DefSubclassProc(hwnd, msg, wparam, lparam); }
+static LRESULT NAVBAR_PATHEDIT_KeyDown(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + WORD key = LOWORD(wparam); + + switch (key) { + case VK_BACK: + { + BOOL control = GetKeyState(VK_CONTROL) & 0x8000; + + /* delete word */ + if (control) + { + WCHAR *text = NULL, *ptr = NULL, *end = NULL; + INT text_len = 0; + UINT caret_pos = 0, selection_end = 0; + EDITWORDBREAKPROCW word_break_proc; + + SendMessageW(info->pathedit_hwnd, EM_GETSEL, (WPARAM)&caret_pos, (WPARAM)&selection_end); + if (caret_pos != selection_end) + break; + + text_len = GetWindowTextLengthW(info->pathedit_hwnd); + if (!text_len) + break; + + text = HeapAlloc(GetProcessHeap(), 0, (text_len + 1) * sizeof(WCHAR)); + if (!text) + break; + + if (!GetWindowTextW(info->pathedit_hwnd, text, text_len + 1)) + goto cleanup; + + ptr = text + caret_pos; + end = StrDupW(ptr); + if (!end) + goto cleanup; + + word_break_proc = (EDITWORDBREAKPROCW)SendMessageW(info->pathedit_hwnd, EM_GETWORDBREAKPROC, 0, 0); + caret_pos = word_break_proc(text, caret_pos, text_len, WB_LEFT); + ptr = text + caret_pos; + *ptr = '\0'; + + lstrcatW(ptr, end); + SetWindowTextW(info->pathedit_hwnd, text); + SendMessageW(info->pathedit_hwnd, EM_SETSEL, caret_pos, caret_pos); + + cleanup: + HeapFree(GetProcessHeap(), 0, text); + if (end) + LocalFree(end); + } + + break; + } + } + + 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; @@ -556,6 +615,7 @@ static LRESULT CALLBACK NAVBAR_PATHEDIT_SubclassProc(HWND hwnd, UINT msg, WPARAM 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); + case WM_KEYDOWN: return NAVBAR_PATHEDIT_KeyDown(hwnd, info, msg, wparam, lparam); }
return DefSubclassProc(hwnd, msg, wparam, lparam);
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=128908
Your paranoid android.
=== w7u_adm (32 bit report) ===
comctl32: combo.c:1596: Test failed: Unexpected caret position 1. combo.c:1597: Test failed: Unexpected caret position 3. combo.c:1598: Test failed: Unexpected caret position 6. combo.c:1599: Test failed: Unexpected caret position 9. combo.c:1600: Test failed: Unexpected caret position 12. combo.c:1601: Test failed: Unexpected caret position 15. combo.c:1602: Test failed: Unexpected caret position 18. combo.c:1603: Test failed: Unexpected caret position 21. combo.c:1604: Test failed: Unexpected caret position 24.
I think this is getting too big for a single MR.
v13: - keep focus on explorer browser to be able to `Backspace` multiple times - back/forward/up/refresh buttons don't actually take focus on Windows, we do, that means when user focuses address edit, clicking down (but not up) on the back/forward/up/refresh button still keeps address edit focused, workaround this by sending Go Up command on Backspace when these buttons are focused - use path word break in address edit - allows to use `Ctrl` + `Left Arrow` and `Ctrl` + `Right Arrow` to jump between path components - depends on !1977 - add `Ctrl` + `Backspace` delete word in address edit - enable visual styles before showing dialog - some applications don't request comctl32 version 6, resulting in half-broken navigation bar, e.g. [qapitrace](https://github.com/apitrace/apitrace) - depends on !2068
On Mon Jan 30 19:29:28 2023 +0000, Esme Povirk wrote:
I think this is getting too big for a single MR.
Yeah, it kinda is, sorry. Should I try splitting it further up and sending parts of it separately, or just stop with the feature creep here and polish?
On Mon Jan 30 19:29:28 2023 +0000, Vladislav Timonin wrote:
Yeah, it kinda is, sorry. Should I try splitting it further up and sending parts of it separately, or just stop with the feature creep here and polish?
I would say split it up and send only one MR at a time. You can still work ahead, but you may have to spend more time on rebases as a result.
I'd end the MR with the "address edit" commit, personally.
Gabriel Ivăncescu (@insn) commented about dlls/comdlg32/navbar.c:
break;
if (!GetWindowTextW(info->pathedit_hwnd, text, text_len + 1))
goto cleanup;
ptr = text + caret_pos;
end = StrDupW(ptr);
if (!end)
goto cleanup;
word_break_proc = (EDITWORDBREAKPROCW)SendMessageW(info->pathedit_hwnd, EM_GETWORDBREAKPROC, 0, 0);
caret_pos = word_break_proc(text, caret_pos, text_len, WB_LEFT);
ptr = text + caret_pos;
*ptr = '\0';
lstrcatW(ptr, end);
Why not just `wcscpy(ptr, end)`?
On Mon Jan 30 20:10:32 2023 +0000, Gabriel Ivăncescu wrote:
Why not just `wcscpy(ptr, end)`?
Welp, thought I was having issues with destination overwriting the source, but looks like I was misled as it seem to work just fine.
On Mon Jan 30 21:04:23 2023 +0000, Vladislav Timonin wrote:
Welp, thought I was having issues with destination overwriting the source, but looks like I was misled as it seem to work just fine.
strcat (and its wide variants) is the equivalent of `strcpy(dst + strlen(dst), src)`. You write a NUL first and then use strcat on it, so `strlen(ptr)` is always 0 (since it points to NUL char). Also, you can get rid of the `*ptr = '\0'`, since strcpy will just copy the string directly in its place (where ptr is), that's what I meant.
On Thu Jan 5 20:24:14 2023 +0000, Gabriel Ivăncescu wrote:
Honestly I don't know, but I think it's fine to embed just the refresh icon since hhctrl.ocx does that, unless someone with more knowledge about this area knows if Refresh is supposed to have a standard location too? Seems a bit weird, but it's Microsoft… :D But at least the other 3 can be picked from comctl32.
The [Microsoft Docs for "Toolbar Standard Button Icons"](https://learn.microsoft.com/en-us/windows/win32/controls/toolbar-standard-bu...) don't mention/show any kind of refresh icon, so it's probably missing for some reason. So I believe the only "sane" way is to include a new bitmap for the refresh icon at least, instead of relying on ieframe/hhctrl internals.
This merge request was closed by Vladislav Timonin.