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