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"
31 WINE_DEFAULT_DEBUG_CHANNEL(explorer
);
38 /* parent information */
39 struct menu_item
* parent
;
40 LPITEMIDLIST pidl
; /* relative to parent; absolute if parent->pidl is NULL */
42 /* folder information */
44 struct menu_item
* base
;
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
;
57 static ULONG
copy_pidls(struct menu_item
* item
, LPITEMIDLIST dest
)
60 ULONG bytes_copied
= 2;
62 if (item
->parent
->pidl
)
64 bytes_copied
= copy_pidls(item
->parent
, dest
);
67 item_size
= ILGetSize(item
->pidl
);
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
)
80 length
= copy_pidls(item
, NULL
);
82 result
= CoTaskMemAlloc(length
);
84 copy_pidls(item
, 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
;
114 hr
= SHBindToParent(pidl
, &IID_IShellFolder
, (void**)&parent_folder
, &relative_pidl
);
119 hr
= IShellFolder_GetDisplayNameOf(parent_folder
, relative_pidl
, SHGDN_INFOLDER
, &strret
);
122 hr
= StrRetToStrW(&strret
, NULL
, displayname
);
126 hr
= IShellFolder_BindToObject(parent_folder
, relative_pidl
, NULL
, &IID_IShellFolder
, (void**)out_folder
);
129 IShellFolder_Release(parent_folder
);
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
;
140 int existing_item_count
, i
;
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
);
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
;
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
;
181 GetMenuItemInfoW(parent_menu
, i
, TRUE
, &mii
);
182 existing_item
= ((struct menu_item
*)mii
.dwItemData
);
187 /* folders before files */
188 if (existing_item
->folder
&& !item
->folder
)
190 if (!existing_item
->folder
&& item
->folder
)
193 cmp
= CompareStringW(LOCALE_USER_DEFAULT
, NORM_IGNORECASE
, item
->displayname
, -1, existing_item
->displayname
, -1);
195 if (cmp
== CSTR_LESS_THAN
)
198 if (cmp
== CSTR_EQUAL
)
206 /* This item manually added to the root menu, so put it at the end */
207 i
= existing_item_count
;
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
;
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 */
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
);
252 /* duplicate shortcut, do nothing */
253 HeapFree(GetProcessHeap(), 0, item
->displayname
);
254 HeapFree(GetProcessHeap(), 0, 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
)
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
);
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
);
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
);
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
)
337 case WM_INITMENUPOPUP
:
339 HMENU hmenu
= (HMENU
)wparam
;
340 struct menu_item
* item
;
343 mi
.cbSize
= sizeof(mi
);
344 mi
.fMask
= MIM_MENUDATA
;
345 GetMenuInfo(hmenu
, &mi
);
346 item
= (struct menu_item
*)mi
.dwMenuData
;
356 HMENU hmenu
= (HMENU
)lparam
;
357 struct menu_item
* item
;
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
;
367 else if (mii
.wID
== MENU_ID_RUN
)
376 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
379 void do_startmenu(HWND hwnd
)
390 WINE_TRACE("creating start menu\n");
392 root_menu
.menuhandle
= public_startmenu
.menuhandle
= user_startmenu
.menuhandle
= CreatePopupMenu();
393 if (!root_menu
.menuhandle
)
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
);
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");