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 1b249c5fc92..daa4babb391 100644 --- a/dlls/comdlg32/navbar.c +++ b/dlls/comdlg32/navbar.c @@ -4,6 +4,7 @@ #include "commdlg.h" #include "cdlg.h" #include "shlwapi.h" +#include "commoncontrols.h"
#include "wine/debug.h" #include "wine/list.h" @@ -35,6 +36,9 @@ typedef struct { struct list crumbs; INT crumbs_total_n; INT crumbs_visible_n; + + HWND overflow_hwnd; + HMENU overflow_menu; } NAVBAR_INFO;
struct crumb { @@ -47,6 +51,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) @@ -83,7 +161,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, @@ -99,10 +177,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; @@ -208,6 +286,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) { @@ -216,7 +324,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, @@ -226,6 +338,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 @@ -246,6 +362,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); @@ -297,6 +415,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); @@ -324,6 +457,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);
@@ -368,12 +504,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; struct list new_crumbs; struct crumb *crumb1, *crumb2; BOOL matching = TRUE; @@ -386,6 +540,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); @@ -417,6 +580,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); @@ -466,6 +642,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); @@ -479,6 +673,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); }