[3/6] explorer: Add a start menu.
Vincent Povirk
madewokherd at gmail.com
Tue Dec 10 15:08:06 CST 2013
-------------- next part --------------
From f3757497dc237a15a2018b827735b0f5c7950589 Mon Sep 17 00:00:00 2001
From: Vincent Povirk <vincent at codeweavers.com>
Date: Fri, 6 Dec 2013 14:16:54 -0600
Subject: [PATCH 3/6] explorer: Add a start menu.
---
programs/explorer/Makefile.in | 1 +
programs/explorer/explorer_private.h | 2 +
programs/explorer/startmenu.c | 419 +++++++++++++++++++++++++++++++++++
programs/explorer/systray.c | 9 +
4 files changed, 431 insertions(+)
create mode 100644 programs/explorer/startmenu.c
diff --git a/programs/explorer/Makefile.in b/programs/explorer/Makefile.in
index 52296fe..28bf590 100644
--- a/programs/explorer/Makefile.in
+++ b/programs/explorer/Makefile.in
@@ -7,6 +7,7 @@ C_SRCS = \
appbar.c \
desktop.c \
explorer.c \
+ startmenu.c \
systray.c
RC_SRCS = explorer.rc
diff --git a/programs/explorer/explorer_private.h b/programs/explorer/explorer_private.h
index 74ca7f0..108a3ab 100644
--- a/programs/explorer/explorer_private.h
+++ b/programs/explorer/explorer_private.h
@@ -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 CALLBACK menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
#endif /* __WINE_EXPLORER_PRIVATE_H */
diff --git a/programs/explorer/startmenu.c b/programs/explorer/startmenu.c
new file mode 100644
index 0000000..6fcca46
--- /dev/null
+++ b/programs/explorer/startmenu.c
@@ -0,0 +1,419 @@
+/*
+ * 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 UNICODE
+#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 = {{0}};
+static struct menu_item public_startmenu = {{0}};
+static struct menu_item user_startmenu = {{0}};
+
+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 struct menu_item* 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);
+ }
+
+ return parent;
+}
+
+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 CALLBACK 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");
+ }
+}
+
diff --git a/programs/explorer/systray.c b/programs/explorer/systray.c
index 96fc61d..f263205 100644
--- a/programs/explorer/systray.c
+++ b/programs/explorer/systray.c
@@ -621,6 +621,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 );
}
--
1.8.1.2
More information about the wine-patches
mailing list