Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/programs/explorer/startmenu.c
4389 views
1
/*
2
* Copyright (C) 2008 Vincent Povirk
3
*
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.
8
*
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.
13
*
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
17
*/
18
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"
30
31
WINE_DEFAULT_DEBUG_CHANNEL(explorer);
32
33
struct menu_item
34
{
35
struct list entry;
36
LPWSTR displayname;
37
38
/* parent information */
39
struct menu_item* parent;
40
LPITEMIDLIST pidl; /* relative to parent; absolute if parent->pidl is NULL */
41
42
/* folder information */
43
IShellFolder* folder;
44
struct menu_item* base;
45
HMENU menuhandle;
46
BOOL menu_filled;
47
};
48
49
static struct list items = LIST_INIT(items);
50
51
static struct menu_item root_menu;
52
static struct menu_item public_startmenu;
53
static struct menu_item user_startmenu;
54
55
#define MENU_ID_RUN 1
56
#define MENU_ID_EXIT 2
57
58
static ULONG copy_pidls(struct menu_item* item, LPITEMIDLIST dest)
59
{
60
ULONG item_size;
61
ULONG bytes_copied = 2;
62
63
if (item->parent->pidl)
64
{
65
bytes_copied = copy_pidls(item->parent, dest);
66
}
67
68
item_size = ILGetSize(item->pidl);
69
70
if (dest)
71
memcpy(((char*)dest) + bytes_copied - 2, item->pidl, item_size);
72
73
return bytes_copied + item_size - 2;
74
}
75
76
static LPITEMIDLIST build_pidl(struct menu_item* item)
77
{
78
ULONG length;
79
LPITEMIDLIST result;
80
81
length = copy_pidls(item, NULL);
82
83
result = CoTaskMemAlloc(length);
84
85
copy_pidls(item, result);
86
87
return result;
88
}
89
90
static void exec_item(struct menu_item* item)
91
{
92
LPITEMIDLIST abs_pidl;
93
SHELLEXECUTEINFOW sei;
94
95
abs_pidl = build_pidl(item);
96
97
ZeroMemory(&sei, sizeof(sei));
98
sei.cbSize = sizeof(sei);
99
sei.fMask = SEE_MASK_IDLIST;
100
sei.nShow = SW_SHOWNORMAL;
101
sei.lpIDList = abs_pidl;
102
103
ShellExecuteExW(&sei);
104
105
CoTaskMemFree(abs_pidl);
106
}
107
108
static HRESULT pidl_to_shellfolder(LPITEMIDLIST pidl, LPWSTR *displayname, IShellFolder **out_folder)
109
{
110
IShellFolder* parent_folder=NULL;
111
LPCITEMIDLIST relative_pidl=NULL;
112
STRRET strret;
113
HRESULT hr;
114
115
hr = SHBindToParent(pidl, &IID_IShellFolder, (void**)&parent_folder, &relative_pidl);
116
117
if (displayname)
118
{
119
if (SUCCEEDED(hr))
120
hr = IShellFolder_GetDisplayNameOf(parent_folder, relative_pidl, SHGDN_INFOLDER, &strret);
121
122
if (SUCCEEDED(hr))
123
hr = StrRetToStrW(&strret, NULL, displayname);
124
}
125
126
if (SUCCEEDED(hr))
127
hr = IShellFolder_BindToObject(parent_folder, relative_pidl, NULL, &IID_IShellFolder, (void**)out_folder);
128
129
if (parent_folder)
130
IShellFolder_Release(parent_folder);
131
132
return hr;
133
}
134
135
static BOOL shell_folder_is_empty(IShellFolder* folder)
136
{
137
IEnumIDList* enumidl;
138
LPITEMIDLIST pidl=NULL;
139
140
if (IShellFolder_EnumObjects(folder, NULL, SHCONTF_NONFOLDERS, &enumidl) == S_OK)
141
{
142
if (IEnumIDList_Next(enumidl, 1, &pidl, NULL) == S_OK)
143
{
144
CoTaskMemFree(pidl);
145
IEnumIDList_Release(enumidl);
146
return FALSE;
147
}
148
149
IEnumIDList_Release(enumidl);
150
}
151
152
if (IShellFolder_EnumObjects(folder, NULL, SHCONTF_FOLDERS, &enumidl) == S_OK)
153
{
154
BOOL found = FALSE;
155
IShellFolder *child_folder;
156
157
while (!found && IEnumIDList_Next(enumidl, 1, &pidl, NULL) == S_OK)
158
{
159
if (IShellFolder_BindToObject(folder, pidl, NULL, &IID_IShellFolder, (void *)&child_folder) == S_OK)
160
{
161
if (!shell_folder_is_empty(child_folder))
162
found = TRUE;
163
164
IShellFolder_Release(child_folder);
165
}
166
167
CoTaskMemFree(pidl);
168
}
169
170
IEnumIDList_Release(enumidl);
171
172
if (found)
173
return FALSE;
174
}
175
176
return TRUE;
177
}
178
179
/* add an individual file or folder to the menu, takes ownership of pidl */
180
static struct menu_item* add_shell_item(struct menu_item* parent, LPITEMIDLIST pidl)
181
{
182
struct menu_item* item;
183
MENUITEMINFOW mii;
184
HMENU parent_menu;
185
int existing_item_count, i;
186
BOOL match = FALSE;
187
SFGAOF flags;
188
189
item = calloc( 1, sizeof(struct menu_item) );
190
191
if (parent->pidl == NULL)
192
{
193
pidl_to_shellfolder(pidl, &item->displayname, &item->folder);
194
}
195
else
196
{
197
STRRET strret;
198
199
if (SUCCEEDED(IShellFolder_GetDisplayNameOf(parent->folder, pidl, SHGDN_INFOLDER, &strret)))
200
StrRetToStrW(&strret, NULL, &item->displayname);
201
202
flags = SFGAO_FOLDER;
203
IShellFolder_GetAttributesOf(parent->folder, 1, (LPCITEMIDLIST*)&pidl, &flags);
204
205
if (flags & SFGAO_FOLDER)
206
IShellFolder_BindToObject(parent->folder, pidl, NULL, &IID_IShellFolder, (void *)&item->folder);
207
}
208
209
if (item->folder && shell_folder_is_empty(item->folder))
210
{
211
IShellFolder_Release(item->folder);
212
free( item->displayname );
213
free( item );
214
CoTaskMemFree(pidl);
215
return NULL;
216
}
217
218
parent_menu = parent->menuhandle;
219
220
item->parent = parent;
221
item->pidl = pidl;
222
223
existing_item_count = GetMenuItemCount(parent_menu);
224
mii.cbSize = sizeof(mii);
225
mii.fMask = MIIM_SUBMENU|MIIM_DATA;
226
227
/* search for an existing menu item with this name or the spot to insert this item */
228
if (parent->pidl != NULL)
229
{
230
for (i=0; i<existing_item_count; i++)
231
{
232
struct menu_item* existing_item;
233
int cmp;
234
235
GetMenuItemInfoW(parent_menu, i, TRUE, &mii);
236
existing_item = ((struct menu_item*)mii.dwItemData);
237
238
if (!existing_item)
239
continue;
240
241
/* folders before files */
242
if (existing_item->folder && !item->folder)
243
continue;
244
if (!existing_item->folder && item->folder)
245
break;
246
247
cmp = CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, item->displayname, -1, existing_item->displayname, -1);
248
249
if (cmp == CSTR_LESS_THAN)
250
break;
251
252
if (cmp == CSTR_EQUAL)
253
{
254
match = TRUE;
255
break;
256
}
257
}
258
}
259
else
260
/* This item manually added to the root menu, so put it at the end */
261
i = existing_item_count;
262
263
if (!match)
264
{
265
/* no existing item with the same name; just add it */
266
mii.fMask = MIIM_STRING|MIIM_DATA;
267
mii.dwTypeData = item->displayname;
268
mii.dwItemData = (ULONG_PTR)item;
269
270
if (item->folder)
271
{
272
MENUINFO mi;
273
item->menuhandle = CreatePopupMenu();
274
mii.fMask |= MIIM_SUBMENU;
275
mii.hSubMenu = item->menuhandle;
276
277
mi.cbSize = sizeof(mi);
278
mi.fMask = MIM_MENUDATA;
279
mi.dwMenuData = (ULONG_PTR)item;
280
SetMenuInfo(item->menuhandle, &mi);
281
}
282
283
InsertMenuItemW(parent->menuhandle, i, TRUE, &mii);
284
285
list_add_tail(&items, &item->entry);
286
}
287
else if (item->folder)
288
{
289
/* there is an existing folder with the same name, combine them */
290
MENUINFO mi;
291
292
item->base = (struct menu_item*)mii.dwItemData;
293
item->menuhandle = item->base->menuhandle;
294
295
mii.dwItemData = (ULONG_PTR)item;
296
SetMenuItemInfoW(parent_menu, i, TRUE, &mii);
297
298
mi.cbSize = sizeof(mi);
299
mi.fMask = MIM_MENUDATA;
300
mi.dwMenuData = (ULONG_PTR)item;
301
SetMenuInfo(item->menuhandle, &mi);
302
303
list_add_tail(&items, &item->entry);
304
}
305
else {
306
/* duplicate shortcut, do nothing */
307
free( item->displayname );
308
free( item );
309
CoTaskMemFree(pidl);
310
item = NULL;
311
}
312
313
return item;
314
}
315
316
static void add_folder_contents(struct menu_item* parent)
317
{
318
IEnumIDList* enumidl;
319
320
if (IShellFolder_EnumObjects(parent->folder, NULL,
321
SHCONTF_FOLDERS|SHCONTF_NONFOLDERS, &enumidl) == S_OK)
322
{
323
LPITEMIDLIST rel_pidl=NULL;
324
while (S_OK == IEnumIDList_Next(enumidl, 1, &rel_pidl, NULL))
325
{
326
add_shell_item(parent, rel_pidl);
327
}
328
329
IEnumIDList_Release(enumidl);
330
}
331
}
332
333
static void destroy_menus(void)
334
{
335
if (!root_menu.menuhandle)
336
return;
337
338
DestroyMenu(root_menu.menuhandle);
339
root_menu.menuhandle = NULL;
340
341
while (!list_empty(&items))
342
{
343
struct menu_item* item;
344
345
item = LIST_ENTRY(list_head(&items), struct menu_item, entry);
346
347
if (item->folder)
348
IShellFolder_Release(item->folder);
349
350
CoTaskMemFree(item->pidl);
351
CoTaskMemFree(item->displayname);
352
353
list_remove(&item->entry);
354
free( item );
355
}
356
}
357
358
static void fill_menu(struct menu_item* item)
359
{
360
if (!item->menu_filled)
361
{
362
add_folder_contents(item);
363
364
if (item->base)
365
{
366
fill_menu(item->base);
367
}
368
369
item->menu_filled = TRUE;
370
}
371
}
372
373
static void run_dialog(void)
374
{
375
void (WINAPI *pRunFileDlg)(HWND owner, HICON icon, const char *dir,
376
const char *title, const char *desc, DWORD flags);
377
HMODULE hShell32;
378
379
hShell32 = LoadLibraryW(L"shell32");
380
pRunFileDlg = (void*)GetProcAddress(hShell32, (LPCSTR)61);
381
382
pRunFileDlg(NULL, NULL, NULL, NULL, NULL, 0);
383
384
FreeLibrary(hShell32);
385
}
386
387
static void shut_down(HWND hwnd)
388
{
389
WCHAR prompt[256];
390
int ret;
391
392
LoadStringW(NULL, IDS_EXIT_PROMPT, prompt, ARRAY_SIZE(prompt));
393
ret = MessageBoxW(hwnd, prompt, L"Wine", MB_YESNO|MB_ICONQUESTION|MB_SYSTEMMODAL);
394
if (ret == IDYES)
395
ExitWindows(0, 0);
396
}
397
398
LRESULT menu_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
399
{
400
switch (msg)
401
{
402
case WM_INITMENUPOPUP:
403
{
404
HMENU hmenu = (HMENU)wparam;
405
struct menu_item* item;
406
MENUINFO mi;
407
408
mi.cbSize = sizeof(mi);
409
mi.fMask = MIM_MENUDATA;
410
GetMenuInfo(hmenu, &mi);
411
item = (struct menu_item*)mi.dwMenuData;
412
413
if (item)
414
fill_menu(item);
415
return 0;
416
}
417
break;
418
419
case WM_MENUCOMMAND:
420
{
421
HMENU hmenu = (HMENU)lparam;
422
struct menu_item* item;
423
MENUITEMINFOW mii;
424
425
mii.cbSize = sizeof(mii);
426
mii.fMask = MIIM_DATA|MIIM_ID;
427
GetMenuItemInfoW(hmenu, wparam, TRUE, &mii);
428
item = (struct menu_item*)mii.dwItemData;
429
430
if (item)
431
exec_item(item);
432
else if (mii.wID == MENU_ID_RUN)
433
run_dialog();
434
else if (mii.wID == MENU_ID_EXIT)
435
shut_down(hwnd);
436
437
destroy_menus();
438
439
return 0;
440
}
441
}
442
443
return DefWindowProcW(hwnd, msg, wparam, lparam);
444
}
445
446
void do_startmenu(HWND hwnd)
447
{
448
LPITEMIDLIST pidl;
449
MENUINFO mi;
450
MENUITEMINFOW mii;
451
RECT rc={0,0,0,0};
452
TPMPARAMS tpm;
453
WCHAR label[64];
454
455
destroy_menus();
456
457
TRACE( "creating start menu\n" );
458
459
root_menu.menuhandle = public_startmenu.menuhandle = user_startmenu.menuhandle = CreatePopupMenu();
460
if (!root_menu.menuhandle)
461
{
462
return;
463
}
464
465
user_startmenu.parent = public_startmenu.parent = &root_menu;
466
user_startmenu.base = &public_startmenu;
467
user_startmenu.menu_filled = public_startmenu.menu_filled = FALSE;
468
469
if (!user_startmenu.pidl)
470
SHGetSpecialFolderLocation(NULL, CSIDL_STARTMENU, &user_startmenu.pidl);
471
472
if (!user_startmenu.folder)
473
pidl_to_shellfolder(user_startmenu.pidl, NULL, &user_startmenu.folder);
474
475
if (!public_startmenu.pidl)
476
SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_STARTMENU, &public_startmenu.pidl);
477
478
if (!public_startmenu.folder)
479
pidl_to_shellfolder(public_startmenu.pidl, NULL, &public_startmenu.folder);
480
481
if ((user_startmenu.folder && !shell_folder_is_empty(user_startmenu.folder)) ||
482
(public_startmenu.folder && !shell_folder_is_empty(public_startmenu.folder)))
483
{
484
fill_menu(&user_startmenu);
485
486
AppendMenuW(root_menu.menuhandle, MF_SEPARATOR, 0, NULL);
487
}
488
489
if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_CONTROLS, &pidl)))
490
add_shell_item(&root_menu, pidl);
491
492
LoadStringW(NULL, IDS_RUN, label, ARRAY_SIZE(label));
493
mii.cbSize = sizeof(mii);
494
mii.fMask = MIIM_STRING|MIIM_ID;
495
mii.dwTypeData = label;
496
mii.wID = MENU_ID_RUN;
497
InsertMenuItemW(root_menu.menuhandle, -1, TRUE, &mii);
498
499
mii.fMask = MIIM_FTYPE;
500
mii.fType = MFT_SEPARATOR;
501
InsertMenuItemW(root_menu.menuhandle, -1, TRUE, &mii);
502
503
LoadStringW(NULL, IDS_EXIT_LABEL, label, ARRAY_SIZE(label));
504
mii.fMask = MIIM_STRING|MIIM_ID;
505
mii.dwTypeData = label;
506
mii.wID = MENU_ID_EXIT;
507
InsertMenuItemW(root_menu.menuhandle, -1, TRUE, &mii);
508
509
mi.cbSize = sizeof(mi);
510
mi.fMask = MIM_STYLE;
511
mi.dwStyle = MNS_NOTIFYBYPOS;
512
SetMenuInfo(root_menu.menuhandle, &mi);
513
514
GetWindowRect(hwnd, &rc);
515
516
tpm.cbSize = sizeof(tpm);
517
tpm.rcExclude = rc;
518
519
if (!TrackPopupMenuEx(root_menu.menuhandle,
520
TPM_LEFTALIGN|TPM_BOTTOMALIGN|TPM_VERTICAL,
521
rc.left, rc.top, hwnd, &tpm))
522
{
523
ERR( "couldn't display menu\n" );
524
}
525
}
526
527