3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/shell32/shv_item_new.c
5 * PURPOSE: provides new shell item service
6 * PROGRAMMERS: Johannes Anderwald (janderwald@reactos.org)
11 WINE_DEFAULT_DEBUG_CHANNEL(shell
);
15 SHELLNEW_TYPE_COMMAND
= 1,
16 SHELLNEW_TYPE_DATA
= 2,
17 SHELLNEW_TYPE_FILENAME
= 4,
18 SHELLNEW_TYPE_NULLFILE
= 8
22 typedef struct __SHELLNEW_ITEM__
29 struct __SHELLNEW_ITEM__
* Next
;
30 }SHELLNEW_ITEM
, *PSHELLNEW_ITEM
;
34 const IContextMenu2Vtbl
*lpVtblContextMenu
;
35 const IShellExtInitVtbl
*lpvtblShellExtInit
;
37 IShellFolder
*pSFParent
;
38 PSHELLNEW_ITEM s_SnHead
;
39 }INewMenuImpl
, *LPINewMenuImpl
;
41 //static const IContextMenu2Vtbl cmvt;
42 //static const IShellExtInitVtbl sei;
43 static WCHAR szNew
[MAX_PATH
];
48 GetKeyDescription(LPWSTR szKeyName
, LPWSTR szResult
)
51 DWORD dwDesc
, dwError
;
54 static const WCHAR szFriendlyTypeName
[] = { '\\','F','r','i','e','n','d','l','y','T','y','p','e','N','a','m','e',0 };
56 TRACE("GetKeyDescription: keyname %s\n", debugstr_w(szKeyName
));
58 if (RegOpenKeyExW(HKEY_CLASSES_ROOT
,szKeyName
,0, KEY_READ
| KEY_QUERY_VALUE
,&hKey
) != ERROR_SUCCESS
)
61 if (RegLoadMUIStringW(hKey
,szFriendlyTypeName
,szResult
,MAX_PATH
,&dwDesc
,0,NULL
) == ERROR_SUCCESS
)
63 TRACE("result %s\n", debugstr_w(szResult
));
67 /* fetch default value */
68 dwDesc
= sizeof(szDesc
);
69 dwError
= RegGetValueW(hKey
,NULL
,NULL
, RRF_RT_REG_SZ
,NULL
,szDesc
,&dwDesc
);
70 if(dwError
== ERROR_SUCCESS
)
72 if (wcsncmp(szKeyName
, szDesc
, dwDesc
/ sizeof(WCHAR
)))
74 /* recurse for to a linked key */
75 if (!GetKeyDescription(szDesc
, szResult
))
78 wcscpy(szResult
, szDesc
);
83 /* use default value as description */
84 wcscpy(szResult
, szDesc
);
89 /* registry key w/o default key?? */
90 TRACE("RegGetValue failed with %x\n", dwError
);
91 wcscpy(szResult
, szKeyName
);
99 PSHELLNEW_ITEM
LoadItem(LPWSTR szKeyName
)
103 WCHAR szName
[MAX_PATH
];
104 WCHAR szCommand
[MAX_PATH
];
105 WCHAR szDesc
[MAX_PATH
] = {0};
106 WCHAR szIcon
[MAX_PATH
] = {0};
107 DWORD dwName
, dwCommand
;
109 PSHELLNEW_ITEM pNewItem
;
111 static const WCHAR szShellNew
[] = { '\\','S','h','e','l','l','N','e','w',0 };
112 static const WCHAR szCmd
[] = { 'C','o','m','m','a','n','d',0 };
113 static const WCHAR szData
[] = { 'D','a','t','a',0 };
114 static const WCHAR szFileName
[] = { 'F','i','l','e','N','a','m','e', 0 };
115 static const WCHAR szNullFile
[] = { 'N','u','l','l','F','i','l','e', 0 };
118 wcscpy(szName
, szKeyName
);
119 GetKeyDescription(szKeyName
, szDesc
);
120 wcscat(szName
, szShellNew
);
121 result
= RegOpenKeyExW(HKEY_CLASSES_ROOT
,szName
,0,KEY_READ
,&hKey
);
123 //TRACE("LoadItem dwName %d keyname %s szName %s szDesc %s szIcon %s\n", dwName, debugstr_w(szKeyName), debugstr_w(szName), debugstr_w(szDesc), debugstr_w(szIcon));
125 if (result
!= ERROR_SUCCESS
)
136 dwCommand
= MAX_PATH
;
137 result
= RegEnumValueW(hKey
,dwIndex
,szName
,&dwName
,NULL
,NULL
,(LPBYTE
)szCommand
, &dwCommand
);
138 if (result
== ERROR_SUCCESS
)
141 LPWSTR szTarget
= szCommand
;
142 //TRACE("szName %s szCommand %s\n", debugstr_w(szName), debugstr_w(szCommand));
143 if (!wcsicmp(szName
, szCmd
))
145 type
= SHELLNEW_TYPE_COMMAND
;
146 }else if (!wcsicmp(szName
, szData
))
148 type
= SHELLNEW_TYPE_DATA
;
150 else if (!wcsicmp(szName
, szFileName
))
152 type
= SHELLNEW_TYPE_FILENAME
;
154 else if (!wcsicmp(szName
, szNullFile
))
156 type
= SHELLNEW_TYPE_NULLFILE
;
161 pNewItem
= HeapAlloc(GetProcessHeap(), 0, sizeof(SHELLNEW_ITEM
));
162 pNewItem
->Type
= type
;
164 pNewItem
->szTarget
= _wcsdup(szTarget
);
166 pNewItem
->szTarget
= NULL
;
168 pNewItem
->szDesc
= _wcsdup(szDesc
);
169 pNewItem
->szIcon
= _wcsdup(szIcon
);
170 pNewItem
->szExt
= _wcsdup(szKeyName
);
171 pNewItem
->Next
= NULL
;
176 }while(result
!= ERROR_NO_MORE_ITEMS
);
183 LoadShellNewItems(INewMenuImpl
* This
)
186 WCHAR szName
[MAX_PATH
];
188 PSHELLNEW_ITEM pNewItem
;
189 PSHELLNEW_ITEM pCurItem
= NULL
;
190 static WCHAR szLnk
[] = { '.','l','n','k',0 };
192 /* insert do new folder action */
193 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEW
, szNew
, sizeof(szNew
) / sizeof(WCHAR
)))
195 szNew
[MAX_PATH
-1] = 0;
200 result
= RegEnumKeyW(HKEY_CLASSES_ROOT
,dwIndex
,szName
,MAX_PATH
);
201 if (result
== ERROR_SUCCESS
)
203 pNewItem
= LoadItem(szName
);
206 if (!wcsicmp(pNewItem
->szExt
, szLnk
))
210 pNewItem
->Next
= This
->s_SnHead
;
211 This
->s_SnHead
= pNewItem
;
215 This
->s_SnHead
= pCurItem
= pNewItem
;
222 pCurItem
->Next
= pNewItem
;
227 pCurItem
= This
->s_SnHead
= pNewItem
;
233 }while(result
!= ERROR_NO_MORE_ITEMS
);
235 if (This
->s_SnHead
== NULL
)
242 InsertShellNewItems(HMENU hMenu
, UINT idFirst
, UINT idMenu
, INewMenuImpl
* This
)
245 PSHELLNEW_ITEM pCurItem
;
247 WCHAR szBuffer
[MAX_PATH
];
249 if (This
->s_SnHead
== NULL
)
251 if (!LoadShellNewItems(This
))
255 ZeroMemory(&mii
, sizeof(mii
));
256 mii
.cbSize
= sizeof(mii
);
258 /* insert do new shortcut action */
259 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEWFOLDER
, szBuffer
, sizeof(szBuffer
) / sizeof(szBuffer
[0])))
261 szBuffer
[MAX_PATH
-1] = 0;
262 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
263 mii
.fType
= MFT_STRING
;
264 mii
.dwTypeData
= szBuffer
;
265 mii
.cch
= wcslen(mii
.dwTypeData
);
267 InsertMenuItemW(hMenu
, idMenu
++, TRUE
, &mii
);
269 /* insert do new shortcut action */
270 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEWLINK
, szBuffer
, sizeof(szBuffer
) / sizeof(szBuffer
[0])))
272 szBuffer
[MAX_PATH
-1] = 0;
273 mii
.dwTypeData
= szBuffer
;
274 mii
.cch
= wcslen(mii
.dwTypeData
);
276 InsertMenuItemW(hMenu
, idMenu
++, TRUE
, &mii
);
278 /* insert seperator for custom new action */
279 mii
.fMask
= MIIM_TYPE
| MIIM_ID
;
280 mii
.fType
= MFT_SEPARATOR
;
282 InsertMenuItemW(hMenu
, idMenu
++, TRUE
, &mii
);
284 mii
.fMask
= MIIM_ID
| MIIM_TYPE
| MIIM_STATE
| MIIM_DATA
;
287 * implement loading of icons
288 * and using MFT_OWNERDRAWN
290 mii
.fType
= MFT_STRING
;
291 mii
.fState
= MFS_ENABLED
;
293 pCurItem
= This
->s_SnHead
;
300 TRACE("szDesc %s\n", debugstr_w(pCurItem
->szDesc
));
301 mii
.dwTypeData
= pCurItem
->szDesc
;
302 mii
.cch
= wcslen(mii
.dwTypeData
);
304 InsertMenuItemW(hMenu
, idMenu
++, TRUE
, &mii
);
306 pCurItem
= pCurItem
->Next
;
313 DoShellNewCmd(INewMenuImpl
* This
, LPCMINVOKECOMMANDINFO lpcmi
)
315 PSHELLNEW_ITEM pCurItem
= This
->s_SnHead
;
316 IPersistFolder3
* psf
;
319 WCHAR szTemp
[MAX_PATH
];
320 WCHAR szBuffer
[MAX_PATH
];
321 WCHAR szPath
[MAX_PATH
];
323 PROCESS_INFORMATION pi
;
326 DWORD dwWritten
, dwError
;
328 static const WCHAR szP1
[] = { '%', '1', 0 };
329 static const WCHAR szFormat
[] = {'%','s',' ','(','%','d',')','%','s',0 };
332 target
= LOWORD(lpcmi
->lpVerb
);
339 pCurItem
= pCurItem
->Next
;
346 if (IShellFolder2_QueryInterface(This
->pSFParent
, &IID_IPersistFolder2
, (LPVOID
*)&psf
) != S_OK
)
348 ERR("Failed to get interface IID_IPersistFolder2\n");
351 if (IPersistFolder2_GetCurFolder(psf
, &pidl
) != S_OK
)
353 ERR("IPersistFolder2_GetCurFolder failed\n");
357 if (IShellFolder2_GetDisplayNameOf(This
->pSFParent
, pidl
, SHGDN_FORPARSING
, &strTemp
) != S_OK
)
359 ERR("IShellFolder_GetDisplayNameOf failed\n");
362 StrRetToBufW(&strTemp
, pidl
, szPath
, MAX_PATH
);
364 switch(pCurItem
->Type
)
366 case SHELLNEW_TYPE_COMMAND
:
371 if (!ExpandEnvironmentStringsW(pCurItem
->szTarget
, szBuffer
, MAX_PATH
))
373 TRACE("ExpandEnvironmentStrings failed\n");
377 ptr
= wcsstr(szBuffer
, szP1
);
381 swprintf(szTemp
, szBuffer
, szPath
);
389 ZeroMemory(&sInfo
, sizeof(sInfo
));
390 sInfo
.cb
= sizeof(sizeof(sInfo
));
391 szCmd
= _wcsdup(ptr
);
394 if (CreateProcessW(NULL
, szCmd
, NULL
, NULL
,FALSE
,0,NULL
,NULL
,&sInfo
, &pi
))
396 CloseHandle( pi
.hProcess
);
397 CloseHandle( pi
.hThread
);
402 case SHELLNEW_TYPE_DATA
:
403 case SHELLNEW_TYPE_FILENAME
:
404 case SHELLNEW_TYPE_NULLFILE
:
408 PathAddBackslashW(szPath
);
409 wcscat(szPath
, szNew
);
410 wcscat(szPath
, L
" ");
411 wcscat(szPath
, pCurItem
->szDesc
);
412 wcscpy(szBuffer
, szPath
);
413 wcscat(szBuffer
, pCurItem
->szExt
);
416 hFile
= CreateFileW(szBuffer
, GENERIC_WRITE
, 0, NULL
, CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, NULL
);
417 if (hFile
!= INVALID_HANDLE_VALUE
)
419 dwError
= GetLastError();
421 TRACE("FileName %s szBuffer %s i %u error %x\n", debugstr_w(szBuffer
), debugstr_w(szPath
), i
, dwError
);
422 swprintf(szBuffer
, szFormat
, szPath
, i
, pCurItem
->szExt
);
424 }while(hFile
== INVALID_HANDLE_VALUE
&& dwError
== ERROR_FILE_EXISTS
);
426 if (hFile
== INVALID_HANDLE_VALUE
)
429 if (pCurItem
->Type
== SHELLNEW_TYPE_DATA
)
431 i
= WideCharToMultiByte(CP_ACP
, 0, pCurItem
->szTarget
, -1, (LPSTR
)szTemp
, MAX_PATH
*2, NULL
, NULL
);
434 WriteFile(hFile
, (LPCVOID
)szTemp
, i
, &dwWritten
, NULL
);
438 if (pCurItem
->Type
== SHELLNEW_TYPE_FILENAME
)
440 if (!CopyFileW(pCurItem
->szTarget
, szBuffer
, FALSE
))
443 TRACE("Notifying fs %s\n", debugstr_w(szBuffer
));
444 SHChangeNotify(SHCNE_CREATE
, SHCNF_PATHW
, (LPCVOID
)szBuffer
, NULL
);
450 /**************************************************************************
454 DoMeasureItem(INewMenuImpl
*This
, HWND hWnd
, MEASUREITEMSTRUCT
* lpmis
)
456 PSHELLNEW_ITEM pCurItem
;
457 PSHELLNEW_ITEM pItem
;
462 TRACE("DoMeasureItem entered with id %x\n", lpmis
->itemID
);
464 pCurItem
= This
->s_SnHead
;
470 if (i
== lpmis
->itemID
)
475 pCurItem
= pCurItem
->Next
;
481 TRACE("DoMeasureItem no item found\n");
485 GetTextExtentPoint32W(hDC
, pCurItem
->szDesc
, wcslen(pCurItem
->szDesc
), &size
);
486 lpmis
->itemWidth
= size
.cx
+ 32;
487 lpmis
->itemHeight
= max(size
.cy
, 20);
488 ReleaseDC (hWnd
, hDC
);
491 /**************************************************************************
495 DoDrawItem(INewMenuImpl
*This
, HWND hWnd
, DRAWITEMSTRUCT
* drawItem
)
497 PSHELLNEW_ITEM pCurItem
;
498 PSHELLNEW_ITEM pItem
;
500 pCurItem
= This
->s_SnHead
;
502 TRACE("DoDrawItem entered with id %x\n", drawItem
->itemID
);
508 if (i
== drawItem
->itemID
)
513 pCurItem
= pCurItem
->Next
;
520 drawItem
->rcItem
.left
+= 20;
522 DrawTextW(drawItem
->hDC
, pCurItem
->szDesc
, wcslen(pCurItem
->szDesc
), &drawItem
->rcItem
, 0);
526 /**************************************************************************
529 static void DoNewFolder(
534 WCHAR wszName
[MAX_PATH
];
536 IShellFolder_QueryInterface(This
->pSFParent
, &IID_ISFHelper
, (LPVOID
*)&psfhlp
);
541 if (ISFHelper_GetUniqueName(psfhlp
, wszName
, MAX_PATH
) != S_OK
)
543 if (ISFHelper_AddFolder(psfhlp
, 0, wszName
, &pidl
) != S_OK
)
548 IShellView_Refresh(psv
);
549 /* if we are in a shellview do labeledit */
550 IShellView_SelectItem(psv
,
551 pidl
,(SVSI_DESELECTOTHERS
| SVSI_EDIT
| SVSI_ENSUREVISIBLE
552 |SVSI_FOCUSED
|SVSI_SELECT
));
553 IShellView_Refresh(psv
);
557 ISFHelper_Release(psfhlp
);
562 static LPINewMenuImpl __inline
impl_from_IShellExtInit( IShellExtInit
*iface
)
564 return (INewMenuImpl
*)((char*)iface
- FIELD_OFFSET(INewMenuImpl
, lpvtblShellExtInit
));
567 static LPINewMenuImpl __inline
impl_from_IContextMenu( IContextMenu2
*iface
)
569 return (INewMenuImpl
*)((char*)iface
- FIELD_OFFSET(INewMenuImpl
, lpVtblContextMenu
));
572 static HRESULT WINAPI
INewItem_fnQueryInterface(INewMenuImpl
* This
, REFIID riid
, LPVOID
*ppvObj
)
574 TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This
,debugstr_guid(riid
),ppvObj
);
578 if(IsEqualIID(riid
, &IID_IUnknown
) ||
579 IsEqualIID(riid
, &IID_IContextMenu
) ||
580 IsEqualIID(riid
, &IID_IContextMenu2
))
582 *ppvObj
= (void *)&This
->lpVtblContextMenu
;
584 else if(IsEqualIID(riid
, &IID_IShellExtInit
))
586 *ppvObj
= (void *)&This
->lpvtblShellExtInit
;
592 IUnknown_AddRef((IUnknown
*)*ppvObj
);
593 TRACE("-- Interface: (%p)->(%p)\n",ppvObj
,*ppvObj
);
596 TRACE("-- Interface: E_NOINTERFACE\n");
597 return E_NOINTERFACE
;
600 static ULONG WINAPI
INewItem_fnAddRef(INewMenuImpl
*iface
)
602 /* INewItem service is singleton */
606 static ULONG WINAPI
INewItem_fnRelease(INewMenuImpl
*This
)
608 /* INewItem service is singleton */
615 INewItem_IContextMenu_fnQueryInterface( IContextMenu2
* iface
, REFIID riid
, void** ppvObject
)
617 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
618 return INewItem_fnQueryInterface(This
, riid
, ppvObject
);
624 INewItem_IContextMenu_fnAddRef(IContextMenu2
*iface
)
626 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
627 return INewItem_fnAddRef(This
);
633 INewItem_IContextMenu_fnRelease(IContextMenu2
*iface
)
635 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
636 return INewItem_fnRelease(This
);
642 INewItem_IContextMenu_fnQueryContextMenu(IContextMenu2
*iface
,
653 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
655 TRACE("%p %p %u %u %u %u\n", This
,
656 hmenu
, indexMenu
, idCmdFirst
, idCmdLast
, uFlags
);
658 if (!LoadStringW(shell32_hInstance
, FCIDM_SHVIEW_NEW
, szBuffer
, 200))
664 hSubMenu
= CreateMenu();
665 memset( &mii
, 0, sizeof(mii
) );
666 mii
.cbSize
= sizeof (mii
);
667 mii
.fMask
= MIIM_TYPE
| MIIM_ID
| MIIM_STATE
;
668 mii
.fType
= MFT_STRING
;
669 mii
.wID
= idCmdFirst
+ id
++;
670 mii
.dwTypeData
= szBuffer
;
671 mii
.cch
= wcslen( mii
.dwTypeData
);
672 mii
.fState
= MFS_ENABLED
;
676 id
+= InsertShellNewItems( hSubMenu
, idCmdFirst
, 0, This
);
677 mii
.fMask
|= MIIM_SUBMENU
;
678 mii
.hSubMenu
= hSubMenu
;
682 if (!InsertMenuItemW( hmenu
, indexMenu
, TRUE
, &mii
))
685 return MAKE_HRESULT( SEVERITY_SUCCESS
, 0, id
);
691 INewItem_IContextMenu_fnInvokeCommand( IContextMenu2
* iface
,
692 LPCMINVOKECOMMANDINFO lpici
)
695 LPSHELLVIEW lpSV
= NULL
;
697 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
699 if((lpSB
= (LPSHELLBROWSER
)SendMessageA(lpici
->hwnd
, CWM_GETISHELLBROWSER
,0,0)))
701 IShellBrowser_QueryActiveShellView(lpSB
, &lpSV
);
704 if (LOWORD(lpici
->lpVerb
) == 0)
706 DoNewFolder(This
, lpSV
);
710 hr
= DoShellNewCmd(This
, lpici
);
711 if (SUCCEEDED(hr
) && lpSV
)
713 IShellView_Refresh(lpSV
);
716 TRACE("INewItem_IContextMenu_fnInvokeCommand %x\n", hr
);
723 INewItem_IContextMenu_fnGetCommandString( IContextMenu2
* iface
,
730 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
732 FIXME("%p %lu %u %p %p %u\n", This
,
733 idCmd
, uType
, pwReserved
, pszName
, cchMax
);
741 INewItem_IContextMenu_fnHandleMenuMsg(IContextMenu2
*iface
,
746 INewMenuImpl
*This
= impl_from_IContextMenu(iface
);
747 DRAWITEMSTRUCT
* lpids
= (DRAWITEMSTRUCT
*) lParam
;
748 MEASUREITEMSTRUCT
*lpmis
= (MEASUREITEMSTRUCT
*) lParam
;
750 TRACE("INewItem_IContextMenu_fnHandleMenuMsg (%p)->(msg=%x wp=%lx lp=%lx)\n",This
, uMsg
, wParam
, lParam
);
756 return DoMeasureItem(This
, (HWND
)wParam
, lpmis
);
759 return DoDrawItem(This
, (HWND
)wParam
, lpids
);
767 static const IContextMenu2Vtbl cmvt
=
769 INewItem_IContextMenu_fnQueryInterface
,
770 INewItem_IContextMenu_fnAddRef
,
771 INewItem_IContextMenu_fnRelease
,
772 INewItem_IContextMenu_fnQueryContextMenu
,
773 INewItem_IContextMenu_fnInvokeCommand
,
774 INewItem_IContextMenu_fnGetCommandString
,
775 INewItem_IContextMenu_fnHandleMenuMsg
778 static HRESULT WINAPI
779 INewItem_ExtInit_fnQueryInterface( IShellExtInit
* iface
, REFIID riid
, void** ppvObject
)
781 return INewItem_fnQueryInterface(impl_from_IShellExtInit(iface
), riid
, ppvObject
);
785 INewItem_ExtInit_AddRef( IShellExtInit
* iface
)
787 return INewItem_fnAddRef(impl_from_IShellExtInit(iface
));
791 INewItem_ExtInit_Release( IShellExtInit
* iface
)
793 return INewItem_fnRelease(impl_from_IShellExtInit(iface
));
796 static HRESULT WINAPI
797 INewItem_ExtInit_Initialize( IShellExtInit
* iface
, LPCITEMIDLIST pidlFolder
,
798 IDataObject
*pdtobj
, HKEY hkeyProgID
)
804 static const IShellExtInitVtbl sei
=
806 INewItem_ExtInit_fnQueryInterface
,
807 INewItem_ExtInit_AddRef
,
808 INewItem_ExtInit_Release
,
809 INewItem_ExtInit_Initialize
811 static INewMenuImpl
*cached_ow
= NULL
;
814 INewItem_SetCurrentShellFolder(IShellFolder
* psfParent
)
817 cached_ow
->pSFParent
= psfParent
;
820 HRESULT WINAPI
INewItem_Constructor(IUnknown
* pUnkOuter
, REFIID riid
, LPVOID
*ppv
)
827 ow
= LocalAlloc(LMEM_ZEROINIT
, sizeof(INewMenuImpl
));
830 return E_OUTOFMEMORY
;
833 ow
->lpVtblContextMenu
= &cmvt
;
834 ow
->lpvtblShellExtInit
= &sei
;
838 if (InterlockedCompareExchangePointer((void *)&cached_ow
, ow
, NULL
) != NULL
)
840 /* some other thread already been here */
845 res
= INewItem_fnQueryInterface( cached_ow
, riid
, ppv
);