explorer: Add a start menu.
This commit is contained in:
parent
5a00584bab
commit
058765f765
|
@ -7,6 +7,7 @@ C_SRCS = \
|
|||
appbar.c \
|
||||
desktop.c \
|
||||
explorer.c \
|
||||
startmenu.c \
|
||||
systray.c
|
||||
|
||||
RC_SRCS = explorer.rc
|
||||
|
|
|
@ -24,5 +24,7 @@
|
|||
extern void manage_desktop( WCHAR *arg );
|
||||
extern void initialize_systray( HMODULE graphics_driver, BOOL using_root );
|
||||
extern void initialize_appbar(void);
|
||||
extern void do_startmenu( HWND owner );
|
||||
extern LRESULT menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
|
||||
#endif /* __WINE_EXPLORER_PRIVATE_H */
|
||||
|
|
|
@ -0,0 +1,415 @@
|
|||
/*
|
||||
* 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"
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
GetMenuItemInfoW(hmenu, wparam, TRUE, &mii);
|
||||
item = (struct menu_item*)mii.dwItemData;
|
||||
|
||||
exec_item(item);
|
||||
|
||||
destroy_menus();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProcW(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
void do_startmenu(HWND hwnd)
|
||||
{
|
||||
LPITEMIDLIST pidl;
|
||||
MENUINFO mi;
|
||||
RECT rc={0,0,0,0};
|
||||
TPMPARAMS tpm;
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -625,6 +625,15 @@ static LRESULT WINAPI tray_wndproc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM l
|
|||
ShowWindow( hwnd, SW_HIDE );
|
||||
return 0;
|
||||
|
||||
case WM_COMMAND:
|
||||
if ((HWND)lparam == start_button && HIWORD(wparam) == BN_CLICKED)
|
||||
do_startmenu(hwnd);
|
||||
break;
|
||||
|
||||
case WM_INITMENUPOPUP:
|
||||
case WM_MENUCOMMAND:
|
||||
return menu_wndproc(hwnd, msg, wparam, lparam);
|
||||
|
||||
default:
|
||||
return DefWindowProcW( hwnd, msg, wparam, lparam );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue