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
26 #include "wine/debug.h"
27 #include "wine/list.h"
28 #include "explorer_private.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(explorer
);
37 /* parent information */
38 struct menu_item
* parent
;
39 LPITEMIDLIST pidl
; /* relative to parent; absolute if parent->pidl is NULL */
41 /* folder information */
43 struct menu_item
* base
;
48 static struct list items
= LIST_INIT(items
);
50 static struct menu_item root_menu
;
51 static struct menu_item public_startmenu
;
52 static struct menu_item user_startmenu
;
54 static ULONG
copy_pidls(struct menu_item
* item
, LPITEMIDLIST dest
)
57 ULONG bytes_copied
= 2;
59 if (item
->parent
->pidl
)
61 bytes_copied
= copy_pidls(item
->parent
, dest
);
64 item_size
= ILGetSize(item
->pidl
);
67 memcpy(((char*)dest
) + bytes_copied
- 2, item
->pidl
, item_size
);
69 return bytes_copied
+ item_size
- 2;
72 static LPITEMIDLIST
build_pidl(struct menu_item
* item
)
77 length
= copy_pidls(item
, NULL
);
79 result
= CoTaskMemAlloc(length
);
81 copy_pidls(item
, result
);
86 static void exec_item(struct menu_item
* item
)
88 LPITEMIDLIST abs_pidl
;
89 SHELLEXECUTEINFOW sei
;
91 abs_pidl
= build_pidl(item
);
93 ZeroMemory(&sei
, sizeof(sei
));
94 sei
.cbSize
= sizeof(sei
);
95 sei
.fMask
= SEE_MASK_IDLIST
;
96 sei
.nShow
= SW_SHOWNORMAL
;
97 sei
.lpIDList
= abs_pidl
;
99 ShellExecuteExW(&sei
);
101 CoTaskMemFree(abs_pidl
);
104 static HRESULT
pidl_to_shellfolder(LPITEMIDLIST pidl
, LPWSTR
*displayname
, IShellFolder
**out_folder
)
106 IShellFolder
* parent_folder
=NULL
;
107 LPCITEMIDLIST relative_pidl
=NULL
;
111 hr
= SHBindToParent(pidl
, &IID_IShellFolder
, (void**)&parent_folder
, &relative_pidl
);
116 hr
= IShellFolder_GetDisplayNameOf(parent_folder
, relative_pidl
, SHGDN_INFOLDER
, &strret
);
119 hr
= StrRetToStrW(&strret
, NULL
, displayname
);
123 hr
= IShellFolder_BindToObject(parent_folder
, relative_pidl
, NULL
, &IID_IShellFolder
, (void**)out_folder
);
126 IShellFolder_Release(parent_folder
);
131 /* add an individual file or folder to the menu, takes ownership of pidl */
132 static struct menu_item
* add_shell_item(struct menu_item
* parent
, LPITEMIDLIST pidl
)
134 struct menu_item
* item
;
137 int existing_item_count
, i
;
141 item
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(struct menu_item
));
143 if (parent
->pidl
== NULL
)
145 pidl_to_shellfolder(pidl
, &item
->displayname
, &item
->folder
);
151 IShellFolder_GetDisplayNameOf(parent
->folder
, pidl
, SHGDN_INFOLDER
, &strret
);
152 StrRetToStrW(&strret
, NULL
, &item
->displayname
);
154 flags
= SFGAO_FOLDER
;
155 IShellFolder_GetAttributesOf(parent
->folder
, 1, (LPCITEMIDLIST
*)&pidl
, &flags
);
157 if (flags
& SFGAO_FOLDER
)
158 IShellFolder_BindToObject(parent
->folder
, pidl
, NULL
, &IID_IShellFolder
, (void *)&item
->folder
);
161 parent_menu
= parent
->menuhandle
;
163 item
->parent
= parent
;
166 existing_item_count
= GetMenuItemCount(parent_menu
);
167 mii
.cbSize
= sizeof(mii
);
168 mii
.fMask
= MIIM_SUBMENU
|MIIM_DATA
;
170 /* search for an existing menu item with this name or the spot to insert this item */
171 if (parent
->pidl
!= NULL
)
173 for (i
=0; i
<existing_item_count
; i
++)
175 struct menu_item
* existing_item
;
178 GetMenuItemInfoW(parent_menu
, i
, TRUE
, &mii
);
179 existing_item
= ((struct menu_item
*)mii
.dwItemData
);
184 /* folders before files */
185 if (existing_item
->folder
&& !item
->folder
)
187 if (!existing_item
->folder
&& item
->folder
)
190 cmp
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, item
->displayname
, -1, existing_item
->displayname
, -1);
192 if (cmp
== CSTR_LESS_THAN
)
195 if (cmp
== CSTR_EQUAL
)
203 /* This item manually added to the root menu, so put it at the end */
204 i
= existing_item_count
;
208 /* no existing item with the same name; just add it */
209 mii
.fMask
= MIIM_STRING
|MIIM_DATA
;
210 mii
.dwTypeData
= item
->displayname
;
211 mii
.dwItemData
= (ULONG_PTR
)item
;
216 item
->menuhandle
= CreatePopupMenu();
217 mii
.fMask
|= MIIM_SUBMENU
;
218 mii
.hSubMenu
= item
->menuhandle
;
220 mi
.cbSize
= sizeof(mi
);
221 mi
.fMask
= MIM_MENUDATA
;
222 mi
.dwMenuData
= (ULONG_PTR
)item
;
223 SetMenuInfo(item
->menuhandle
, &mi
);
226 InsertMenuItemW(parent
->menuhandle
, i
, TRUE
, &mii
);
228 list_add_tail(&items
, &item
->entry
);
230 else if (item
->folder
)
232 /* there is an existing folder with the same name, combine them */
235 item
->base
= (struct menu_item
*)mii
.dwItemData
;
236 item
->menuhandle
= item
->base
->menuhandle
;
238 mii
.dwItemData
= (ULONG_PTR
)item
;
239 SetMenuItemInfoW(parent_menu
, i
, TRUE
, &mii
);
241 mi
.cbSize
= sizeof(mi
);
242 mi
.fMask
= MIM_MENUDATA
;
243 mi
.dwMenuData
= (ULONG_PTR
)item
;
244 SetMenuInfo(item
->menuhandle
, &mi
);
246 list_add_tail(&items
, &item
->entry
);
249 /* duplicate shortcut, do nothing */
250 HeapFree(GetProcessHeap(), 0, item
->displayname
);
251 HeapFree(GetProcessHeap(), 0, item
);
259 static void add_folder_contents(struct menu_item
* parent
)
261 IEnumIDList
* enumidl
;
263 if (IShellFolder_EnumObjects(parent
->folder
, NULL
,
264 SHCONTF_FOLDERS
|SHCONTF_NONFOLDERS
, &enumidl
) == S_OK
)
266 LPITEMIDLIST rel_pidl
=NULL
;
267 while (S_OK
== IEnumIDList_Next(enumidl
, 1, &rel_pidl
, NULL
))
269 add_shell_item(parent
, rel_pidl
);
272 IEnumIDList_Release(enumidl
);
276 static void destroy_menus(void)
278 if (!root_menu
.menuhandle
)
281 DestroyMenu(root_menu
.menuhandle
);
282 root_menu
.menuhandle
= NULL
;
284 while (!list_empty(&items
))
286 struct menu_item
* item
;
288 item
= LIST_ENTRY(list_head(&items
), struct menu_item
, entry
);
291 IShellFolder_Release(item
->folder
);
293 CoTaskMemFree(item
->pidl
);
294 CoTaskMemFree(item
->displayname
);
296 list_remove(&item
->entry
);
297 HeapFree(GetProcessHeap(), 0, item
);
301 static void fill_menu(struct menu_item
* item
)
303 if (!item
->menu_filled
)
305 add_folder_contents(item
);
309 fill_menu(item
->base
);
312 item
->menu_filled
= TRUE
;
316 LRESULT
menu_wndproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
320 case WM_INITMENUPOPUP
:
322 HMENU hmenu
= (HMENU
)wparam
;
323 struct menu_item
* item
;
326 mi
.cbSize
= sizeof(mi
);
327 mi
.fMask
= MIM_MENUDATA
;
328 GetMenuInfo(hmenu
, &mi
);
329 item
= (struct menu_item
*)mi
.dwMenuData
;
339 HMENU hmenu
= (HMENU
)lparam
;
340 struct menu_item
* item
;
343 mii
.cbSize
= sizeof(mii
);
344 mii
.fMask
= MIIM_DATA
;
345 GetMenuItemInfoW(hmenu
, wparam
, TRUE
, &mii
);
346 item
= (struct menu_item
*)mii
.dwItemData
;
356 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
359 void do_startmenu(HWND hwnd
)
368 WINE_TRACE("creating start menu\n");
370 root_menu
.menuhandle
= public_startmenu
.menuhandle
= user_startmenu
.menuhandle
= CreatePopupMenu();
371 if (!root_menu
.menuhandle
)
376 user_startmenu
.parent
= public_startmenu
.parent
= &root_menu
;
377 user_startmenu
.base
= &public_startmenu
;
378 user_startmenu
.menu_filled
= public_startmenu
.menu_filled
= FALSE
;
380 if (!user_startmenu
.pidl
)
381 SHGetSpecialFolderLocation(NULL
, CSIDL_STARTMENU
, &user_startmenu
.pidl
);
383 if (!user_startmenu
.folder
)
384 pidl_to_shellfolder(user_startmenu
.pidl
, NULL
, &user_startmenu
.folder
);
386 if (!public_startmenu
.pidl
)
387 SHGetSpecialFolderLocation(NULL
, CSIDL_COMMON_STARTMENU
, &public_startmenu
.pidl
);
389 if (!public_startmenu
.folder
)
390 pidl_to_shellfolder(public_startmenu
.pidl
, NULL
, &public_startmenu
.folder
);
392 fill_menu(&user_startmenu
);
394 AppendMenuW(root_menu
.menuhandle
, MF_SEPARATOR
, 0, NULL
);
396 if (SUCCEEDED(SHGetSpecialFolderLocation(NULL
, CSIDL_CONTROLS
, &pidl
)))
397 add_shell_item(&root_menu
, pidl
);
399 mi
.cbSize
= sizeof(mi
);
400 mi
.fMask
= MIM_STYLE
;
401 mi
.dwStyle
= MNS_NOTIFYBYPOS
;
402 SetMenuInfo(root_menu
.menuhandle
, &mi
);
404 GetWindowRect(hwnd
, &rc
);
406 tpm
.cbSize
= sizeof(tpm
);
409 if (!TrackPopupMenuEx(root_menu
.menuhandle
,
410 TPM_LEFTALIGN
|TPM_BOTTOMALIGN
|TPM_VERTICAL
,
411 rc
.left
, rc
.top
, hwnd
, &tpm
))
413 WINE_ERR("couldn't display menu\n");