d3d9/tests: Don't require a WM_MOVE message.
[wine.git] / programs / explorer / startmenu.c
blobe9c651bb0affc51bd605dc358052456b8fcb1b02
1 /*
2 * Copyright (C) 2008 Vincent Povirk
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define COBJMACROS
20 #include <windows.h>
21 #include <shellapi.h>
22 #include <shlguid.h>
23 #include <shlobj.h>
24 #include <shlwapi.h>
25 #include <shobjidl.h>
26 #include "wine/debug.h"
27 #include "wine/list.h"
28 #include "explorer_private.h"
29 #include "resource.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(explorer);
33 struct menu_item
35 struct list entry;
36 LPWSTR displayname;
38 /* parent information */
39 struct menu_item* parent;
40 LPITEMIDLIST pidl; /* relative to parent; absolute if parent->pidl is NULL */
42 /* folder information */
43 IShellFolder* folder;
44 struct menu_item* base;
45 HMENU menuhandle;
46 BOOL menu_filled;
49 static struct list items = LIST_INIT(items);
51 static struct menu_item root_menu;
52 static struct menu_item public_startmenu;
53 static struct menu_item user_startmenu;
55 #define MENU_ID_RUN 1
57 static ULONG copy_pidls(struct menu_item* item, LPITEMIDLIST dest)
59 ULONG item_size;
60 ULONG bytes_copied = 2;
62 if (item->parent->pidl)
64 bytes_copied = copy_pidls(item->parent, dest);
67 item_size = ILGetSize(item->pidl);
69 if (dest)
70 memcpy(((char*)dest) + bytes_copied - 2, item->pidl, item_size);
72 return bytes_copied + item_size - 2;
75 static LPITEMIDLIST build_pidl(struct menu_item* item)
77 ULONG length;
78 LPITEMIDLIST result;
80 length = copy_pidls(item, NULL);
82 result = CoTaskMemAlloc(length);
84 copy_pidls(item, result);
86 return result;
89 static void exec_item(struct menu_item* item)
91 LPITEMIDLIST abs_pidl;
92 SHELLEXECUTEINFOW sei;
94 abs_pidl = build_pidl(item);
96 ZeroMemory(&sei, sizeof(sei));
97 sei.cbSize = sizeof(sei);
98 sei.fMask = SEE_MASK_IDLIST;
99 sei.nShow = SW_SHOWNORMAL;
100 sei.lpIDList = abs_pidl;
102 ShellExecuteExW(&sei);
104 CoTaskMemFree(abs_pidl);
107 static HRESULT pidl_to_shellfolder(LPITEMIDLIST pidl, LPWSTR *displayname, IShellFolder **out_folder)
109 IShellFolder* parent_folder=NULL;
110 LPCITEMIDLIST relative_pidl=NULL;
111 STRRET strret;
112 HRESULT hr;
114 hr = SHBindToParent(pidl, &IID_IShellFolder, (void**)&parent_folder, &relative_pidl);
116 if (displayname)
118 if (SUCCEEDED(hr))
119 hr = IShellFolder_GetDisplayNameOf(parent_folder, relative_pidl, SHGDN_INFOLDER, &strret);
121 if (SUCCEEDED(hr))
122 hr = StrRetToStrW(&strret, NULL, displayname);
125 if (SUCCEEDED(hr))
126 hr = IShellFolder_BindToObject(parent_folder, relative_pidl, NULL, &IID_IShellFolder, (void**)out_folder);
128 if (parent_folder)
129 IShellFolder_Release(parent_folder);
131 return hr;
134 /* add an individual file or folder to the menu, takes ownership of pidl */
135 static struct menu_item* add_shell_item(struct menu_item* parent, LPITEMIDLIST pidl)
137 struct menu_item* item;
138 MENUITEMINFOW mii;
139 HMENU parent_menu;
140 int existing_item_count, i;
141 BOOL match = FALSE;
142 SFGAOF flags;
144 item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct menu_item));
146 if (parent->pidl == NULL)
148 pidl_to_shellfolder(pidl, &item->displayname, &item->folder);
150 else
152 STRRET strret;
154 if (SUCCEEDED(IShellFolder_GetDisplayNameOf(parent->folder, pidl, SHGDN_INFOLDER, &strret)))
155 StrRetToStrW(&strret, NULL, &item->displayname);
157 flags = SFGAO_FOLDER;
158 IShellFolder_GetAttributesOf(parent->folder, 1, (LPCITEMIDLIST*)&pidl, &flags);
160 if (flags & SFGAO_FOLDER)
161 IShellFolder_BindToObject(parent->folder, pidl, NULL, &IID_IShellFolder, (void *)&item->folder);
164 parent_menu = parent->menuhandle;
166 item->parent = parent;
167 item->pidl = pidl;
169 existing_item_count = GetMenuItemCount(parent_menu);
170 mii.cbSize = sizeof(mii);
171 mii.fMask = MIIM_SUBMENU|MIIM_DATA;
173 /* search for an existing menu item with this name or the spot to insert this item */
174 if (parent->pidl != NULL)
176 for (i=0; i<existing_item_count; i++)
178 struct menu_item* existing_item;
179 int cmp;
181 GetMenuItemInfoW(parent_menu, i, TRUE, &mii);
182 existing_item = ((struct menu_item*)mii.dwItemData);
184 if (!existing_item)
185 continue;
187 /* folders before files */
188 if (existing_item->folder && !item->folder)
189 continue;
190 if (!existing_item->folder && item->folder)
191 break;
193 cmp = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, item->displayname, -1, existing_item->displayname, -1);
195 if (cmp == CSTR_LESS_THAN)
196 break;
198 if (cmp == CSTR_EQUAL)
200 match = TRUE;
201 break;
205 else
206 /* This item manually added to the root menu, so put it at the end */
207 i = existing_item_count;
209 if (!match)
211 /* no existing item with the same name; just add it */
212 mii.fMask = MIIM_STRING|MIIM_DATA;
213 mii.dwTypeData = item->displayname;
214 mii.dwItemData = (ULONG_PTR)item;
216 if (item->folder)
218 MENUINFO mi;
219 item->menuhandle = CreatePopupMenu();
220 mii.fMask |= MIIM_SUBMENU;
221 mii.hSubMenu = item->menuhandle;
223 mi.cbSize = sizeof(mi);
224 mi.fMask = MIM_MENUDATA;
225 mi.dwMenuData = (ULONG_PTR)item;
226 SetMenuInfo(item->menuhandle, &mi);
229 InsertMenuItemW(parent->menuhandle, i, TRUE, &mii);
231 list_add_tail(&items, &item->entry);
233 else if (item->folder)
235 /* there is an existing folder with the same name, combine them */
236 MENUINFO mi;
238 item->base = (struct menu_item*)mii.dwItemData;
239 item->menuhandle = item->base->menuhandle;
241 mii.dwItemData = (ULONG_PTR)item;
242 SetMenuItemInfoW(parent_menu, i, TRUE, &mii);
244 mi.cbSize = sizeof(mi);
245 mi.fMask = MIM_MENUDATA;
246 mi.dwMenuData = (ULONG_PTR)item;
247 SetMenuInfo(item->menuhandle, &mi);
249 list_add_tail(&items, &item->entry);
251 else {
252 /* duplicate shortcut, do nothing */
253 HeapFree(GetProcessHeap(), 0, item->displayname);
254 HeapFree(GetProcessHeap(), 0, item);
255 CoTaskMemFree(pidl);
256 item = NULL;
259 return item;
262 static void add_folder_contents(struct menu_item* parent)
264 IEnumIDList* enumidl;
266 if (IShellFolder_EnumObjects(parent->folder, NULL,
267 SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &enumidl) == S_OK)
269 LPITEMIDLIST rel_pidl=NULL;
270 while (S_OK == IEnumIDList_Next(enumidl, 1, &rel_pidl, NULL))
272 add_shell_item(parent, rel_pidl);
275 IEnumIDList_Release(enumidl);
279 static void destroy_menus(void)
281 if (!root_menu.menuhandle)
282 return;
284 DestroyMenu(root_menu.menuhandle);
285 root_menu.menuhandle = NULL;
287 while (!list_empty(&items))
289 struct menu_item* item;
291 item = LIST_ENTRY(list_head(&items), struct menu_item, entry);
293 if (item->folder)
294 IShellFolder_Release(item->folder);
296 CoTaskMemFree(item->pidl);
297 CoTaskMemFree(item->displayname);
299 list_remove(&item->entry);
300 HeapFree(GetProcessHeap(), 0, item);
304 static void fill_menu(struct menu_item* item)
306 if (!item->menu_filled)
308 add_folder_contents(item);
310 if (item->base)
312 fill_menu(item->base);
315 item->menu_filled = TRUE;
319 static void run_dialog(void)
321 void WINAPI (*pRunFileDlg)(HWND hWndOwner, HICON hIcon, LPCSTR lpszDir,
322 LPCSTR lpszTitle, LPCSTR lpszDesc, DWORD dwFlags);
323 HMODULE hShell32;
325 hShell32 = LoadLibraryA("shell32");
326 pRunFileDlg = (void*)GetProcAddress(hShell32, (LPCSTR)61);
328 pRunFileDlg(NULL, NULL, NULL, NULL, NULL, 0);
330 FreeLibrary(hShell32);
333 LRESULT menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
335 switch (msg)
337 case WM_INITMENUPOPUP:
339 HMENU hmenu = (HMENU)wparam;
340 struct menu_item* item;
341 MENUINFO mi;
343 mi.cbSize = sizeof(mi);
344 mi.fMask = MIM_MENUDATA;
345 GetMenuInfo(hmenu, &mi);
346 item = (struct menu_item*)mi.dwMenuData;
348 if (item)
349 fill_menu(item);
350 return 0;
352 break;
354 case WM_MENUCOMMAND:
356 HMENU hmenu = (HMENU)lparam;
357 struct menu_item* item;
358 MENUITEMINFOW mii;
360 mii.cbSize = sizeof(mii);
361 mii.fMask = MIIM_DATA|MIIM_ID;
362 GetMenuItemInfoW(hmenu, wparam, TRUE, &mii);
363 item = (struct menu_item*)mii.dwItemData;
365 if (item)
366 exec_item(item);
367 else if (mii.wID == MENU_ID_RUN)
368 run_dialog();
370 destroy_menus();
372 return 0;
376 return DefWindowProcW(hwnd, msg, wparam, lparam);
379 void do_startmenu(HWND hwnd)
381 LPITEMIDLIST pidl;
382 MENUINFO mi;
383 MENUITEMINFOW mii;
384 RECT rc={0,0,0,0};
385 TPMPARAMS tpm;
386 WCHAR run_label[50];
388 destroy_menus();
390 WINE_TRACE("creating start menu\n");
392 root_menu.menuhandle = public_startmenu.menuhandle = user_startmenu.menuhandle = CreatePopupMenu();
393 if (!root_menu.menuhandle)
395 return;
398 user_startmenu.parent = public_startmenu.parent = &root_menu;
399 user_startmenu.base = &public_startmenu;
400 user_startmenu.menu_filled = public_startmenu.menu_filled = FALSE;
402 if (!user_startmenu.pidl)
403 SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &user_startmenu.pidl);
405 if (!user_startmenu.folder)
406 pidl_to_shellfolder(user_startmenu.pidl, NULL, &user_startmenu.folder);
408 if (!public_startmenu.pidl)
409 SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &public_startmenu.pidl);
411 if (!public_startmenu.folder)
412 pidl_to_shellfolder(public_startmenu.pidl, NULL, &public_startmenu.folder);
414 fill_menu(&user_startmenu);
416 AppendMenuW(root_menu.menuhandle, MF_SEPARATOR, 0, NULL);
418 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl)))
419 add_shell_item(&root_menu, pidl);
421 LoadStringW(NULL, IDS_RUN, run_label, sizeof(run_label)/sizeof(run_label[0]));
423 mii.cbSize = sizeof(mii);
424 mii.fMask = MIIM_STRING|MIIM_ID;
425 mii.dwTypeData = run_label;
426 mii.wID = MENU_ID_RUN;
428 InsertMenuItemW(root_menu.menuhandle, -1, TRUE, &mii);
430 mi.cbSize = sizeof(mi);
431 mi.fMask = MIM_STYLE;
432 mi.dwStyle = MNS_NOTIFYBYPOS;
433 SetMenuInfo(root_menu.menuhandle, &mi);
435 GetWindowRect(hwnd, &rc);
437 tpm.cbSize = sizeof(tpm);
438 tpm.rcExclude = rc;
440 if (!TrackPopupMenuEx(root_menu.menuhandle,
441 TPM_LEFTALIGN|TPM_BOTTOMALIGN|TPM_VERTICAL,
442 rc.left, rc.top, hwnd, &tpm))
444 WINE_ERR("couldn't display menu\n");