123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503 |
- /*
- * Copyright (C) 2008 Vincent Povirk
- *
- * 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 <windows.h>
- #include <shellapi.h>
- #include <shlguid.h>
- #include <shlobj.h>
- #include <shlwapi.h>
- #include <shobjidl.h>
- #include "wine/debug.h"
- #include "wine/list.h"
- #include "explorer_private.h"
- #include "resource.h"
- WINE_DEFAULT_DEBUG_CHANNEL(explorer);
- struct menu_item
- {
- struct list entry;
- LPWSTR displayname;
- /* parent information */
- struct menu_item* parent;
- LPITEMIDLIST pidl; /* relative to parent; absolute if parent->pidl is NULL */
- /* folder information */
- IShellFolder* folder;
- struct menu_item* base;
- HMENU menuhandle;
- BOOL menu_filled;
- };
- static struct list items = LIST_INIT(items);
- static struct menu_item root_menu;
- static struct menu_item public_startmenu;
- static struct menu_item user_startmenu;
- #define MENU_ID_RUN 1
- static ULONG copy_pidls(struct menu_item* item, LPITEMIDLIST dest)
- {
- ULONG item_size;
- ULONG bytes_copied = 2;
- if (item->parent->pidl)
- {
- bytes_copied = copy_pidls(item->parent, dest);
- }
- item_size = ILGetSize(item->pidl);
- if (dest)
- memcpy(((char*)dest) + bytes_copied - 2, item->pidl, item_size);
- return bytes_copied + item_size - 2;
- }
- static LPITEMIDLIST build_pidl(struct menu_item* item)
- {
- ULONG length;
- LPITEMIDLIST result;
- length = copy_pidls(item, NULL);
- result = CoTaskMemAlloc(length);
- copy_pidls(item, result);
- return result;
- }
- static void exec_item(struct menu_item* item)
- {
- LPITEMIDLIST abs_pidl;
- SHELLEXECUTEINFOW sei;
- abs_pidl = build_pidl(item);
- ZeroMemory(&sei, sizeof(sei));
- sei.cbSize = sizeof(sei);
- sei.fMask = SEE_MASK_IDLIST;
- sei.nShow = SW_SHOWNORMAL;
- sei.lpIDList = abs_pidl;
- ShellExecuteExW(&sei);
- CoTaskMemFree(abs_pidl);
- }
- static HRESULT pidl_to_shellfolder(LPITEMIDLIST pidl, LPWSTR *displayname, IShellFolder **out_folder)
- {
- IShellFolder* parent_folder=NULL;
- LPCITEMIDLIST relative_pidl=NULL;
- STRRET strret;
- HRESULT hr;
- hr = SHBindToParent(pidl, &IID_IShellFolder, (void**)&parent_folder, &relative_pidl);
- if (displayname)
- {
- if (SUCCEEDED(hr))
- hr = IShellFolder_GetDisplayNameOf(parent_folder, relative_pidl, SHGDN_INFOLDER, &strret);
- if (SUCCEEDED(hr))
- hr = StrRetToStrW(&strret, NULL, displayname);
- }
- if (SUCCEEDED(hr))
- hr = IShellFolder_BindToObject(parent_folder, relative_pidl, NULL, &IID_IShellFolder, (void**)out_folder);
- if (parent_folder)
- IShellFolder_Release(parent_folder);
- return hr;
- }
- static BOOL shell_folder_is_empty(IShellFolder* folder)
- {
- IEnumIDList* enumidl;
- LPITEMIDLIST pidl=NULL;
- if (IShellFolder_EnumObjects(folder, NULL, SHCONTF_NONFOLDERS, &enumidl) == S_OK)
- {
- if (IEnumIDList_Next(enumidl, 1, &pidl, NULL) == S_OK)
- {
- CoTaskMemFree(pidl);
- IEnumIDList_Release(enumidl);
- return FALSE;
- }
- IEnumIDList_Release(enumidl);
- }
- if (IShellFolder_EnumObjects(folder, NULL, SHCONTF_FOLDERS, &enumidl) == S_OK)
- {
- BOOL found = FALSE;
- IShellFolder *child_folder;
- while (!found && IEnumIDList_Next(enumidl, 1, &pidl, NULL) == S_OK)
- {
- if (IShellFolder_BindToObject(folder, pidl, NULL, &IID_IShellFolder, (void *)&child_folder) == S_OK)
- {
- if (!shell_folder_is_empty(child_folder))
- found = TRUE;
- IShellFolder_Release(child_folder);
- }
- CoTaskMemFree(pidl);
- }
- IEnumIDList_Release(enumidl);
- if (found)
- return FALSE;
- }
- return TRUE;
- }
- /* add an individual file or folder to the menu, takes ownership of pidl */
- static struct menu_item* add_shell_item(struct menu_item* parent, LPITEMIDLIST pidl)
- {
- struct menu_item* item;
- MENUITEMINFOW mii;
- HMENU parent_menu;
- int existing_item_count, i;
- BOOL match = FALSE;
- SFGAOF flags;
- item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct menu_item));
- if (parent->pidl == NULL)
- {
- pidl_to_shellfolder(pidl, &item->displayname, &item->folder);
- }
- else
- {
- STRRET strret;
- if (SUCCEEDED(IShellFolder_GetDisplayNameOf(parent->folder, pidl, SHGDN_INFOLDER, &strret)))
- StrRetToStrW(&strret, NULL, &item->displayname);
- flags = SFGAO_FOLDER;
- IShellFolder_GetAttributesOf(parent->folder, 1, (LPCITEMIDLIST*)&pidl, &flags);
- if (flags & SFGAO_FOLDER)
- IShellFolder_BindToObject(parent->folder, pidl, NULL, &IID_IShellFolder, (void *)&item->folder);
- }
- if (item->folder && shell_folder_is_empty(item->folder))
- {
- IShellFolder_Release(item->folder);
- HeapFree(GetProcessHeap(), 0, item->displayname);
- HeapFree(GetProcessHeap(), 0, item);
- CoTaskMemFree(pidl);
- return NULL;
- }
- parent_menu = parent->menuhandle;
- item->parent = parent;
- item->pidl = pidl;
- existing_item_count = GetMenuItemCount(parent_menu);
- mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_SUBMENU|MIIM_DATA;
- /* search for an existing menu item with this name or the spot to insert this item */
- if (parent->pidl != NULL)
- {
- for (i=0; i<existing_item_count; i++)
- {
- struct menu_item* existing_item;
- int cmp;
- GetMenuItemInfoW(parent_menu, i, TRUE, &mii);
- existing_item = ((struct menu_item*)mii.dwItemData);
- if (!existing_item)
- continue;
- /* folders before files */
- if (existing_item->folder && !item->folder)
- continue;
- if (!existing_item->folder && item->folder)
- break;
- cmp = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, item->displayname, -1, existing_item->displayname, -1);
- if (cmp == CSTR_LESS_THAN)
- break;
- if (cmp == CSTR_EQUAL)
- {
- match = TRUE;
- break;
- }
- }
- }
- else
- /* This item manually added to the root menu, so put it at the end */
- i = existing_item_count;
- if (!match)
- {
- /* no existing item with the same name; just add it */
- mii.fMask = MIIM_STRING|MIIM_DATA;
- mii.dwTypeData = item->displayname;
- mii.dwItemData = (ULONG_PTR)item;
- if (item->folder)
- {
- MENUINFO mi;
- item->menuhandle = CreatePopupMenu();
- mii.fMask |= MIIM_SUBMENU;
- mii.hSubMenu = item->menuhandle;
- mi.cbSize = sizeof(mi);
- mi.fMask = MIM_MENUDATA;
- mi.dwMenuData = (ULONG_PTR)item;
- SetMenuInfo(item->menuhandle, &mi);
- }
- InsertMenuItemW(parent->menuhandle, i, TRUE, &mii);
- list_add_tail(&items, &item->entry);
- }
- else if (item->folder)
- {
- /* there is an existing folder with the same name, combine them */
- MENUINFO mi;
- item->base = (struct menu_item*)mii.dwItemData;
- item->menuhandle = item->base->menuhandle;
- mii.dwItemData = (ULONG_PTR)item;
- SetMenuItemInfoW(parent_menu, i, TRUE, &mii);
- mi.cbSize = sizeof(mi);
- mi.fMask = MIM_MENUDATA;
- mi.dwMenuData = (ULONG_PTR)item;
- SetMenuInfo(item->menuhandle, &mi);
- list_add_tail(&items, &item->entry);
- }
- else {
- /* duplicate shortcut, do nothing */
- HeapFree(GetProcessHeap(), 0, item->displayname);
- HeapFree(GetProcessHeap(), 0, item);
- CoTaskMemFree(pidl);
- item = NULL;
- }
- return item;
- }
- static void add_folder_contents(struct menu_item* parent)
- {
- IEnumIDList* enumidl;
- if (IShellFolder_EnumObjects(parent->folder, NULL,
- SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &enumidl) == S_OK)
- {
- LPITEMIDLIST rel_pidl=NULL;
- while (S_OK == IEnumIDList_Next(enumidl, 1, &rel_pidl, NULL))
- {
- add_shell_item(parent, rel_pidl);
- }
- IEnumIDList_Release(enumidl);
- }
- }
- static void destroy_menus(void)
- {
- if (!root_menu.menuhandle)
- return;
- DestroyMenu(root_menu.menuhandle);
- root_menu.menuhandle = NULL;
- while (!list_empty(&items))
- {
- struct menu_item* item;
- item = LIST_ENTRY(list_head(&items), struct menu_item, entry);
- if (item->folder)
- IShellFolder_Release(item->folder);
- CoTaskMemFree(item->pidl);
- CoTaskMemFree(item->displayname);
- list_remove(&item->entry);
- HeapFree(GetProcessHeap(), 0, item);
- }
- }
- static void fill_menu(struct menu_item* item)
- {
- if (!item->menu_filled)
- {
- add_folder_contents(item);
- if (item->base)
- {
- fill_menu(item->base);
- }
- item->menu_filled = TRUE;
- }
- }
- static void run_dialog(void)
- {
- void WINAPI (*pRunFileDlg)(HWND hWndOwner, HICON hIcon, LPCSTR lpszDir,
- LPCSTR lpszTitle, LPCSTR lpszDesc, DWORD dwFlags);
- HMODULE hShell32;
- hShell32 = LoadLibraryW(L"shell32");
- pRunFileDlg = (void*)GetProcAddress(hShell32, (LPCSTR)61);
- pRunFileDlg(NULL, NULL, NULL, NULL, NULL, 0);
- FreeLibrary(hShell32);
- }
- LRESULT menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
- {
- switch (msg)
- {
- case WM_INITMENUPOPUP:
- {
- HMENU hmenu = (HMENU)wparam;
- struct menu_item* item;
- MENUINFO mi;
- mi.cbSize = sizeof(mi);
- mi.fMask = MIM_MENUDATA;
- GetMenuInfo(hmenu, &mi);
- item = (struct menu_item*)mi.dwMenuData;
- if (item)
- fill_menu(item);
- return 0;
- }
- break;
- case WM_MENUCOMMAND:
- {
- HMENU hmenu = (HMENU)lparam;
- struct menu_item* item;
- MENUITEMINFOW mii;
- mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_DATA|MIIM_ID;
- GetMenuItemInfoW(hmenu, wparam, TRUE, &mii);
- item = (struct menu_item*)mii.dwItemData;
- if (item)
- exec_item(item);
- else if (mii.wID == MENU_ID_RUN)
- run_dialog();
- destroy_menus();
- return 0;
- }
- }
- return DefWindowProcW(hwnd, msg, wparam, lparam);
- }
- void do_startmenu(HWND hwnd)
- {
- LPITEMIDLIST pidl;
- MENUINFO mi;
- MENUITEMINFOW mii;
- RECT rc={0,0,0,0};
- TPMPARAMS tpm;
- WCHAR run_label[50];
- destroy_menus();
- WINE_TRACE("creating start menu\n");
- root_menu.menuhandle = public_startmenu.menuhandle = user_startmenu.menuhandle = CreatePopupMenu();
- if (!root_menu.menuhandle)
- {
- return;
- }
- user_startmenu.parent = public_startmenu.parent = &root_menu;
- user_startmenu.base = &public_startmenu;
- user_startmenu.menu_filled = public_startmenu.menu_filled = FALSE;
- if (!user_startmenu.pidl)
- SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &user_startmenu.pidl);
- if (!user_startmenu.folder)
- pidl_to_shellfolder(user_startmenu.pidl, NULL, &user_startmenu.folder);
- if (!public_startmenu.pidl)
- SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &public_startmenu.pidl);
- if (!public_startmenu.folder)
- pidl_to_shellfolder(public_startmenu.pidl, NULL, &public_startmenu.folder);
- if ((user_startmenu.folder && !shell_folder_is_empty(user_startmenu.folder)) ||
- (public_startmenu.folder && !shell_folder_is_empty(public_startmenu.folder)))
- {
- fill_menu(&user_startmenu);
- AppendMenuW(root_menu.menuhandle, MF_SEPARATOR, 0, NULL);
- }
- if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl)))
- add_shell_item(&root_menu, pidl);
- LoadStringW(NULL, IDS_RUN, run_label, ARRAY_SIZE(run_label));
- mii.cbSize = sizeof(mii);
- mii.fMask = MIIM_STRING|MIIM_ID;
- mii.dwTypeData = run_label;
- mii.wID = MENU_ID_RUN;
- InsertMenuItemW(root_menu.menuhandle, -1, TRUE, &mii);
- mi.cbSize = sizeof(mi);
- mi.fMask = MIM_STYLE;
- mi.dwStyle = MNS_NOTIFYBYPOS;
- SetMenuInfo(root_menu.menuhandle, &mi);
- GetWindowRect(hwnd, &rc);
- tpm.cbSize = sizeof(tpm);
- tpm.rcExclude = rc;
- if (!TrackPopupMenuEx(root_menu.menuhandle,
- TPM_LEFTALIGN|TPM_BOTTOMALIGN|TPM_VERTICAL,
- rc.left, rc.top, hwnd, &tpm))
- {
- WINE_ERR("couldn't display menu\n");
- }
- }
|