midimap: Handle MIDI running status.
[wine.git] / programs / explorer / startmenu.c
blob3173ae21fc6a80efafe68366c446b3a8b221c461
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 static BOOL shell_folder_is_empty(IShellFolder* folder)
136 IEnumIDList* enumidl;
137 LPITEMIDLIST pidl=NULL;
139 if (IShellFolder_EnumObjects(folder, NULL, SHCONTF_NONFOLDERS, &enumidl) == S_OK)
141 if (IEnumIDList_Next(enumidl, 1, &pidl, NULL) == S_OK)
143 CoTaskMemFree(pidl);
144 IEnumIDList_Release(enumidl);
145 return FALSE;
148 IEnumIDList_Release(enumidl);
151 if (IShellFolder_EnumObjects(folder, NULL, SHCONTF_FOLDERS, &enumidl) == S_OK)
153 BOOL found = FALSE;
154 IShellFolder *child_folder;
156 while (!found && IEnumIDList_Next(enumidl, 1, &pidl, NULL) == S_OK)
158 if (IShellFolder_BindToObject(folder, pidl, NULL, &IID_IShellFolder, (void *)&child_folder) == S_OK)
160 if (!shell_folder_is_empty(child_folder))
161 found = TRUE;
163 IShellFolder_Release(child_folder);
166 CoTaskMemFree(pidl);
169 IEnumIDList_Release(enumidl);
171 if (found)
172 return FALSE;
175 return TRUE;
178 /* add an individual file or folder to the menu, takes ownership of pidl */
179 static struct menu_item* add_shell_item(struct menu_item* parent, LPITEMIDLIST pidl)
181 struct menu_item* item;
182 MENUITEMINFOW mii;
183 HMENU parent_menu;
184 int existing_item_count, i;
185 BOOL match = FALSE;
186 SFGAOF flags;
188 item = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct menu_item));
190 if (parent->pidl == NULL)
192 pidl_to_shellfolder(pidl, &item->displayname, &item->folder);
194 else
196 STRRET strret;
198 if (SUCCEEDED(IShellFolder_GetDisplayNameOf(parent->folder, pidl, SHGDN_INFOLDER, &strret)))
199 StrRetToStrW(&strret, NULL, &item->displayname);
201 flags = SFGAO_FOLDER;
202 IShellFolder_GetAttributesOf(parent->folder, 1, (LPCITEMIDLIST*)&pidl, &flags);
204 if (flags & SFGAO_FOLDER)
205 IShellFolder_BindToObject(parent->folder, pidl, NULL, &IID_IShellFolder, (void *)&item->folder);
208 if (item->folder && shell_folder_is_empty(item->folder))
210 IShellFolder_Release(item->folder);
211 HeapFree(GetProcessHeap(), 0, item->displayname);
212 HeapFree(GetProcessHeap(), 0, item);
213 CoTaskMemFree(pidl);
214 return NULL;
217 parent_menu = parent->menuhandle;
219 item->parent = parent;
220 item->pidl = pidl;
222 existing_item_count = GetMenuItemCount(parent_menu);
223 mii.cbSize = sizeof(mii);
224 mii.fMask = MIIM_SUBMENU|MIIM_DATA;
226 /* search for an existing menu item with this name or the spot to insert this item */
227 if (parent->pidl != NULL)
229 for (i=0; i<existing_item_count; i++)
231 struct menu_item* existing_item;
232 int cmp;
234 GetMenuItemInfoW(parent_menu, i, TRUE, &mii);
235 existing_item = ((struct menu_item*)mii.dwItemData);
237 if (!existing_item)
238 continue;
240 /* folders before files */
241 if (existing_item->folder && !item->folder)
242 continue;
243 if (!existing_item->folder && item->folder)
244 break;
246 cmp = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, item->displayname, -1, existing_item->displayname, -1);
248 if (cmp == CSTR_LESS_THAN)
249 break;
251 if (cmp == CSTR_EQUAL)
253 match = TRUE;
254 break;
258 else
259 /* This item manually added to the root menu, so put it at the end */
260 i = existing_item_count;
262 if (!match)
264 /* no existing item with the same name; just add it */
265 mii.fMask = MIIM_STRING|MIIM_DATA;
266 mii.dwTypeData = item->displayname;
267 mii.dwItemData = (ULONG_PTR)item;
269 if (item->folder)
271 MENUINFO mi;
272 item->menuhandle = CreatePopupMenu();
273 mii.fMask |= MIIM_SUBMENU;
274 mii.hSubMenu = item->menuhandle;
276 mi.cbSize = sizeof(mi);
277 mi.fMask = MIM_MENUDATA;
278 mi.dwMenuData = (ULONG_PTR)item;
279 SetMenuInfo(item->menuhandle, &mi);
282 InsertMenuItemW(parent->menuhandle, i, TRUE, &mii);
284 list_add_tail(&items, &item->entry);
286 else if (item->folder)
288 /* there is an existing folder with the same name, combine them */
289 MENUINFO mi;
291 item->base = (struct menu_item*)mii.dwItemData;
292 item->menuhandle = item->base->menuhandle;
294 mii.dwItemData = (ULONG_PTR)item;
295 SetMenuItemInfoW(parent_menu, i, TRUE, &mii);
297 mi.cbSize = sizeof(mi);
298 mi.fMask = MIM_MENUDATA;
299 mi.dwMenuData = (ULONG_PTR)item;
300 SetMenuInfo(item->menuhandle, &mi);
302 list_add_tail(&items, &item->entry);
304 else {
305 /* duplicate shortcut, do nothing */
306 HeapFree(GetProcessHeap(), 0, item->displayname);
307 HeapFree(GetProcessHeap(), 0, item);
308 CoTaskMemFree(pidl);
309 item = NULL;
312 return item;
315 static void add_folder_contents(struct menu_item* parent)
317 IEnumIDList* enumidl;
319 if (IShellFolder_EnumObjects(parent->folder, NULL,
320 SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &enumidl) == S_OK)
322 LPITEMIDLIST rel_pidl=NULL;
323 while (S_OK == IEnumIDList_Next(enumidl, 1, &rel_pidl, NULL))
325 add_shell_item(parent, rel_pidl);
328 IEnumIDList_Release(enumidl);
332 static void destroy_menus(void)
334 if (!root_menu.menuhandle)
335 return;
337 DestroyMenu(root_menu.menuhandle);
338 root_menu.menuhandle = NULL;
340 while (!list_empty(&items))
342 struct menu_item* item;
344 item = LIST_ENTRY(list_head(&items), struct menu_item, entry);
346 if (item->folder)
347 IShellFolder_Release(item->folder);
349 CoTaskMemFree(item->pidl);
350 CoTaskMemFree(item->displayname);
352 list_remove(&item->entry);
353 HeapFree(GetProcessHeap(), 0, item);
357 static void fill_menu(struct menu_item* item)
359 if (!item->menu_filled)
361 add_folder_contents(item);
363 if (item->base)
365 fill_menu(item->base);
368 item->menu_filled = TRUE;
372 static void run_dialog(void)
374 void WINAPI (*pRunFileDlg)(HWND hWndOwner, HICON hIcon, LPCSTR lpszDir,
375 LPCSTR lpszTitle, LPCSTR lpszDesc, DWORD dwFlags);
376 HMODULE hShell32;
378 hShell32 = LoadLibraryW(L"shell32");
379 pRunFileDlg = (void*)GetProcAddress(hShell32, (LPCSTR)61);
381 pRunFileDlg(NULL, NULL, NULL, NULL, NULL, 0);
383 FreeLibrary(hShell32);
386 LRESULT menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
388 switch (msg)
390 case WM_INITMENUPOPUP:
392 HMENU hmenu = (HMENU)wparam;
393 struct menu_item* item;
394 MENUINFO mi;
396 mi.cbSize = sizeof(mi);
397 mi.fMask = MIM_MENUDATA;
398 GetMenuInfo(hmenu, &mi);
399 item = (struct menu_item*)mi.dwMenuData;
401 if (item)
402 fill_menu(item);
403 return 0;
405 break;
407 case WM_MENUCOMMAND:
409 HMENU hmenu = (HMENU)lparam;
410 struct menu_item* item;
411 MENUITEMINFOW mii;
413 mii.cbSize = sizeof(mii);
414 mii.fMask = MIIM_DATA|MIIM_ID;
415 GetMenuItemInfoW(hmenu, wparam, TRUE, &mii);
416 item = (struct menu_item*)mii.dwItemData;
418 if (item)
419 exec_item(item);
420 else if (mii.wID == MENU_ID_RUN)
421 run_dialog();
423 destroy_menus();
425 return 0;
429 return DefWindowProcW(hwnd, msg, wparam, lparam);
432 void do_startmenu(HWND hwnd)
434 LPITEMIDLIST pidl;
435 MENUINFO mi;
436 MENUITEMINFOW mii;
437 RECT rc={0,0,0,0};
438 TPMPARAMS tpm;
439 WCHAR run_label[50];
441 destroy_menus();
443 WINE_TRACE("creating start menu\n");
445 root_menu.menuhandle = public_startmenu.menuhandle = user_startmenu.menuhandle = CreatePopupMenu();
446 if (!root_menu.menuhandle)
448 return;
451 user_startmenu.parent = public_startmenu.parent = &root_menu;
452 user_startmenu.base = &public_startmenu;
453 user_startmenu.menu_filled = public_startmenu.menu_filled = FALSE;
455 if (!user_startmenu.pidl)
456 SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &user_startmenu.pidl);
458 if (!user_startmenu.folder)
459 pidl_to_shellfolder(user_startmenu.pidl, NULL, &user_startmenu.folder);
461 if (!public_startmenu.pidl)
462 SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &public_startmenu.pidl);
464 if (!public_startmenu.folder)
465 pidl_to_shellfolder(public_startmenu.pidl, NULL, &public_startmenu.folder);
467 if ((user_startmenu.folder && !shell_folder_is_empty(user_startmenu.folder)) ||
468 (public_startmenu.folder && !shell_folder_is_empty(public_startmenu.folder)))
470 fill_menu(&user_startmenu);
472 AppendMenuW(root_menu.menuhandle, MF_SEPARATOR, 0, NULL);
475 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl)))
476 add_shell_item(&root_menu, pidl);
478 LoadStringW(NULL, IDS_RUN, run_label, ARRAY_SIZE(run_label));
480 mii.cbSize = sizeof(mii);
481 mii.fMask = MIIM_STRING|MIIM_ID;
482 mii.dwTypeData = run_label;
483 mii.wID = MENU_ID_RUN;
485 InsertMenuItemW(root_menu.menuhandle, -1, TRUE, &mii);
487 mi.cbSize = sizeof(mi);
488 mi.fMask = MIM_STYLE;
489 mi.dwStyle = MNS_NOTIFYBYPOS;
490 SetMenuInfo(root_menu.menuhandle, &mi);
492 GetWindowRect(hwnd, &rc);
494 tpm.cbSize = sizeof(tpm);
495 tpm.rcExclude = rc;
497 if (!TrackPopupMenuEx(root_menu.menuhandle,
498 TPM_LEFTALIGN|TPM_BOTTOMALIGN|TPM_VERTICAL,
499 rc.left, rc.top, hwnd, &tpm))
501 WINE_ERR("couldn't display menu\n");