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)
-- v2: 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.
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/Makefile.in | 2 + dlls/comdlg32/comdlg32.rc | 4 + dlls/comdlg32/itemdlg.c | 84 ++--- dlls/comdlg32/navbar.bmp | Bin 0 -> 9354 bytes dlls/comdlg32/navbar.c | 144 ++++++++ dlls/comdlg32/navbar.h | 23 ++ dlls/comdlg32/navbar.svg | 676 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 891 insertions(+), 42 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 a5d09faa563..4710f1955b7 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
@@ -602,3 +603,6 @@ NETWORK ICON network.ico
/* @makedep: fontpics.bmp */ 38 BITMAP fontpics.bmp + +/* @makedep: navbar.bmp */ +IDB_NAVBAR BITMAP navbar.bmp diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 95778c2f358..89b67f24e6f 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -37,11 +37,11 @@ #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
#include <initguid.h> +#include "navbar.h" + /* This seems to be another version of IID_IFileDialogCustomize. If * there is any difference I have yet to find it. */ DEFINE_GUID(IID_IFileDialogCustomizeAlt, 0x8016B7B3, 0x3D49, 0x4504, 0xA0,0xAA, 0x2A,0x37,0x49,0x4E,0x60,0x6F); @@ -1567,6 +1567,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; }
@@ -1638,7 +1645,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; @@ -1765,11 +1772,24 @@ static void update_layout(FileDialogImpl *This) filename_rc.bottom = filename_rc.top + filename_height; }
- hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAV_TOOLBAR); - if(hwnd) + hwnd = GetDlgItem(This->dlg_hwnd, IDC_NAVBAR); + if (hwnd) { - GetWindowRect(hwnd, &toolbar_rc); - MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&toolbar_rc, 2); + INT w, h; + + GetWindowRect(hwnd, &navbar_rc); + + navbar_rc.left = dialog_rc.left; + navbar_rc.right = dialog_rc.right; + + MapWindowPoints(NULL, This->dlg_hwnd, (POINT*)&navbar_rc, 2); + + w = navbar_rc.right - navbar_rc.left; + h = navbar_rc.bottom - navbar_rc.top; + navbar_rc.top = hspacing; + navbar_rc.bottom = navbar_rc.top + h; + navbar_rc.left = hspacing; + navbar_rc.right = w - hspacing; }
/* The custom controls */ @@ -1781,7 +1801,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;
@@ -1790,7 +1810,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); @@ -1827,6 +1847,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 @@ -1893,35 +1918,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(30, This->dpi_y, USER_DEFAULT_SCREEN_DPI), + hwnd, (HMENU)IDC_NAVBAR, NULL, NULL); }
static void update_control_text(FileDialogImpl *This) @@ -2062,8 +2062,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);
@@ -2216,8 +2216,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"); } @@ -2235,6 +2233,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 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 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..c8fd4c4ef9f --- /dev/null +++ b/dlls/comdlg32/navbar.c @@ -0,0 +1,144 @@ +/* + * 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 back_btn_hwnd; + HWND fwd_btn_hwnd; +} NAVBAR_INFO; + +static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) +{ + CREATESTRUCTW *cs = (CREATESTRUCTW*)lparam; + INT x; + HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); + HICON icon; + + 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); + + x = 0; + info->back_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, L"Back", + 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); + icon = ImageList_GetIcon(info->icons, ILI_BACK, ILD_NORMAL); + SendMessageW(info->back_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); + + x += cs->cy + 1; + info->fwd_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, L"Forward", + 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); + icon = ImageList_GetIcon(info->icons, ILI_FORWARD, ILD_NORMAL); + SendMessageW(info->fwd_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); + + 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); + DestroyWindow(info->back_btn_hwnd); + DestroyWindow(info->fwd_btn_hwnd); + + 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, info, msg, wparam, lparam); + case WM_NCDESTROY: 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..b881fec24a4 --- /dev/null +++ b/dlls/comdlg32/navbar.h @@ -0,0 +1,23 @@ +#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 + +/* bitmap ids */ +#define IDB_NAVBAR 3000 +/* icon indicies 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 | 16 ++++++++++++++++ dlls/comdlg32/navbar.h | 1 + 3 files changed, 25 insertions(+)
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 89b67f24e6f..703f525b7ca 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -2160,6 +2160,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) @@ -2235,6 +2242,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 c8fd4c4ef9f..a2102673894 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -27,6 +27,7 @@ /* private control ids */ #define IDC_NAVBACK 201 #define IDC_NAVFORWARD 202 +#define IDC_NAVUP 203
typedef struct { HWND parent_hwnd; @@ -36,6 +37,7 @@ typedef struct {
HWND back_btn_hwnd; HWND fwd_btn_hwnd; + HWND up_btn_hwnd; } NAVBAR_INFO;
static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) @@ -72,6 +74,16 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar SendMessageW(info->fwd_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); DestroyIcon(icon);
+ x += cs->cy + 1; + info->up_btn_hwnd = CreateWindowExW(0, WC_BUTTONW, L"Up", + 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); + icon = ImageList_GetIcon(info->icons, ILI_UP, ILD_NORMAL); + SendMessageW(info->up_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -83,6 +95,7 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa ImageList_Destroy(info->icons); DestroyWindow(info->back_btn_hwnd); DestroyWindow(info->fwd_btn_hwnd); + DestroyWindow(info->up_btn_hwnd);
HeapFree(GetProcessHeap(), 0, info);
@@ -99,6 +112,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 b881fec24a4..a2e6cb8c854 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -8,6 +8,7 @@ /* navbar notifications */ #define NBN_NAVBACK WM_USER + 102 #define NBN_NAVFORWARD WM_USER + 103 +#define NBN_NAVUP WM_USER + 104
/* bitmap ids */ #define IDB_NAVBAR 3000
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 | 370 ++++++++++++++++++++++++++++++++++++++++ dlls/comdlg32/navbar.h | 4 + 3 files changed, 393 insertions(+)
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 703f525b7ca..453c83dcf1a 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -2167,6 +2167,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) @@ -2243,6 +2252,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; @@ -3362,6 +3372,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) @@ -3373,6 +3384,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) + SendMessageW(hwnd, NBM_SETPIDL, 0, (LPARAM)pidlFolder); + else + ERR("Failed to update navbar.\n"); + }
events_OnFolderChange(This);
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index a2102673894..55b338475c8 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -23,33 +23,259 @@ #include "navbar.h" #include "commdlg.h" #include "cdlg.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 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 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; + HBRUSH old_brush; + HBRUSH bg_brush = GetSysColorBrush(COLOR_BTNFACE); + HBRUSH frame_brush = GetSysColorBrush(COLOR_3DSHADOW); + + GetClientRect(hwnd, &rc); + + /* clear background to avoid border trailing when resizing */ + FillRect(hdc, &rc, bg_brush); + + /* draw the 1px frame */ + FrameRect(hdc, &rc, frame_brush); + + /* erase the left border, retaining the top and bottom pixels */ + old_brush = SelectObject(hdc, bg_brush); + PatBlt(hdc, rc.left, rc.top + 1, 1, rc.bottom - rc.top - 2, PATCOPY); + + SelectObject(hdc, old_brush); + + 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(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_head(&info->crumbs); + if (&crumb1->entry == &info->crumbs) + { + ERR("no crumbs to layout\n"); + return; + } + + crumb2 = (struct crumb *)list_head(&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 second crumb take leftover space */ + crumb2_size = max_crumbs_w - crumb1->full_w; + if (crumb2_size < crumb2->full_w) + crumb2->current_w = crumb2_size; + } + + 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(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(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, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) { CREATESTRUCTW *cs = (CREATESTRUCTW*)lparam; + HDC hdc; INT x; HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); HICON icon;
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); @@ -84,6 +310,13 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar SendMessageW(info->up_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); DestroyIcon(icon);
+ 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); @@ -91,17 +324,45 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar
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); + + DestroyWindow(crumb1->hwnd); + + list_remove(&crumb1->entry); + HeapFree(GetProcessHeap(), 0, crumb1); + } + SetWindowLongPtrW(hwnd, 0, 0); ImageList_Destroy(info->icons); DestroyWindow(info->back_btn_hwnd); DestroyWindow(info->fwd_btn_hwnd); DestroyWindow(info->up_btn_hwnd); + DestroyWindow(info->background_hwnd);
HeapFree(GetProcessHeap(), 0, info);
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)) @@ -115,8 +376,115 @@ 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; + BOOL matching = TRUE; + 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; + + list_add_tail(&new_crumbs, &crumb1->entry); + info->crumbs_total_n += 1; + } + while (ILRemoveLastID(pidl)); + ILFree(pidl); + + /* reuse existing crumbs */ + crumb1 = LIST_ENTRY((&info->crumbs)->prev, struct crumb, entry); + crumb2 = LIST_ENTRY((&new_crumbs)->prev, struct crumb, entry); + for (;;) + { + if (&crumb1->entry != &info->crumbs) + { + if (&crumb2->entry != &new_crumbs + && matching + && (matching = ILIsEqual(crumb2->pidl, crumb1->pidl))) + { + DestroyWindow(crumb2->hwnd); + ILFree(crumb2->pidl); + crumb2->pidl = crumb1->pidl; + crumb2->hwnd = crumb1->hwnd; + } + else + { + DestroyWindow(crumb1->hwnd); + ILFree(crumb1->pidl); + } + } + else + break; + + crumb1 = LIST_ENTRY(crumb1->entry.prev, struct crumb, entry); + crumb2 = LIST_ENTRY(crumb2->entry.prev, 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); }
@@ -131,7 +499,9 @@ static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LP { case WM_NCCREATE: return NAVBAR_Create(hwnd, info, msg, wparam, lparam); case WM_NCDESTROY: 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 a2e6cb8c854..5360f9fc129 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -5,10 +5,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
/* bitmap ids */ #define IDB_NAVBAR 3000
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/navbar.c | 204 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 3 deletions(-)
diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index 55b338475c8..bdb0902e7e3 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -24,6 +24,7 @@ #include "commdlg.h" #include "cdlg.h" #include "shlwapi.h" +#include "commoncontrols.h"
#include "wine/debug.h" #include "wine/list.h" @@ -55,6 +56,9 @@ typedef struct { struct list crumbs; INT crumbs_total_n; INT crumbs_visible_n; + + HWND overflow_hwnd; + HMENU overflow_menu; } NAVBAR_INFO;
struct crumb { @@ -67,6 +71,80 @@ struct crumb { INT x; };
+static void NAVBAR_OVERFLOW_Insert(NAVBAR_INFO *info, ITEMIDLIST* pidl, WCHAR *display_name) +{ + MENUITEMINFOW menu_item = {0}; + + 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}; + + menu_item.cbSize = sizeof(MENUITEMINFOW); + menu_item.fMask = MIIM_DATA; + + for (i = 0; i < menu_item_count; i++) + { + /* deleting an item shifts further indices, so don't use `i` */ + GetMenuItemInfoW(info->overflow_menu, 0, TRUE, &menu_item); + ILFree((ITEMIDLIST *)menu_item.dwItemData); + DeleteMenu(info->overflow_menu, 0, 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); + } + + 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 - (icon_width / 2), 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) @@ -103,7 +181,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, @@ -119,10 +197,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; @@ -228,6 +306,36 @@ static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) struct crumb *crumb; INT can_fit_n = info->crumbs_visible_n; UINT background_flags = 0; + INT menu_item_count = GetMenuItemCount(info->overflow_menu); + MENUITEMINFOW first_crumb_menu_item = {0}; + BUTTON_SPLITINFO split_info = {0}; + + split_info.mask = BCSIF_STYLE; + + if (menu_item_count > 0) + { + /* remove the first crumb from overflow menu and hold onto it */ + first_crumb_menu_item.cbSize = sizeof(MENUITEMINFOW); + first_crumb_menu_item.fMask = MIIM_FTYPE | MIIM_DATA | MIIM_STRING | MIIM_BITMAP; /* match with NAVBAR_OVERFLOW_Insert */ + GetMenuItemInfoW(info->overflow_menu, menu_item_count - 1, TRUE, &first_crumb_menu_item); + + first_crumb_menu_item.cch += 1; + first_crumb_menu_item.dwTypeData = HeapAlloc(GetProcessHeap(), 0, first_crumb_menu_item.cch); + GetMenuItemInfoW(info->overflow_menu, menu_item_count - 1, TRUE, &first_crumb_menu_item); + + RemoveMenu(info->overflow_menu, menu_item_count - 1, MF_BYPOSITION); + + /* 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); + } + + NAVBAR_OVERFLOW_Clear(info);
LIST_FOR_EACH_ENTRY(crumb, &info->crumbs, struct crumb, entry) { @@ -236,7 +344,11 @@ static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) if (can_fit_n > 0) flags |= SWP_SHOWWINDOW | SWP_NOCOPYBITS; else + { flags |= SWP_HIDEWINDOW; + if (menu_item_count > 0) + NAVBAR_OVERFLOW_Insert(info, ILClone(crumb->pidl), crumb->display_name); + }
hdwp = DeferWindowPos(hdwp, crumb->hwnd, HWND_TOP, crumb->x, 0, @@ -246,6 +358,10 @@ static HDWP NAVBAR_DoLayout(NAVBAR_INFO *info, HDWP hdwp) can_fit_n -= 1; }
+ /* move the first crumb back */ + if (menu_item_count > 0) + InsertMenuItemW(info->overflow_menu, -1, TRUE, &first_crumb_menu_item); + if (info->background_w > 0) background_flags |= SWP_SHOWWINDOW | SWP_NOCOPYBITS; else @@ -266,6 +382,8 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar INT x; HGDIOBJ gui_font = GetStockObject(DEFAULT_GUI_FONT); HICON icon; + SIZE button_size; + MENUINFO menu_info = {0};
info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NAVBAR_INFO)); list_init(&info->crumbs); @@ -317,6 +435,21 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar 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_SPLITBUTTON, + x, 0, 0, 0, + hwnd, 0, COMDLG32_hInstance, NULL); + SendMessageW(info->overflow_hwnd, WM_SETFONT, (WPARAM)gui_font, FALSE); + SendMessageW(info->overflow_hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&button_size); + SetWindowPos(info->overflow_hwnd, NULL, 0, 0, + button_size.cx + MulDiv(8, info->dpi_x, USER_DEFAULT_SCREEN_DPI), cs->cy, SWP_NOMOVE); + + info->overflow_menu = CreatePopupMenu(); + menu_info.cbSize = sizeof(MENUINFO); + menu_info.fMask = MIM_STYLE; + menu_info.dwStyle = MNS_NOTIFYBYPOS; + SetMenuInfo(info->overflow_menu, &menu_info); + SetWindowLongPtrW(hwnd, 0, (DWORD_PTR)info);
return DefWindowProcW(hwnd, msg, wparam, lparam); @@ -344,6 +477,9 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa DestroyWindow(info->fwd_btn_hwnd); DestroyWindow(info->up_btn_hwnd); DestroyWindow(info->background_hwnd); + DestroyWindow(info->overflow_hwnd); + NAVBAR_OVERFLOW_Clear(info); + DestroyMenu(info->overflow_menu);
HeapFree(GetProcessHeap(), 0, info);
@@ -388,12 +524,30 @@ 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}; + + 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; @@ -409,6 +563,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); @@ -439,6 +602,19 @@ static LRESULT NAVBAR_SetPIDL(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa while (ILRemoveLastID(pidl)); ILFree(pidl);
+ NAVBAR_OVERFLOW_Clear(info); + + /* move the first crumb to overflow menu, if there are at least 2 crumbs */ + crumb1 = LIST_ENTRY(list_tail(&new_crumbs), struct crumb, entry); + if (crumb1 && crumb1->entry.prev != &new_crumbs) + { + NAVBAR_OVERFLOW_Insert(info, crumb1->pidl, crumb1->display_name); + DestroyWindow(crumb1->hwnd); + + list_remove(&crumb1->entry); + HeapFree(GetProcessHeap(), 0, crumb1); + } + /* reuse existing crumbs */ crumb1 = LIST_ENTRY((&info->crumbs)->prev, struct crumb, entry); crumb2 = LIST_ENTRY((&new_crumbs)->prev, struct crumb, entry); @@ -488,6 +664,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); @@ -501,6 +695,10 @@ static LRESULT CALLBACK NAVBAR_WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LP case WM_NCDESTROY: 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/comdlg32.rc | 7 + dlls/comdlg32/navbar.c | 320 +++++++++++++++++++++++++++++++++++++- dlls/comdlg32/navbar.h | 5 + 3 files changed, 327 insertions(+), 5 deletions(-)
diff --git a/dlls/comdlg32/comdlg32.rc b/dlls/comdlg32/comdlg32.rc index 4710f1955b7..a1c71ece16b 100644 --- a/dlls/comdlg32/comdlg32.rc +++ b/dlls/comdlg32/comdlg32.rc @@ -149,6 +149,13 @@ STRINGTABLE IDS_FONT_SIZE_INPUT "Font size has to be a number." }
+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'." +} + /* * WARNING: DO NOT CHANGE THE SIZE OF THE STANDARD DIALOG TEMPLATES. */ diff --git a/dlls/comdlg32/navbar.c b/dlls/comdlg32/navbar.c index bdb0902e7e3..3ad5b805ce2 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -59,6 +59,8 @@ typedef struct {
HWND overflow_hwnd; HMENU overflow_menu; + + HWND pathedit_hwnd; } NAVBAR_INFO;
struct crumb { @@ -71,6 +73,266 @@ struct crumb { INT x; };
+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_head(&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) +{ + INT len = 0, size; + WCHAR *text = NULL, *expanded = NULL; + DWORD expanded_cch; + ITEMIDLIST *pidl = NULL; + HRESULT hr; + + len = GetWindowTextLengthW(info->pathedit_hwnd); + if (!len) + goto exit; + + size = (len + 1) * sizeof(WCHAR); + text = HeapAlloc(GetProcessHeap(), 0, size); + if (!text) + goto exit; + + if (!GetWindowTextW(info->pathedit_hwnd, text, size)) + goto cleanup; + + /* expand variables */ + expanded_cch = ExpandEnvironmentStringsW(text, NULL, 0); + if (!expanded_cch) + goto cleanup; + + expanded = HeapAlloc(GetProcessHeap(), 0, expanded_cch * sizeof(WCHAR)); + if (!expanded) + goto cleanup; + + expanded_cch = ExpandEnvironmentStringsW(text, expanded, expanded_cch); + if (!expanded_cch) + goto cleanup; + + /* check if we have a valid path */ + if (GetFileAttributesW(expanded) == 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->pathedit_hwnd, caption, NULL, MB_ICONERROR | MB_OK); + goto cleanup; + } + + hr = SHILCreateFromPath(expanded, &pidl, 0); + if (FAILED(hr)) + goto cleanup; + + SendMessageW(info->parent_hwnd, NBN_NAVPIDL, 0, (LPARAM)pidl); + ILFree(pidl); +cleanup: + if (text) + HeapFree(GetProcessHeap(), 0, text); + if (expanded) + HeapFree(GetProcessHeap(), 0, expanded); +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 the 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}; @@ -145,8 +407,25 @@ static LRESULT NAVBAR_OVERFLOW_DrawIcon(HWND hwnd, NAVBAR_INFO *info, UINT msg, return DefWindowProcW(hwnd, msg, wparam, lparam); }
+static LRESULT CALLBACK NAVBAR_OVERFLOW_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_LBUTTONUP: + case WM_RBUTTONUP: + NAVBAR_PATHEDIT_Edit(info); + break; + } + + 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; + switch (msg) { case WM_PAINT: @@ -174,6 +453,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); @@ -184,7 +466,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)) @@ -199,12 +481,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(crumb1, &info->crumbs, struct crumb, entry) { INT next_w = w + crumb1->full_w; @@ -289,6 +579,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; @@ -430,10 +725,11 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar
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_SPLITBUTTON, @@ -443,6 +739,7 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar SendMessageW(info->overflow_hwnd, BCM_GETIDEALSIZE, 0, (LPARAM)&button_size); SetWindowPos(info->overflow_hwnd, NULL, 0, 0, button_size.cx + MulDiv(8, info->dpi_x, USER_DEFAULT_SCREEN_DPI), cs->cy, SWP_NOMOVE); + SetWindowSubclass(info->overflow_hwnd, NAVBAR_OVERFLOW_SubclassProc, 0, (DWORD_PTR)info);
info->overflow_menu = CreatePopupMenu(); menu_info.cbSize = sizeof(MENUINFO); @@ -450,6 +747,14 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar menu_info.dwStyle = 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); @@ -480,6 +785,7 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa DestroyWindow(info->overflow_hwnd); NAVBAR_OVERFLOW_Clear(info); DestroyMenu(info->overflow_menu); + DestroyWindow(info->pathedit_hwnd);
HeapFree(GetProcessHeap(), 0, info);
@@ -492,9 +798,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; } @@ -592,6 +901,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 5360f9fc129..1438fd2b322 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -14,6 +14,11 @@ #define NBN_NAVUP WM_USER + 104 #define NBN_NAVPIDL WM_USER + 105
+/* strings */ +#define IDS_COPY_ADDRESS_AS_TEXT 2001 +#define IDS_EDIT_ADDRESS 2002 +#define IDS_WINE_CANT_FIND_ADDRESS 2004 + /* bitmap ids */ #define IDB_NAVBAR 3000 /* icon indicies in IDB_NAVBAR */
From: Vladislav Timonin timoninvlad@yandex.ru
--- dlls/comdlg32/itemdlg.c | 18 +++++++++++ dlls/comdlg32/navbar.c | 66 +++++++++++++++++++++++++++++++++++++---- dlls/comdlg32/navbar.h | 1 + 3 files changed, 79 insertions(+), 6 deletions(-)
diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index 453c83dcf1a..4c7d4fa0fdf 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -2176,6 +2176,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) @@ -2253,6 +2270,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 3ad5b805ce2..930c2ddf025 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -36,6 +36,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(commdlg); #define IDC_NAVFORWARD 202 #define IDC_NAVUP 203 #define IDC_NAVCRUMB 204 +#define IDC_NAVREFRESHGOTO 205
typedef struct { HWND parent_hwnd; @@ -48,6 +49,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; @@ -90,6 +92,7 @@ static void NAVBAR_PATHEDIT_Edit(NAVBAR_INFO *info) { struct crumb *crumb; HDWP hdwp; + HICON icon;
NAVBAR_PATHEDIT_SetCurrentPath(info);
@@ -102,6 +105,11 @@ 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 the Refresh button to Go to button */ + icon = ImageList_GetIcon(info->icons, ILI_FORWARD, ILD_NORMAL); + SendMessageW(info->refresh_goto_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); + SetFocus(info->pathedit_hwnd); }
@@ -114,6 +122,7 @@ static void NAVBAR_PATHEDIT_Copy(NAVBAR_INFO *info) static void NAVBAR_PATHEDIT_Dismiss(NAVBAR_INFO *info) { HDWP hdwp; + HICON icon;
if (!IsWindowVisible(info->pathedit_hwnd)) return; @@ -124,11 +133,19 @@ 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 the Go to button back to Refresh button */ + icon = ImageList_GetIcon(info->icons, ILI_REFRESH, ILD_NORMAL); + SendMessageW(info->refresh_goto_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); }
static LRESULT NAVBAR_PATHEDIT_KillFocus(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wparam, LPARAM lparam) { - NAVBAR_PATHEDIT_Dismiss(info); + /* if user clicked on the 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); } @@ -296,6 +313,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; @@ -463,10 +495,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)) @@ -488,10 +520,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 + refresh_goto_btn_w, info->dpi_x, USER_DEFAULT_SCREEN_DPI); if (max_crumbs_w < 0) return;
@@ -563,7 +600,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;
@@ -755,6 +792,16 @@ static LRESULT NAVBAR_Create(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpar 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); + icon = ImageList_GetIcon(info->icons, ILI_REFRESH, ILD_NORMAL); + SendMessageW(info->refresh_goto_btn_hwnd, BM_SETIMAGE, (WPARAM)IMAGE_ICON, (LPARAM)icon); + DestroyIcon(icon); + 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); @@ -781,6 +828,7 @@ static LRESULT NAVBAR_Destroy(HWND hwnd, NAVBAR_INFO *info, UINT msg, WPARAM wpa DestroyWindow(info->back_btn_hwnd); DestroyWindow(info->fwd_btn_hwnd); DestroyWindow(info->up_btn_hwnd); + DestroyWindow(info->refresh_goto_btn_hwnd); DestroyWindow(info->background_hwnd); DestroyWindow(info->overflow_hwnd); NAVBAR_OVERFLOW_Clear(info); @@ -828,6 +876,12 @@ 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_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 1438fd2b322..f4d0eb73c45 100644 --- a/dlls/comdlg32/navbar.h +++ b/dlls/comdlg32/navbar.h @@ -13,6 +13,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_COPY_ADDRESS_AS_TEXT 2001
v2, some minor changes: - added `.` to all commits titles - moved crumb padding in `SetPIDL` out of the loop - added trace message to `SetPIDL` - added license notice
Esme Povirk (@madewokherd) commented about dlls/comdlg32/navbar.c:
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);
According to MSDN, child windows are destroyed by the system before WM_NCDESTROY is sent, so I don't think this can work.
On Tue Dec 27 12:04:05 2022 +0000, Jinoh Kang wrote:
You can use `list_add_head` to _prepend_ items to a list. You can avoid reversing the list this way. Sorry for failing to mention this earlier.
Think reverse path order did turn out to be convenient in some places, but yeah, linked list works both ways just fine, going to change the order to left-to-right so that it's hopefully more straightforward to read.
Esme Povirk (@madewokherd) commented about dlls/comdlg32/navbar.c:
DestroyWindow(crumb2->hwnd);
ILFree(crumb2->pidl);
crumb2->pidl = crumb1->pidl;
crumb2->hwnd = crumb1->hwnd;
}
else
{
DestroyWindow(crumb1->hwnd);
ILFree(crumb1->pidl);
}
}
else
break;
crumb1 = LIST_ENTRY(crumb1->entry.prev, struct crumb, entry);
crumb2 = LIST_ENTRY(crumb2->entry.prev, struct crumb, entry);
Since the loop doesn't terminate when it runs out of items in new_crumbs, this will continue iterating items from the end again. I think it happens to do the right thing, but it is confusing.