Reactos
1/*
2 * provides new shell item service
3 *
4 * Copyright 2007 Johannes Anderwald (johannes.anderwald@reactos.org)
5 * Copyright 2009 Andrew Hill
6 * Copyright 2012 Rafal Harabien
7 * Copyright 2019 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include "precomp.h"
25
26WINE_DEFAULT_DEBUG_CHANNEL(shell);
27
28CNewMenu::CNewMenu() :
29 m_pidlFolder(NULL),
30 m_pItems(NULL),
31 m_pLinkItem(NULL),
32 m_pSite(NULL),
33 m_idCmdFirst(0),
34 m_idCmdFolder(-1),
35 m_idCmdLink(-1),
36 m_bCustomIconFolder(FALSE),
37 m_bCustomIconLink(FALSE),
38 m_hIconFolder(NULL),
39 m_hIconLink(NULL)
40{
41}
42
43CNewMenu::~CNewMenu()
44{
45 UnloadAllItems();
46
47 if (m_bCustomIconFolder && m_hIconFolder)
48 DestroyIcon(m_hIconFolder);
49 if (m_bCustomIconLink && m_hIconLink)
50 DestroyIcon(m_hIconLink);
51 if (m_pidlFolder)
52 ILFree(m_pidlFolder);
53}
54
55void CNewMenu::UnloadItem(SHELLNEW_ITEM *pItem)
56{
57 /* Note: free allows NULL as argument */
58 free(pItem->pData);
59 free(pItem->pwszDesc);
60 free(pItem->pwszExt);
61
62 if (pItem->hIcon)
63 DestroyIcon(pItem->hIcon);
64
65 HeapFree(GetProcessHeap(), 0, pItem);
66}
67
68void CNewMenu::UnloadAllItems()
69{
70 SHELLNEW_ITEM *pCurItem;
71
72 /* Unload the handler items, including the link item */
73 while (m_pItems)
74 {
75 pCurItem = m_pItems;
76 m_pItems = m_pItems->pNext;
77 UnloadItem(pCurItem);
78 }
79 m_pItems = NULL;
80 m_pLinkItem = NULL;
81}
82
83CNewMenu::SHELLNEW_ITEM *CNewMenu::LoadItem(LPCWSTR pwszExt)
84{
85 HKEY hKey;
86 WCHAR wszBuf[MAX_PATH];
87 PBYTE pData = NULL;
88 DWORD cbData;
89
90 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s\\ShellNew", pwszExt);
91
92 TRACE("LoadItem Keyname %s Name %s\n", debugstr_w(pwszExt), debugstr_w(wszBuf));
93
94 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
95 {
96 WCHAR wszValue[MAX_PATH];
97 cbData = sizeof(wszValue);
98 if (RegGetValueW(HKEY_CLASSES_ROOT, pwszExt, NULL, RRF_RT_REG_SZ, NULL, wszValue,
99 &cbData) != ERROR_SUCCESS || !wszValue[0])
100 {
101 TRACE("Failed to open key\n");
102 return NULL;
103 }
104 wszValue[_countof(wszValue) - 1] = UNICODE_NULL; // Avoid buffer overrun
105 StringCbPrintfW(wszBuf, sizeof(wszBuf), L"%s\\%s\\ShellNew", pwszExt, wszValue);
106 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszBuf, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
107 {
108 TRACE("Failed to open key\n");
109 return NULL;
110 }
111 }
112
113 /* Find first valid value */
114 struct
115 {
116 LPCWSTR pszName;
117 SHELLNEW_TYPE Type;
118 BOOL bNeedData;
119 BOOL bStr;
120 } Types[] = {
121 {L"FileName", SHELLNEW_TYPE_FILENAME, TRUE, TRUE},
122 {L"Command", SHELLNEW_TYPE_COMMAND, TRUE, TRUE},
123 {L"Data", SHELLNEW_TYPE_DATA, TRUE, FALSE},
124 {L"NullFile", SHELLNEW_TYPE_NULLFILE, FALSE},
125 {NULL}
126 };
127 UINT i;
128
129 for (i = 0; Types[i].pszName; ++i)
130 {
131 /* Note: We are using ANSI function because strings can be treated as data */
132 cbData = 0;
133 DWORD dwFlags = Types[i].bStr ? RRF_RT_REG_SZ : RRF_RT_ANY;
134 DWORD dwType;
135 if (RegGetValueW(hKey, NULL, Types[i].pszName, dwFlags, NULL, NULL, &cbData) == ERROR_SUCCESS)
136 {
137 if (Types[i].bNeedData && cbData > 0)
138 {
139 pData = (PBYTE)malloc(cbData);
140 RegGetValueW(hKey, NULL, Types[i].pszName, dwFlags, &dwType, pData, &cbData);
141 if (!Types[i].bStr && (dwType == REG_SZ || dwType == REG_EXPAND_SZ))
142 {
143 PBYTE pData2 = (PBYTE)malloc(cbData);
144 cbData = WideCharToMultiByte(CP_ACP, 0, (LPWSTR)pData, -1, (LPSTR)pData2, cbData, NULL, NULL);
145 free(pData);
146 pData = pData2;
147 }
148 }
149 break;
150 }
151 }
152 RegCloseKey(hKey);
153
154 /* Was any key found? */
155 if (!Types[i].pszName)
156 {
157 free(pData);
158 return NULL;
159 }
160
161 SHFILEINFOW fi;
162 if (!SHGetFileInfoW(pwszExt, FILE_ATTRIBUTE_NORMAL, &fi, sizeof(fi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME | SHGFI_ICON | SHGFI_SMALLICON))
163 {
164 free(pData);
165 return NULL;
166 }
167
168 /* Create new item */
169 SHELLNEW_ITEM *pNewItem = static_cast<SHELLNEW_ITEM *>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SHELLNEW_ITEM)));
170 if (!pNewItem)
171 {
172 free(pData);
173 return NULL;
174 }
175
176 TRACE("new item %ls\n", fi.szTypeName);
177 pNewItem->Type = Types[i].Type;
178 pNewItem->pData = pData;
179 pNewItem->cbData = pData ? cbData : 0;
180 pNewItem->pwszExt = _wcsdup(pwszExt);
181 pNewItem->pwszDesc = _wcsdup(fi.szTypeName);
182 if (fi.hIcon)
183 pNewItem->hIcon = fi.hIcon;
184
185 return pNewItem;
186}
187
188BOOL
189CNewMenu::CacheItems()
190{
191 HKEY hKey;
192 DWORD dwSize = 0;
193 DWORD dwIndex = 0;
194 LPWSTR lpValue;
195 LPWSTR lpValues;
196 WCHAR wszName[MAX_PATH];
197 SHELLNEW_ITEM *pNewItem;
198 SHELLNEW_ITEM *pCurItem = NULL;
199
200 /* Enumerate all extensions */
201 while (RegEnumKeyW(HKEY_CLASSES_ROOT, dwIndex++, wszName, _countof(wszName)) == ERROR_SUCCESS)
202 {
203 if (wszName[0] != L'.')
204 continue;
205
206 pNewItem = LoadItem(wszName);
207 if (pNewItem)
208 {
209 dwSize += wcslen(wszName) + 1;
210 if (!m_pLinkItem && _wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
211 {
212 /* The unique link handler */
213 m_pLinkItem = pNewItem;
214 }
215
216 /* Add at the end of the list */
217 if (pCurItem)
218 {
219 pCurItem->pNext = pNewItem;
220 pCurItem = pNewItem;
221 }
222 else
223 {
224 pCurItem = m_pItems = pNewItem;
225 }
226 }
227 }
228
229 dwSize++;
230
231 lpValues = (LPWSTR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize * sizeof(WCHAR));
232 if (!lpValues)
233 return FALSE;
234
235 for (pCurItem = m_pItems, lpValue = lpValues; pCurItem; pCurItem = pCurItem->pNext)
236 {
237 memcpy(lpValue, pCurItem->pwszExt, (wcslen(pCurItem->pwszExt) + 1) * sizeof(WCHAR));
238 lpValue += wcslen(pCurItem->pwszExt) + 1;
239 }
240
241 if (RegCreateKeyEx(HKEY_CURRENT_USER, ShellNewKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &hKey, NULL) != ERROR_SUCCESS)
242 {
243 HeapFree(GetProcessHeap(), 0, lpValues);
244 return FALSE;
245 }
246
247 if (RegSetValueExW(hKey, L"Classes", NULL, REG_MULTI_SZ, (LPBYTE)lpValues, dwSize * sizeof(WCHAR)) != ERROR_SUCCESS)
248 {
249 HeapFree(GetProcessHeap(), 0, lpValues);
250 RegCloseKey(hKey);
251 return FALSE;
252 }
253
254 HeapFree(GetProcessHeap(), 0, lpValues);
255 RegCloseKey(hKey);
256
257 return TRUE;
258}
259
260BOOL
261CNewMenu::LoadCachedItems()
262{
263 LPWSTR wszName;
264 LPWSTR lpValues;
265 DWORD dwSize;
266 HKEY hKey;
267 SHELLNEW_ITEM *pNewItem;
268 SHELLNEW_ITEM *pCurItem = NULL;
269
270 if (RegOpenKeyExW(HKEY_CURRENT_USER, ShellNewKey, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
271 return FALSE;
272
273 if (RegQueryValueExW(hKey, L"Classes", NULL, NULL, NULL, &dwSize) != ERROR_SUCCESS)
274 return FALSE;
275
276 lpValues = (LPWSTR) HeapAlloc(GetProcessHeap(), 0, dwSize);
277 if (!lpValues)
278 return FALSE;
279
280 if (RegQueryValueExW(hKey, L"Classes", NULL, NULL, (LPBYTE)lpValues, &dwSize) != ERROR_SUCCESS)
281 {
282 HeapFree(GetProcessHeap(), 0, lpValues);
283 return FALSE;
284 }
285
286 wszName = lpValues;
287
288 for (; *wszName != '\0'; wszName += wcslen(wszName) + 1)
289 {
290 pNewItem = LoadItem(wszName);
291 if (pNewItem)
292 {
293 if (!m_pLinkItem && _wcsicmp(pNewItem->pwszExt, L".lnk") == 0)
294 {
295 /* The unique link handler */
296 m_pLinkItem = pNewItem;
297 }
298
299 /* Add at the end of the list */
300 if (pCurItem)
301 {
302 pCurItem->pNext = pNewItem;
303 pCurItem = pNewItem;
304 }
305 else
306 {
307 pCurItem = m_pItems = pNewItem;
308 }
309 }
310 }
311
312 HeapFree(GetProcessHeap(), 0, lpValues);
313 RegCloseKey(hKey);
314
315 return TRUE;
316}
317
318BOOL
319CNewMenu::LoadAllItems()
320{
321 // TODO: We need to find a way to refresh the cache from time to time, when
322 // e.g. new extensions with ShellNew handlers have been added or removed.
323
324 /* If there are any unload them */
325 UnloadAllItems();
326
327 if (!LoadCachedItems())
328 {
329 CacheItems();
330 }
331
332 return (m_pItems != NULL);
333}
334
335UINT
336CNewMenu::InsertShellNewItems(HMENU hMenu, UINT idCmdFirst, UINT Pos)
337{
338 MENUITEMINFOW mii;
339 UINT idCmd = idCmdFirst;
340 WCHAR wszBuf[256];
341
342 if (m_pItems == NULL)
343 {
344 if (!LoadAllItems())
345 return 0;
346 }
347
348 ZeroMemory(&mii, sizeof(mii));
349 mii.cbSize = sizeof(mii);
350
351 m_idCmdFirst = idCmd;
352
353 /* Insert the new folder action */
354 if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWFOLDER, wszBuf, _countof(wszBuf)))
355 wszBuf[0] = 0;
356 mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING;
357 mii.dwTypeData = wszBuf;
358 mii.cch = wcslen(mii.dwTypeData);
359 mii.wID = idCmd;
360 mii.hbmpItem = HBMMENU_CALLBACK;
361 if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
362 m_idCmdFolder = idCmd++;
363
364 /* Insert the new shortcut action */
365 if (m_pLinkItem)
366 {
367 if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEWLINK, wszBuf, _countof(wszBuf)))
368 wszBuf[0] = 0;
369 mii.dwTypeData = wszBuf;
370 mii.cch = wcslen(mii.dwTypeData);
371 mii.wID = idCmd;
372 if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
373 m_idCmdLink = idCmd++;
374 }
375
376 /* Insert a seperator for the custom new action */
377 mii.fMask = MIIM_TYPE | MIIM_ID;
378 mii.fType = MFT_SEPARATOR;
379 mii.wID = -1;
380 InsertMenuItemW(hMenu, Pos++, TRUE, &mii);
381
382 /* Insert the rest of the items */
383 mii.fMask = MIIM_ID | MIIM_BITMAP | MIIM_STRING | MIIM_DATA;
384 mii.fType = 0;
385
386 for (SHELLNEW_ITEM *pCurItem = m_pItems; pCurItem; pCurItem = pCurItem->pNext)
387 {
388 /* Skip shortcut item */
389 if (pCurItem == m_pLinkItem)
390 continue;
391
392 TRACE("szDesc %s\n", debugstr_w(pCurItem->pwszDesc));
393 mii.dwItemData = (ULONG_PTR)pCurItem;
394 mii.dwTypeData = pCurItem->pwszDesc;
395 mii.cch = wcslen(mii.dwTypeData);
396 mii.wID = idCmd;
397 if (InsertMenuItemW(hMenu, Pos++, TRUE, &mii))
398 ++idCmd;
399 }
400
401 return idCmd - idCmdFirst;
402}
403
404CNewMenu::SHELLNEW_ITEM *CNewMenu::FindItemFromIdOffset(UINT IdOffset)
405{
406 /* Folder */
407 if (m_idCmdFirst + IdOffset == m_idCmdFolder)
408 return NULL;
409
410 /* Shortcut */
411 if (m_idCmdFirst + IdOffset == m_idCmdLink)
412 return m_pLinkItem;
413
414 /* Find shell new item - Retrieve menu item info */
415 MENUITEMINFOW mii;
416 ZeroMemory(&mii, sizeof(mii));
417 mii.cbSize = sizeof(mii);
418 mii.fMask = MIIM_DATA;
419
420 if (GetMenuItemInfoW(m_hSubMenu, m_idCmdFirst + IdOffset, FALSE, &mii) && mii.dwItemData)
421 return (SHELLNEW_ITEM *)mii.dwItemData;
422 else
423 return NULL;
424}
425
426HRESULT CNewMenu::SelectNewItem(LONG wEventId, UINT uFlags, LPWSTR pszName, BOOL bRename)
427{
428 CComPtr<IShellBrowser> lpSB;
429 CComPtr<IShellView> lpSV;
430 HRESULT hr = E_FAIL;
431 LPITEMIDLIST pidl;
432 PITEMID_CHILD pidlNewItem;
433 DWORD dwSelectFlags;
434
435 dwSelectFlags = SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED | SVSI_SELECT;
436 if (bRename)
437 dwSelectFlags |= SVSI_EDIT;
438
439 /* Notify the view object about the new item */
440 SHChangeNotify(wEventId, uFlags | SHCNF_FLUSH, (LPCVOID)pszName, NULL);
441
442 if (!m_pSite)
443 return S_OK;
444
445 /* Get a pointer to the shell view */
446 hr = IUnknown_QueryService(m_pSite, SID_SFolderView, IID_PPV_ARG(IShellView, &lpSV));
447 if (FAILED_UNEXPECTEDLY(hr))
448 return S_OK;
449
450 /* Attempt to get the pidl of the new item */
451 hr = SHILCreateFromPathW(pszName, &pidl, NULL);
452 if (FAILED_UNEXPECTEDLY(hr))
453 return hr;
454
455 pidlNewItem = ILFindLastID(pidl);
456
457 hr = lpSV->SelectItem(pidlNewItem, dwSelectFlags);
458
459 SHFree(pidl);
460
461 return hr;
462}
463
464// Code is duplicated in CDefaultContextMenu
465HRESULT CNewMenu::CreateNewFolder(LPCMINVOKECOMMANDINFO lpici)
466{
467 WCHAR wszPath[MAX_PATH];
468 WCHAR wszName[MAX_PATH];
469 WCHAR wszNewFolder[25];
470 HRESULT hr;
471
472 /* Get folder path */
473 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
474 if (FAILED_UNEXPECTEDLY(hr))
475 return hr;
476
477 if (!LoadStringW(shell32_hInstance, IDS_NEWFOLDER, wszNewFolder, _countof(wszNewFolder)))
478 return E_FAIL;
479
480 /* Create the name of the new directory */
481 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, wszNewFolder))
482 return E_FAIL;
483
484 /* Create the new directory and show the appropriate dialog in case of error */
485 if (SHCreateDirectory(lpici->hwnd, wszName) != ERROR_SUCCESS)
486 return E_FAIL;
487
488 /* Show and select the new item in the def view */
489 SelectNewItem(SHCNE_MKDIR, SHCNF_PATHW, wszName, TRUE);
490
491 return S_OK;
492}
493
494HRESULT CNewMenu::NewItemByCommand(SHELLNEW_ITEM *pItem, LPCWSTR wszPath)
495{
496 WCHAR wszBuf[MAX_PATH];
497 LPWSTR Ptr, pwszCmd;
498 WCHAR wszTemp[MAX_PATH];
499 STARTUPINFOW si;
500 PROCESS_INFORMATION pi;
501
502 if (!ExpandEnvironmentStringsW((LPWSTR)pItem->pData, wszBuf, _countof(wszBuf)))
503 {
504 TRACE("ExpandEnvironmentStrings failed\n");
505 return E_FAIL;
506 }
507
508 /* Expand command parameter, FIXME: there can be more modifiers */
509 Ptr = wcsstr(wszBuf, L"%1");
510 if (Ptr)
511 {
512 Ptr[1] = L's';
513 StringCbPrintfW(wszTemp, sizeof(wszTemp), wszBuf, wszPath);
514 pwszCmd = wszTemp;
515 }
516 else
517 {
518 pwszCmd = wszBuf;
519 }
520
521 /* Create process */
522 ZeroMemory(&si, sizeof(si));
523 si.cb = sizeof(si);
524 if (CreateProcessW(NULL, pwszCmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
525 {
526 CloseHandle(pi.hProcess);
527 CloseHandle(pi.hThread);
528 return S_OK;
529 }
530 else
531 {
532 ERR("Failed to create process\n");
533 return E_FAIL;
534 }
535}
536
537HRESULT CNewMenu::NewItemByNonCommand(SHELLNEW_ITEM *pItem, LPWSTR wszName,
538 DWORD cchNameMax, LPCWSTR wszPath)
539{
540 BOOL bSuccess = TRUE;
541
542 CStringW strNewItem;
543 strNewItem.Format(IDS_NEWITEMFORMAT, pItem->pwszDesc);
544 strNewItem += pItem->pwszExt;
545
546 /* Create the name of the new file */
547 if (!PathYetAnotherMakeUniqueName(wszName, wszPath, NULL, strNewItem))
548 return E_FAIL;
549
550 /* Create new file */
551 HANDLE hFile = CreateFileW(wszName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
552 if (hFile != INVALID_HANDLE_VALUE)
553 {
554 if (pItem->Type == SHELLNEW_TYPE_DATA)
555 {
556 /* Write a content */
557 DWORD cbWritten;
558 WriteFile(hFile, pItem->pData, pItem->cbData, &cbWritten, NULL);
559 }
560
561 /* Close file now */
562 CloseHandle(hFile);
563 }
564 else
565 {
566 bSuccess = FALSE;
567 }
568
569 if (pItem->Type == SHELLNEW_TYPE_FILENAME)
570 {
571 /* Copy file */
572 if (!CopyFileW((LPWSTR)pItem->pData, wszName, FALSE))
573 ERR("Copy file failed: %ls\n", (LPWSTR)pItem->pData);
574 }
575
576 /* Show message if we failed */
577 if (bSuccess)
578 {
579 TRACE("Notifying fs %s\n", debugstr_w(wszName));
580 SelectNewItem(SHCNE_CREATE, SHCNF_PATHW, wszName, pItem != m_pLinkItem);
581 }
582 else
583 {
584 CStringW Caption(MAKEINTRESOURCEW(IDS_CREATEFILE_CAPTION));
585 CStringW Message(MAKEINTRESOURCEW(IDS_CREATEFILE_DENIED));
586 Message.FormatMessage(Message.GetString(), wszName);
587 MessageBoxW(0, Message, Caption, MB_ICONEXCLAMATION | MB_OK);
588 }
589
590 return S_OK;
591}
592
593HRESULT CNewMenu::CreateNewItem(SHELLNEW_ITEM *pItem, LPCMINVOKECOMMANDINFO lpcmi)
594{
595 HRESULT hr;
596 WCHAR wszPath[MAX_PATH], wszName[MAX_PATH];
597
598 /* Get folder path */
599 hr = SHGetPathFromIDListW(m_pidlFolder, wszPath);
600 if (FAILED_UNEXPECTEDLY(hr))
601 return hr;
602
603 if (pItem == m_pLinkItem)
604 {
605 NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
606 NewItemByCommand(pItem, wszName);
607 return S_OK;
608 }
609
610 switch (pItem->Type)
611 {
612 case SHELLNEW_TYPE_COMMAND:
613 NewItemByCommand(pItem, wszPath);
614 break;
615
616 case SHELLNEW_TYPE_DATA:
617 case SHELLNEW_TYPE_FILENAME:
618 case SHELLNEW_TYPE_NULLFILE:
619 NewItemByNonCommand(pItem, wszName, _countof(wszName), wszPath);
620 break;
621
622 case SHELLNEW_TYPE_INVALID:
623 ERR("Invalid type\n");
624 break;
625 }
626
627 return S_OK;
628}
629
630HRESULT STDMETHODCALLTYPE CNewMenu::SetSite(IUnknown *pUnkSite)
631{
632 m_pSite = pUnkSite;
633 return S_OK;
634}
635
636HRESULT STDMETHODCALLTYPE CNewMenu::GetSite(REFIID riid, void **ppvSite)
637{
638 return m_pSite->QueryInterface(riid, ppvSite);
639}
640
641HRESULT
642WINAPI
643CNewMenu::QueryContextMenu(HMENU hMenu,
644 UINT indexMenu,
645 UINT idCmdFirst,
646 UINT idCmdLast,
647 UINT uFlags)
648{
649 MENUITEMINFOW mii;
650 UINT cItems = 0;
651 WCHAR wszNew[200];
652
653 TRACE("%p %p %u %u %u %u\n", this,
654 hMenu, indexMenu, idCmdFirst, idCmdLast, uFlags);
655
656 if (!LoadStringW(shell32_hInstance, FCIDM_SHVIEW_NEW, wszNew, _countof(wszNew)))
657 return E_FAIL;
658
659 m_hSubMenu = CreateMenu();
660 if (!m_hSubMenu)
661 return E_FAIL;
662
663 cItems = InsertShellNewItems(m_hSubMenu, idCmdFirst, 0);
664
665 ZeroMemory(&mii, sizeof(mii));
666 mii.cbSize = sizeof(mii);
667 mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE | MIIM_SUBMENU;
668 mii.fType = MFT_STRING;
669 mii.wID = -1;
670 mii.dwTypeData = wszNew;
671 mii.cch = wcslen(mii.dwTypeData);
672 mii.fState = MFS_ENABLED;
673 mii.hSubMenu = m_hSubMenu;
674
675 if (!InsertMenuItemW(hMenu, indexMenu, TRUE, &mii))
676 return E_FAIL;
677
678 return MAKE_HRESULT(SEVERITY_SUCCESS, 0, cItems);
679}
680
681HRESULT
682WINAPI
683CNewMenu::InvokeCommand(LPCMINVOKECOMMANDINFO lpici)
684{
685 HRESULT hr = E_FAIL;
686
687 if (m_idCmdFirst + LOWORD(lpici->lpVerb) == m_idCmdFolder)
688 {
689 hr = CreateNewFolder(lpici);
690 }
691 else
692 {
693 SHELLNEW_ITEM *pItem = FindItemFromIdOffset(LOWORD(lpici->lpVerb));
694 if (pItem)
695 hr = CreateNewItem(pItem, lpici);
696 }
697
698 TRACE("CNewMenu::InvokeCommand %x\n", hr);
699 return hr;
700}
701
702HRESULT
703WINAPI
704CNewMenu::GetCommandString(UINT_PTR idCmd,
705 UINT uType,
706 UINT *pwReserved,
707 LPSTR pszName,
708 UINT cchMax)
709{
710 FIXME("%p %lu %u %p %p %u\n", this,
711 idCmd, uType, pwReserved, pszName, cchMax );
712
713 return E_NOTIMPL;
714}
715
716HRESULT
717WINAPI
718CNewMenu::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
719{
720 return HandleMenuMsg2(uMsg, wParam, lParam, NULL);
721}
722
723HRESULT
724WINAPI
725CNewMenu::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *plResult)
726{
727 switch (uMsg)
728 {
729 case WM_MEASUREITEM:
730 {
731 MEASUREITEMSTRUCT* lpmis = reinterpret_cast<MEASUREITEMSTRUCT*>(lParam);
732 if (!lpmis || lpmis->CtlType != ODT_MENU)
733 break;
734
735 if (lpmis->itemWidth < (UINT)GetSystemMetrics(SM_CXMENUCHECK))
736 lpmis->itemWidth = GetSystemMetrics(SM_CXMENUCHECK);
737 if (lpmis->itemHeight < 16)
738 lpmis->itemHeight = 16;
739
740 if (plResult)
741 *plResult = TRUE;
742 break;
743 }
744 case WM_DRAWITEM:
745 {
746 DRAWITEMSTRUCT* lpdis = reinterpret_cast<DRAWITEMSTRUCT*>(lParam);
747 if (!lpdis || lpdis->CtlType != ODT_MENU)
748 break;
749
750 DWORD id = lpdis->itemID;
751 HICON hIcon = NULL;
752 if (id == m_idCmdFolder)
753 {
754 hIcon = m_hIconFolder;
755 }
756 else if (id == m_idCmdLink)
757 {
758 hIcon = m_hIconLink;
759 }
760 else
761 {
762 SHELLNEW_ITEM *pItem = FindItemFromIdOffset(id - m_idCmdFirst);
763 if (pItem)
764 hIcon = pItem->hIcon;
765 }
766
767 if (!hIcon)
768 break;
769
770 DrawIconEx(lpdis->hDC,
771 2,
772 lpdis->rcItem.top + (lpdis->rcItem.bottom - lpdis->rcItem.top - 16) / 2,
773 hIcon,
774 16,
775 16,
776 0, NULL, DI_NORMAL);
777
778 if(plResult)
779 *plResult = TRUE;
780 }
781 }
782
783 return S_OK;
784}
785
786HRESULT WINAPI
787CNewMenu::Initialize(PCIDLIST_ABSOLUTE pidlFolder,
788 IDataObject *pdtobj, HKEY hkeyProgID)
789{
790 const INT cx = GetSystemMetrics(SM_CXSMICON), cy = GetSystemMetrics(SM_CYSMICON);
791 WCHAR wszIconPath[MAX_PATH];
792 int icon_idx;
793
794 m_pidlFolder = ILClone(pidlFolder);
795
796 /* Load folder and shortcut icons */
797 if (HLM_GetIconW(IDI_SHELL_FOLDER - 1, wszIconPath, _countof(wszIconPath), &icon_idx))
798 {
799 ::ExtractIconExW(wszIconPath, icon_idx, &m_hIconFolder, NULL, 1);
800 m_bCustomIconFolder = TRUE;
801 }
802 else
803 {
804 m_hIconFolder = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_FOLDER), IMAGE_ICON, cx, cy, LR_SHARED);
805 }
806
807 if (HLM_GetIconW(IDI_SHELL_SHORTCUT - 1, wszIconPath, _countof(wszIconPath), &icon_idx))
808 {
809 ::ExtractIconExW(wszIconPath, icon_idx, &m_hIconLink, NULL, 1);
810 m_bCustomIconLink = TRUE;
811 }
812 else
813 {
814 m_hIconLink = (HICON)LoadImage(shell32_hInstance, MAKEINTRESOURCE(IDI_SHELL_SHORTCUT), IMAGE_ICON, cx, cy, LR_SHARED);
815 }
816
817 return S_OK;
818}